qt: Change comments to English

This commit is contained in:
Calcitem 2021-01-03 13:16:34 +08:00
parent f5c4a5f7d9
commit ca495f1f49
14 changed files with 402 additions and 619 deletions

View File

@ -26,13 +26,13 @@ BoardItem::BoardItem(QGraphicsItem *parent) :
{
Q_UNUSED(parent)
// 棋盘中心放在场景中心
// The center of the board is in the center of the scene
setPos(0, 0);
// 初始化24个落子点
// Initialize 24 drop points
for (int r = 0; r < FILE_NB; r++) {
// 内圈的12点钟方向为第一个位置按顺时针方向排序
// 然后是中圈和外圈
// The first position is the 12 o'clock direction of the inner ring, which is sorted clockwise
// Then there is the middle ring and the outer ring
int a = (r + 1) * LINE_INTERVAL;
position[r * RANK_NB + 0].rx() = 0;
@ -89,14 +89,14 @@ void BoardItem::paint(QPainter *painter,
Q_UNUSED(option)
Q_UNUSED(widget)
// 填充阴影
// Fill shadow
#ifndef MOBILE_APP_UI
QColor shadowColor(128, 42, 42);
shadowColor.setAlphaF(0.3);
painter->fillRect(boundingRect(), QBrush(shadowColor));
#endif /* ! MOBILE_APP_UI */
// 填充图片
// Fill in picture
#ifdef MOBILE_APP_UI
painter->setPen(Qt::NoPen);
painter->setBrush(QColor(239, 239, 239));
@ -106,7 +106,7 @@ void BoardItem::paint(QPainter *painter,
QPixmap(":/image/resources/image/board.png"));
#endif /* MOBILE_APP_UI */
// 实线画笔
// Solid line brush
#ifdef MOBILE_APP_UI
QPen pen(QBrush(QColor(241, 156, 159)), LINE_WEIGHT, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin);
#else
@ -114,28 +114,28 @@ void BoardItem::paint(QPainter *painter,
#endif
painter->setPen(pen);
// 空画刷
// No brush
painter->setBrush(Qt::NoBrush);
for (uint8_t i = 0; i < FILE_NB; i++) {
// 画3个方框
// Draw three boxes
painter->drawPolygon(position + i * RANK_NB, RANK_NB);
}
// 画4条纵横线
// Draw 4 vertical and horizontal lines
for (int i = 0; i < RANK_NB; i += 2) {
painter->drawLine(position[i], position[(FILE_NB - 1) * RANK_NB + i]);
}
if (hasObliqueLine) {
// 画4条斜线
// Draw 4 diagonal lines
for (int i = 1; i < RANK_NB; i += 2) {
painter->drawLine(position[i], position[(FILE_NB - 1) * RANK_NB + i]);
}
}
#ifdef PLAYER_DRAW_SEAT_NUMBER
// 画 Seat 编号
// Draw the seat number
QPen fontPen(QBrush(Qt::white), LINE_WEIGHT, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin);
painter->setPen(fontPen);
QFont font;
@ -154,12 +154,12 @@ void BoardItem::paint(QPainter *painter,
QPointF BoardItem::nearestPosition(QPointF const pos)
{
// 初始最近点设为(0,0)点
// The initial closest point is set to (0,0) point
QPointF nearestPos = QPointF(0, 0);
// 寻找最近的落子点
// Look for the nearest spot
for (auto i : position) {
// 如果鼠标点距离落子点在棋子半径内
// If the distance between the mouse point and the falling point is within the radius of the chess piece
if (QLineF(pos, i).length() < PIECE_SIZE / 2) {
nearestPos = i;
break;
@ -171,14 +171,14 @@ QPointF BoardItem::nearestPosition(QPointF const pos)
QPointF BoardItem::polar2pos(File file, Rank rank)
{
return position[((int)file - 1) * RANK_NB + (int)rank - 1]; // TODO: 为什么是 r - 1 和算法部分不一样?
return position[((int)file - 1) * RANK_NB + (int)rank - 1];
}
bool BoardItem::pos2polar(QPointF pos, File &file, Rank &rank)
{
// 寻找最近的落子点
// Look for the nearest spot
for (int i = 0; i < EFFECTIVE_SQUARE_NB; i++) {
// 如果pos点在落子点附近
// If the pos point is near the falling point
if (QLineF(pos, position[i]).length() < PIECE_SIZE / 6) {
file = File(i / RANK_NB + 1);
rank = Rank(i % RANK_NB + 1);

View File

@ -37,8 +37,8 @@ public:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget = nullptr) override;
// 用UserType+1表示棋子用qgraphicsitem_cast()判断是否为BoardItem类的对象
// 还有一个方式是把类名放在Data的0key位置setData(0, "BoardItem")然后用data(0)来判断
// Use UserType + 1 to represent chess pieces, and use qgraphicsitem_cast() determines whether it is an object of the boarditem class
// Another way is to put the class name in the 0key position of data, SetData(0, "BoardItem"), and then use data(0) to judge
enum
{
Type = UserType + 1
@ -49,35 +49,26 @@ public:
return Type;
}
// 设置有无斜线
//Set with or without slash
void setDiagonal(bool arg = true);
// 返回最近的落子点
//Return to the nearest drop point
QPointF nearestPosition(QPointF pos);
// 将模型的圈、位转化为落子点坐标
// The circle and position of the model are transformed into the point coordinates
QPointF polar2pos(File file, Rank rank);
// 将落子点坐标转化为模型用的圈、位
// The coordinates of the falling point are transformed into circles and positions for the model
bool pos2polar(QPointF pos, File &file, Rank &rank);
// 3圈禁止修改
static const uint8_t FILE_NB = 3;
// 8位禁止修改
static const uint8_t RANK_NB = 8;
private:
// 棋盘尺寸
int size;
// 影子尺寸
int size; // board size
int sizeShadow {5};
// 24个落子点
QPointF position[EFFECTIVE_SQUARE_NB];
// 是否有斜线
QPointF position[EFFECTIVE_SQUARE_NB]; // 24 points
bool hasObliqueLine {false};
};

View File

@ -62,8 +62,9 @@ Game::Game(
timeLimit(0),
stepsLimit(50)
{
// 已在view的样式表中添加背景scene中不用添加背景
// 区别在于view中的背景不随视图变换而变换scene中的背景随视图变换而变换
// The background has been added to the style sheet of view, but not to scene
// The difference is that the background in view does not change with the view transformation,
// and the background in scene changes with the view transformation
//scene.setBackgroundBrush(QPixmap(":/image/resources/image/background.png"));
#ifdef MOBILE_APP_UI
scene.setBackgroundBrush(QColor(239, 239, 239));
@ -79,7 +80,7 @@ Game::Game(
qRegisterMetaType<std::string>("string");
#ifdef QT_GUI_LIB
// 关联AI和控制器的着法命令行
// The command line of AI and controller
connect(aiThread[BLACK], SIGNAL(command(const string &, bool)),
this, SLOT(command(const string &, bool)));
connect(aiThread[WHITE], SIGNAL(command(const string &, bool)),
@ -94,7 +95,7 @@ Game::Game(
uint16_t clientPort = server->getPort() == 30001 ? 30002 : 30001;
client = new Client(nullptr, clientPort);
// 关联AI和网络类的着法命令行
// The command line of AI andnetwork
connect(getClient(), SIGNAL(command(const string &, bool)),
this, SLOT(command(const string &, bool)));
#endif // NET_FIGHT_SUPPORT
@ -106,19 +107,13 @@ Game::Game(
#endif
moveHistory.reserve(256);
// 安装事件过滤器监视scene的各个事件
// 由于我重载了QGraphicsScene相关事件在重载函数中已设定不必安装监视器。
//scene.installEventFilter(this);
}
Game::~Game()
{
// 停止计时器
if (timeID != 0)
killTimer(timeID);
// 停掉线程
pauseAndWaitThreads();
deleteAiThreads();
@ -133,12 +128,13 @@ Game::~Game()
const map<int, QStringList> Game::getActions()
{
// 主窗口更新菜单栏
// 之所以不用信号和槽的模式,是因为发信号的时候槽还来不及关联
// Main window update menu bar
// The reason why we don't use the mode of signal and slot is that
// it's too late for the slot to be associated when the signal is sent
map<int, QStringList> actions;
for (int i = 0; i < N_RULES; i++) {
// map的key存放int索引值value存放规则名称和规则提示
//The key of map stores int index value, and value stores rule name and rule prompt
QStringList strlist;
strlist.append(tr(RULES[i].name));
strlist.append(tr(RULES[i].description));
@ -159,7 +155,7 @@ void Game::gameStart()
position.start();
startTime = time(nullptr);
// 每隔100毫秒调用一次定时器处理函数
// The timer handler is called every 100 milliseconds
if (timeID == 0) {
timeID = startTimer(100);
}
@ -184,14 +180,12 @@ void Game::gameReset()
loggerDebug("\n");
// 停止计时器
if (timeID != 0)
killTimer(timeID);
// 定时器ID为0
timeID = 0;
// 重置游戏
// Reset game
// WAR
if (moveHistory.size() > 1) {
string bak = moveHistory[0];
@ -203,28 +197,28 @@ void Game::gameReset()
elapsedSeconds[BLACK] = elapsedSeconds[WHITE] = 0;
sideToMove = position.side_to_move();
// 停掉线程
// Stop threads
if (!gameOptions.getAutoRestart()) {
pauseThreads();
resetAiPlayers();
}
// 清除棋子
// Clear pieces
qDeleteAll(pieceList);
pieceList.clear();
currentPiece = nullptr;
// 重新绘制棋盘
// Redraw pieces
scene.setDiagonal(rule.hasObliqueLines);
// 绘制所有棋子,放在起始位置
// 0: 先手第1子 1后手第1子
// 2先手第2子 3后手第2子
// Draw all the pieces and put them in the starting position
// 0: the first piece in the first hand; 1: the first piece in the second hand
// 2: the first second piece; 3: the second piece
// ......
PieceItem::Models md;
for (int i = 0; i < rule.piecesCount; i++) {
// 先手的棋子
// The first piece
md = isInverted ? PieceItem::Models::whitePiece : PieceItem::Models::blackPiece;
PieceItem *newP = new PieceItem;
newP->setModel(md);
@ -235,7 +229,7 @@ void Game::gameReset()
pieceList.push_back(newP);
scene.addItem(newP);
// 后手的棋子
// Backhand piece
md = isInverted ? PieceItem::Models::blackPiece : PieceItem::Models::whitePiece;
newP = new PieceItem;
newP->setModel(md);
@ -247,41 +241,41 @@ void Game::gameReset()
scene.addItem(newP);
}
// 读取规则限时要求
// Read rule time limit requirement
timeLimit = 0; // TODO: rule.maxTimeLedToLose;
// 如果规则不要求计时则time1和time2表示已用时间
// If the rule does not require timing, time1 and time2 represent the time used
if (timeLimit <= 0) {
// 将玩家的已用时间清零
// Clear the player's used time
remainingTime[BLACK] = remainingTime[WHITE] = 0;
} else {
// 将玩家的剩余时间置为限定时间
// Set the player's remaining time to a limited time
remainingTime[BLACK] = remainingTime[WHITE] = timeLimit * 60;
}
// 更新棋谱
// Update move history
manualListModel.removeRows(0, manualListModel.rowCount());
manualListModel.insertRow(0);
manualListModel.setData(manualListModel.index(0), position.get_record());
currentRow = 0;
// 发出信号通知主窗口更新LCD显示
// Signal the main window to update the LCD display
const QTime qtime = QTime(0, 0, 0, 0).addSecs(static_cast<int>(remainingTime[BLACK]));
emit time1Changed(qtime.toString("hh:mm:ss"));
emit time2Changed(qtime.toString("hh:mm:ss"));
// 发信号更新状态栏
// Signal update status bar
updateScence();
message = QString::fromStdString(getTips());
emit statusBarChanged(message);
// 更新比分 LCD 显示
// Update LCD display
emit nGamesPlayedChanged(QString::number(position.gamesPlayedCount, 10));
emit score1Changed(QString::number(position.score[BLACK], 10));
emit score2Changed(QString::number(position.score[WHITE], 10));
emit scoreDrawChanged(QString::number(position.score_draw, 10));
// 更新胜率 LCD 显示
// Update winning rate LCD display
position.gamesPlayedCount = position.score[BLACK] + position.score[WHITE] + position.score_draw;
int winningRate_1 = 0, winningRate_2 = 0, winningRate_draw = 0;
if (position.gamesPlayedCount != 0) {
@ -294,7 +288,7 @@ void Game::gameReset()
emit winningRate2Changed(QString::number(winningRate_2, 10));
emit winningRateDrawChanged(QString::number(winningRate_draw, 10));
// 播放音效
// Sound effects play
//playSound(":/sound/resources/sound/newgame.wav");
}
@ -307,18 +301,18 @@ void Game::setInvert(bool arg)
{
isInverted = arg;
// 遍历所有棋子
// For all pieces
for (PieceItem *pieceItem : pieceList) {
if (pieceItem) {
// 黑子变白
// Black -> White
if (pieceItem->getModel() == PieceItem::Models::blackPiece)
pieceItem->setModel(PieceItem::Models::whitePiece);
// 白子变黑
// White -> Black
else if (pieceItem->getModel() == PieceItem::Models::whitePiece)
pieceItem->setModel(PieceItem::Models::blackPiece);
// 刷新棋子显示
// Refresh checkerboard display
pieceItem->update();
}
}
@ -326,7 +320,7 @@ void Game::setInvert(bool arg)
void Game::setRule(int ruleNo, int stepLimited /*= -1*/, int timeLimited /*= -1*/)
{
// 更新规则,原限时和限步不变
// Update the rule, the original time limit and step limit remain unchanged
if (ruleNo < 0 || ruleNo >= N_RULES)
return;
this->ruleIndex = ruleNo;
@ -336,7 +330,7 @@ void Game::setRule(int ruleNo, int stepLimited /*= -1*/, int timeLimited /*= -1*
timeLimit = timeLimited;
}
// 设置模型规则,重置游戏
// Set model rules, reset game
if (set_rule(ruleNo) == false) {
return;
}
@ -352,7 +346,7 @@ void Game::setRule(int ruleNo, int stepLimited /*= -1*/, int timeLimited /*= -1*
moveHistory.clear();
moveHistory.emplace_back(cmd);
// 重置游戏
// Reset game
gameReset();
}
@ -399,7 +393,7 @@ void Game::setAnimation(bool arg) noexcept
{
hasAnimation = arg;
// 默认动画时间500ms
// The default animation time is 500ms
if (hasAnimation)
durationTime = 500;
else
@ -561,7 +555,6 @@ void Game::setOpeningBook(bool enabled)
gameOptions.setOpeningBook(enabled);
}
// 上下翻转
void Game::flip()
{
stopAndWaitAiThreads();
@ -569,13 +562,13 @@ void Game::flip()
position.mirror(moveHistory);
position.rotate(moveHistory, 180);
// 更新棋谱
// Update move history
int row = 0;
for (const auto &str : *(move_hostory())) {
manualListModel.setData(manualListModel.index(row++), str.c_str());
}
// 刷新显示
// Refresh display
if (currentRow == row - 1)
updateScence();
else
@ -585,14 +578,13 @@ void Game::flip()
startAiThreads();
}
// 左右镜像
void Game::mirror()
{
stopAndWaitAiThreads();
position.mirror(moveHistory);
// 更新棋谱
// Update move history
int row = 0;
for (const auto &str : *(move_hostory())) {
@ -601,7 +593,7 @@ void Game::mirror()
loggerDebug("list: %d\n", row);
// 刷新显示
// Update display
if (currentRow == row - 1)
updateScence();
else
@ -611,21 +603,20 @@ void Game::mirror()
startAiThreads();
}
// 视图须时针旋转90°
void Game::turnRight()
{
stopAndWaitAiThreads();
position.rotate(moveHistory, -90);
// 更新棋谱
// Update move history
int row = 0;
for (const auto &str : *(move_hostory())) {
manualListModel.setData(manualListModel.index(row++), str.c_str());
}
// 刷新显示
// Update display
if (currentRow == row - 1)
updateScence();
else
@ -635,20 +626,19 @@ void Game::turnRight()
startAiThreads();
}
// 视图逆时针旋转90°
void Game::turnLeft()
{
stopAndWaitAiThreads();
position.rotate(moveHistory, 90);
// 更新棋谱
// Update move history
int row = 0;
for (const auto &str : *(move_hostory())) {
manualListModel.setData(manualListModel.index(row++), str.c_str());
}
// 刷新显示
// Update display
updateScence();
threadsSetAi(&position);
@ -680,14 +670,14 @@ void Game::timerEvent(QTimerEvent *event)
Q_UNUSED(event)
static QTime qt1, qt2;
// 玩家的已用时间
// Player's time spent
updateTime();
remainingTime[BLACK] = get_elapsed_time(BLACK);
remainingTime[WHITE] = get_elapsed_time(WHITE);
// 如果规则要求计时则time1和time2表示倒计时
// If the rule requires a timer, time1 and time2 indicate a countdown
if (timeLimit > 0) {
// 玩家的剩余时间
// Player's remaining time
remainingTime[BLACK] = timeLimit * 60 - remainingTime[BLACK];
remainingTime[WHITE] = timeLimit * 60 - remainingTime[WHITE];
}
@ -698,46 +688,44 @@ void Game::timerEvent(QTimerEvent *event)
emit time1Changed(qt1.toString("hh:mm:ss"));
emit time2Changed(qt2.toString("hh:mm:ss"));
// 如果胜负已分
// If it's divided
const Color winner = position.get_winner();
if (winner != NOBODY) {
// 停止计时
// Stop the clock
killTimer(timeID);
// 定时器ID为0
// Timer ID is 0
timeID = 0;
// 发信号更新状态栏
// Signal update status bar
updateScence();
message = QString::fromStdString(getTips());
emit statusBarChanged(message);
// 弹框
//QMessageBox::about(NULL, "游戏结果", message);
// 播放音效
#ifndef DONOT_PLAY_WIN_SOUND
playSound(GameSound::win, winner);
#endif
}
// 测试用代码
// For debugging
#if 0
int ti = time.elapsed();
static QTime t;
if (ti < 0)
ti += 86400; // 防止过24:00引起的时间误差加上一天中总秒数
ti += 86400; // Prevent the time error caused by 24:00, plus the total number of seconds in a day
if (timeWhos == 1)
{
time1 = ti - time2;
// 用于显示时间的临时变量多出的50毫秒用于消除计时器误差产生的跳动
// A temporary variable used to display the time. The extra 50 ms is used to eliminate the beat caused by the timer error
t = QTime(0, 0, 0, 50).addMSecs(time1);
emit time1Changed(t.toString("hh:mm:ss"));
}
else if (timeWhos == 2)
{
time2 = ti - time1;
// 用于显示时间的临时变量多出的50毫秒用于消除计时器误差产生的跳动
// A temporary variable used to display the time. The extra 50 ms is used to eliminate the beat caused by the timer error
t = QTime(0, 0, 0, 50).addMSecs(time2);
emit time2Changed(t.toString("hh:mm:ss"));
}
@ -749,10 +737,10 @@ bool Game::isAIsTurn()
return isAiPlayer[sideToMove];
}
// 关键槽函数根据QGraphicsScene的信号和状态来执行选子、落子或去子
// Key slot function, according to the signal and state of qgraphics scene to select, drop or remove sub
bool Game::actionPiece(QPointF p)
{
// 点击非落子点,不执行
// Click non drop point, do not execute
File f;
Rank r;
@ -760,17 +748,17 @@ bool Game::actionPiece(QPointF p)
return false;
}
// 电脑走棋或正在搜索时,点击无效
// When the computer is playing chess or searching, the click is invalid
if (isAIsTurn() ||
aiThread[BLACK]->searching ||
aiThread[WHITE]->searching) {
return false;
}
// 在浏览历史记录时点击棋盘,则认为是悔棋
// When you click the chessboard while browsing the history, it is considered repentance
if (currentRow != manualListModel.rowCount() - 1) {
#ifndef MOBILE_APP_UI
// 定义新对话框
// Define new dialog box
QMessageBox msgBox;
msgBox.setIcon(QMessageBox::Question);
msgBox.setMinimumSize(600, 400);
@ -791,13 +779,13 @@ bool Game::actionPiece(QPointF p)
moveHistory.pop_back();
}
// 如果再决出胜负后悔棋,则重新启动计时
// If you regret the game, restart the timing
if (position.get_winner() == NOBODY) {
// 重新启动计时
// Restart timing
timeID = startTimer(100);
// 发信号更新状态栏
// Signal update status bar
updateScence();
message = QString::fromStdString(getTips());
emit statusBarChanged(message);
@ -809,11 +797,11 @@ bool Game::actionPiece(QPointF p)
}
}
// 如果未开局则开局
// If not, start
if (position.get_phase() == Phase::ready)
gameStart();
// 判断执行选子、落子或去子
// Judge whether to select, drop or remove the seed
bool result = false;
PieceItem *piece = nullptr;
QGraphicsItem *item = scene.itemAt(p, QTransform());
@ -822,17 +810,17 @@ bool Game::actionPiece(QPointF p)
case Action::place:
if (position.put_piece(f, r)) {
if (position.get_action() == Action::remove) {
// 播放成三音效
// Play form mill sound effects
playSound(GameSound::mill, position.side_to_move());
} else {
// 播放移动棋子音效
// Playing the sound effect of moving chess pieces
playSound(GameSound::drog, position.side_to_move());
}
result = true;
break;
}
// 如果移子不成功尝试重新选子这里不break
// If the moving is not successful, try to reselect. There is no break here
[[fallthrough]];
case Action::select:
@ -840,53 +828,49 @@ bool Game::actionPiece(QPointF p)
if (!piece)
break;
if (position.select_piece(f, r)) {
// 播放选子音效
playSound(GameSound::select, position.side_to_move());
result = true;
} else {
// 播放禁止音效
playSound(GameSound::banned, position.side_to_move());
}
break;
case Action::remove:
if (position.remove_piece(f, r)) {
// 播放音效
playSound(GameSound::remove, position.side_to_move());
result = true;
} else {
// 播放禁止音效
playSound(GameSound::banned, position.side_to_move());
}
break;
default:
// 如果是结局状态,不做任何响应
// If it is game over state, no response will be made
break;
}
if (result) {
moveHistory.emplace_back(position.record);
// 发信号更新状态栏
// Signal update status bar
updateScence();
message = QString::fromStdString(getTips());
emit statusBarChanged(message);
// 将新增的棋谱行插入到ListModel
// Insert the new chess score line into listmodel
currentRow = manualListModel.rowCount() - 1;
int k = 0;
// 输出命令行
// Output command line
for (const auto & i : *(move_hostory())) {
// 跳过已添加的因标准list容器没有下标
// Skip added because the standard list container has no subscripts
if (k++ <= currentRow)
continue;
manualListModel.insertRow(++currentRow);
manualListModel.setData(manualListModel.index(currentRow), i.c_str());
}
// 播放胜利或失败音效
// Play win or lose sound
#ifndef DONOT_PLAY_WIN_SOUND
const Color winner = position.get_winner();
if (winner != NOBODY &&
@ -894,12 +878,12 @@ bool Game::actionPiece(QPointF p)
playSound(GameSound::win, winner);
#endif
// AI设置
// 如果还未决出胜负
// AI settings
// If it's not decided yet
if (position.get_winner() == NOBODY) {
resumeAiThreads(position.sideToMove);
}
// 如果已经决出胜负
// If it's decided
else {
pauseThreads();
}
@ -919,13 +903,13 @@ bool Game::resign()
return false;
}
// 将新增的棋谱行插入到ListModel
// Insert the new record line into listmodel
currentRow = manualListModel.rowCount() - 1;
int k = 0;
// 输出命令行
// Output command line
for (const auto & i : *(move_hostory())) {
// 跳过已添加的因标准list容器没有下标
// Skip added because the standard list container has no index
if (k++ <= currentRow)
continue;
manualListModel.insertRow(++currentRow);
@ -939,7 +923,7 @@ bool Game::resign()
return result;
}
// 关键槽函数棋谱的命令行执行与actionPiece独立
// Key slot function, command line execution of chess score, independent of actionPiece
bool Game::command(const string &cmd, bool update /* = true */)
{
int total = 0;
@ -948,7 +932,7 @@ bool Game::command(const string &cmd, bool update /* = true */)
Q_UNUSED(hasSound)
#ifdef QT_GUI_LIB
// 防止接收滞后结束的线程发送的指令
// Prevents receiving instructions sent by threads that end late
if (sender() == aiThread[BLACK] && !isAiPlayer[BLACK])
return false;
@ -956,7 +940,6 @@ bool Game::command(const string &cmd, bool update /* = true */)
return false;
#endif // QT_GUI_LIB
// 声音
GameSound soundType = GameSound::none;
switch (position.get_action()) {
@ -972,7 +955,6 @@ bool Game::command(const string &cmd, bool update /* = true */)
}
//#endif
// 如果未开局则开局
if (position.get_phase() == Phase::ready) {
gameStart();
}
@ -995,35 +977,35 @@ bool Game::command(const string &cmd, bool update /* = true */)
updateScence(position);
}
// 发信号更新状态栏
// Signal update status bar
updateScence();
message = QString::fromStdString(getTips());
emit statusBarChanged(message);
// 对于新开局
// For opening
if (move_hostory()->size() <= 1) {
manualListModel.removeRows(0, manualListModel.rowCount());
manualListModel.insertRow(0);
manualListModel.setData(manualListModel.index(0), position.get_record());
currentRow = 0;
}
// 对于当前局
// For the current position
else {
currentRow = manualListModel.rowCount() - 1;
// 跳过已添加行,迭代器不支持+运算符,只能一个个++
// Skip the added rows. The iterator does not support the + operator and can only skip one by one++
auto i = (move_hostory()->begin());
for (int r = 0; i != (move_hostory())->end(); i++) {
if (r++ > currentRow)
break;
}
// 将新增的棋谱行插入到ListModel
// Insert the new chess score line into listmodel
while (i != move_hostory()->end()) {
manualListModel.insertRow(++currentRow);
manualListModel.setData(manualListModel.index(currentRow), (*i++).c_str());
}
}
// 播放胜利或失败音效
// Play win or lose sound
#ifndef DONOT_PLAY_WIN_SOUND
const Color winner = position.get_winner();
if (winner != NOBODY &&
@ -1032,12 +1014,12 @@ bool Game::command(const string &cmd, bool update /* = true */)
}
#endif
// AI设置
// 如果还未决出胜负
// AI Settings
// If it's not decided yet
if (position.get_winner() == NOBODY) {
resumeAiThreads(position.sideToMove);
}
// 如果已经决出胜负
// If it's decided
else {
pauseThreads();
@ -1106,16 +1088,15 @@ bool Game::command(const string &cmd, bool update /* = true */)
}
#ifdef MESSAGEBOX_ENABLE
// 弹框
message = QString::fromStdString(position.get_tips());
QMessageBox::about(NULL, "游戏结果", message);
QMessageBox::about(NULL, "Game Result", message);
#endif
}
gameTest->writeToMemory(QString::fromStdString(cmd));
#ifdef NET_FIGHT_SUPPORT
// 网络: 将着法放到服务器的发送列表中
// Network: put the method in the server's send list
getServer()->setAction(QString::fromStdString(cmd));
#endif
@ -1143,14 +1124,14 @@ bool Game::command(const string &cmd, bool update /* = true */)
return true;
}
// 浏览历史局面通过command函数刷新局面显示
// Browse the historical situation and refresh the situation display through the command function
bool Game::phaseChange(int row, bool forceUpdate)
{
// 如果row是当前浏览的棋谱行则不需要刷新
// If row is the currently viewed chess score line, there is no need to refresh it
if (currentRow == row && !forceUpdate)
return false;
// 需要刷新
// Need to refresh
currentRow = row;
int rows = manualListModel.rowCount();
QStringList mlist = manualListModel.stringList();
@ -1162,10 +1143,10 @@ bool Game::phaseChange(int row, bool forceUpdate)
position.command(mlist.at(i).toStdString().c_str());
}
// 下面这步关键,会让悔棋者承担时间损失
// The key step is to let the penitent bear the loss of time
set_start_time(static_cast<int>(start_timeb()));
// 刷新棋局场景
// Refresh the chess scene
updateScence(position);
return true;
@ -1181,16 +1162,16 @@ bool Game::updateScence(Position &p)
const Piece *board = p.get_board();
QPointF pos;
// game类中的棋子代码
// Chess code in game class
int key;
// 棋子总数
// Total number of pieces
int nTotalPieces = rule.piecesCount * 2;
// 动画组
// Animation group
auto *animationGroup = new QParallelAnimationGroup;
// 棋子就位
// The pieces are in place
PieceItem *piece = nullptr;
PieceItem *deletedPiece = nullptr;
@ -1199,21 +1180,21 @@ bool Game::updateScence(Position &p)
piece->setSelected(false);
// 将pieceList的下标转换为game的棋子代号
// Convert the subscript of pieceList to the chess code of game
key = (i % 2) ? (i / 2 + W_STONE_1) : (i / 2 + B_STONE_1);
int j;
// 遍历棋盘,查找并放置棋盘上的棋子
// Traverse the board, find and place the pieces on the board
for (j = SQ_BEGIN; j < SQ_END; j++) {
if (board[j] == key) {
pos = scene.polar2pos(File(j / RANK_NB), Rank(j % RANK_NB + 1));
if (piece->pos() != pos) {
// 让移动的棋子位于顶层
// Let the moving pieces be at the top level
piece->setZValue(1);
// 棋子移动动画
// Pieces movement animation
QPropertyAnimation *animation = new QPropertyAnimation(piece, "pos");
animation->setDuration(durationTime);
animation->setStartValue(piece->pos());
@ -1221,16 +1202,16 @@ bool Game::updateScence(Position &p)
animation->setEasingCurve(QEasingCurve::InOutQuad);
animationGroup->addAnimation(animation);
} else {
// 让静止的棋子位于底层
// Let the still pieces be at the bottom
piece->setZValue(0);
}
break;
}
}
// 如果没有找到,放置棋盘外的棋子
// If not, place the pieces outside the chessboard
if (j == (RANK_NB) * (FILE_NB + 1)) {
// 判断是被吃掉的子,还是未安放的子
// Judge whether it is a removing seed or an unplaced one
if (key & B_STONE) {
pos = (key - 0x11 < nTotalPieces / 2 - p.count<IN_HAND>(BLACK)) ?
scene.pos_p2_g : scene.pos_p1;
@ -1240,7 +1221,7 @@ bool Game::updateScence(Position &p)
}
if (piece->pos() != pos) {
// 为了对最近移除的棋子置为选择状态作准备
// In order to prepare for the selection of the recently removed pieces
deletedPiece = piece;
#ifdef GAME_PLACING_SHOW_REMOVED_PIECES
@ -1261,7 +1242,7 @@ bool Game::updateScence(Position &p)
piece->setSelected(false);
}
// 添加摆棋阶段禁子点
// Add banned points in placing phase
if (rule.hasBannedLocations && p.get_phase() == Phase::placing) {
for (int j = SQ_BEGIN; j < SQ_END; j++) {
if (board[j] == BAN_STONE) {
@ -1280,7 +1261,7 @@ bool Game::updateScence(Position &p)
}
}
// 走棋阶段清除禁子点
// Clear banned points in moving phase
if (rule.hasBannedLocations && p.get_phase() != Phase::placing) {
while (nTotalPieces < static_cast<int>(pieceList.size())) {
delete pieceList.at(pieceList.size() - 1);
@ -1288,7 +1269,7 @@ bool Game::updateScence(Position &p)
}
}
// 选中当前棋子
// Select the current piece
int ipos = p.current_square();
if (ipos) {
key = board[p.current_square()];
@ -1299,19 +1280,19 @@ bool Game::updateScence(Position &p)
}
}
// 对最近移除的棋子置为选择状态
// Set the most recently removed pieces to select action
if (deletedPiece) {
deletedPiece->setSelected(true);
}
animationGroup->start(QAbstractAnimation::DeleteWhenStopped);
// 更新比分 LCD 显示
// Update LCD display
emit score1Changed(QString::number(p.score[BLACK], 10));
emit score2Changed(QString::number(p.score[WHITE], 10));
emit scoreDrawChanged(QString::number(p.score_draw, 10));
// 更新胜率 LCD 显示
// Update winning rate LCD display
position.gamesPlayedCount = position.score[BLACK] + position.score[WHITE] + position.score_draw;
int winningRate_1 = 0, winningRate_2 = 0, winningRate_draw = 0;
if (position.gamesPlayedCount != 0) {
@ -1362,19 +1343,16 @@ void Game::saveScore()
QFile file;
// 文件对象
file.setFileName(path);
if (file.isOpen()) {
file.close();
}
// 打开文件,只写方式打开
if (!(file.open(QFileDevice::WriteOnly | QFileDevice::Text))) {
return;
}
// 写文件
QTextStream textStream(&file);
textStream << QCoreApplication::applicationFilePath() << endl << endl;

View File

@ -13,14 +13,15 @@
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
along with this program. If not, see <http:// www.gnu.org/licenses/>.
*/
/* 这个类处理场景对象QGraphicsScene
* MVC模型中唯一的控制模块
*
* QGraphicsScene实现它
*
/*
* This class deals with the scene object QGraphicsScene
* It is the only control module in MVC model of this program
* It doesn't do any operation on the controls in the main window, only signals the main window
* You could have overloaded QGraphicsScene to implement it and saved the trouble of writing event filters
* But it doesn't look good to use one scene class to do so many control module operations
*/
#ifndef GAMECONTROLLER_H
@ -86,7 +87,7 @@ public:
);
~Game() override;
//主窗口菜单栏明细
// Main window menu bar details
const map<int, QStringList> getActions();
int getRuleIndex() noexcept
@ -166,101 +167,101 @@ public:
signals:
// 总盘数改变的信号
// Signal of total disk number change
void nGamesPlayedChanged(const QString &score);
// 玩家1(先手)赢盘数改变的信号
// Player 1 (first hand) signal to change the number of winning sets
void score1Changed(const QString &score);
// 玩家2(后手)赢盘数改变的信号
// Signal for player 2 (backhand) to change the number of winning sets
void score2Changed(const QString &score);
// 和棋数改变的信号
// The signal of the change of draw number
void scoreDrawChanged(const QString &score);
// 玩家1(先手)胜率改变的信号
// Signal of player 1 (first hand) winning rate change
void winningRate1Changed(const QString &score);
// 玩家2(后手)胜率改变的信号
// Signal of player 2 (backhand) winning rate change
void winningRate2Changed(const QString &score);
// 和棋率改变的信号
// Signal of change of draw rate
void winningRateDrawChanged(const QString &score);
// 玩家1(先手)用时改变的信号
// Player 1 (first hand) time changed signal
void time1Changed(const QString &time);
// 玩家2(后手)用时改变的信号
// Player 2 (backhand) time changed signal
void time2Changed(const QString &time);
// 通知主窗口更新状态栏的信号
// A signal that tells the main window to update the status bar
void statusBarChanged(const QString &message);
public slots:
// 设置规则
// Set rules
void setRule(int ruleNo, int stepLimited = std::numeric_limits<uint16_t>::max(), int timeLimited = -1);
// 游戏开始
// The game begins
void gameStart();
// 游戏重置
// Game reset
void gameReset();
// 设置编辑棋局状态
// Set edit chess state
void setEditing(bool arg = true) noexcept;
// 设置黑白反转状态
// Set black and white inversion state
void setInvert(bool arg = true);
// id为1时让电脑执先手, id为2时让的电脑执后手
// If Id is 1, let the computer take the lead; if Id is 2, let the computer take the second place
void setEngine(Color color, bool enabled = true);
void setEngineBlack(bool enabled);
void setEngineWhite(bool enabled);
// 是否有落子动画
// Is there a falling animation
void setAnimation(bool arg = true) noexcept;
// 是否有落子音效
// Is there a drop sound effect
void setSound(bool arg = true) noexcept;
// 播放声音
// Play the sound
static void playSound(GameSound soundType, Color c);
// 是否必败时认输
// Do you admit defeat when you lose
void setResignIfMostLose(bool enabled);
// 是否自动开局
// Auto start or not
void setAutoRestart(bool enabled = false);
// 是否开局自动改变先后手
// Is the start automatically changed to the first before the second
void setAutoChangeFirstMove(bool enabled = false);
// AI 是否随机走子
// Is AI random
void setShuffling(bool enabled);
// AI 是否记录残局库
// Does AI record the game library
void setLearnEndgame(bool enabled);
// Alpha-Beta 搜索时是否迭代加深
// Does alpha beta search deepen iteratively
void setIDS(bool enabled);
// DepthExtension
// DepthExtension
void setDepthExtension(bool enabled);
// OpeningBook
// OpeningBook
void setOpeningBook(bool enabled);
// 上下翻转
// Flip up and down
void flip();
// 左右镜像
// Left and right mirror images
void mirror();
// 视图须时针旋转90°
// The view must be rotated 90 ° clockwise
void turnRight();
// 视图逆时针旋转90°
// View rotated 90 degree counterclockwise
void turnLeft();
bool isAIsTurn();
@ -340,28 +341,28 @@ public slots:
delete aiThread[WHITE];
}
// 根据QGraphicsScene的信号和状态来执行选子、落子或去子
// According to the signal and state of qgraphics scene, select, drop or delete the sub objects
bool actionPiece(QPointF p);
// 认输
// Admit defeat
bool resign();
// 棋谱的命令行执行
// Command line execution of chess score
bool command(const string &cmd, bool update = true);
// 历史局面及局面改变
// Historical situation and situation change
bool phaseChange(int row, bool forceUpdate = false);
// 更新棋局显示,每步后执行才能刷新局面
// Update the chess game display. Only after each step can the situation be refreshed
bool updateScence();
bool updateScence(Position &p);
#ifdef NET_FIGHT_SUPPORT
// 显示网络配置窗口
// The network configuration window is displayed
void showNetworkWindow();
#endif
// 显示引擎对战窗口
// Show engine vs. window
void showTestWindow();
void saveScore();
@ -372,42 +373,46 @@ public slots:
}
protected:
//bool eventFilter(QObject * watched, QEvent * event);
// 定时器
// bool eventFilter(QObject * watched, QEvent * event);
// Timer
void timerEvent(QTimerEvent *event) override;
private:
// 棋对象的数据模型
// Data model of chess object
Position position;
Color sideToMove;
// 测试
// Testing
Test *gameTest;
private:
// 2个AI的线程
// 2 AI threads
Thread *aiThread[COLOR_NB];
// 棋局的场景类
// The scene class of chess game
GameScene &scene;
// 所有棋子
// All the pieces
vector<PieceItem *> pieceList;
// 当前棋子
// Current chess pieces
PieceItem *currentPiece;
// 当前浏览的棋谱行
// Current browsing chess score line
int currentRow;
// 是否处于“编辑棋局”状态
// Is it in "Edit chess game" state
bool isEditing;
// 是否黑白反转
// Reverse black and white
bool isInverted;
public:
// 电脑执先手时为 true
// True when the computer takes the lead
bool isAiPlayer[COLOR_NB];
string getTips()
@ -416,69 +421,70 @@ public:
}
private:
// 是否有落子动画
// Is there a falling animation
bool hasAnimation;
// 动画持续时间
// Animation duration
int durationTime;
// 游戏开始时间
// Game start time
TimePoint gameStartTime;
// 游戏结束时间
// Game end time
TimePoint gameEndTime;
// 游戏持续时间
// Game duration
TimePoint gameDurationTime;
// 游戏开始周期
// Game start cycle
stopwatch::rdtscp_clock::time_point gameStartCycle;
// 游戏结束周期
// Game end cycle
stopwatch::rdtscp_clock::time_point gameEndCycle;
// 游戏持续周期
// Game duration
stopwatch::rdtscp_clock::duration gameDurationCycle;
// 时间相关
// Time dependent
time_t startTime;
time_t currentTime;
time_t elapsedSeconds[COLOR_NB];
// 是否有落子音效
// Is there a drop sound effect
inline static bool hasSound {true};
// 是否必败时认输
// Do you admit defeat when you lose
bool resignIfMostLose_ {false};
// 是否自动交换先后手
// Do you want to exchange first before second
bool isAutoChangeFirstMove { false };
// AI 是否为先手
// Is ai the first
bool isAiFirstMove { false };
// 定时器ID
// Timer ID
int timeID;
// 规则号
// Rule number
int ruleIndex;
// 规则限时(分钟)
// Rule time limit (minutes)
int timeLimit;
// 规则限步数
// Rule step limit
int stepsLimit;
// 玩家剩余时间(秒)
// Player's remaining time (seconds)
time_t remainingTime[COLOR_NB];
// 用于主窗口状态栏显示的字符串
// String used to display the status bar of the main window
QString message;
// 棋谱字符串列表模型
// String list model of chess score
QStringListModel manualListModel;
// 提示语
// Hint
string tips;
std::vector <std::string> moveHistory;

View File

@ -34,7 +34,6 @@ GameScene::GameScene(QObject *parent) :
pos_p2(LINE_INTERVAL *(-4), LINE_INTERVAL *(-6)),
pos_p2_g(LINE_INTERVAL * 4, LINE_INTERVAL *(-6))
{
// 添加棋盘
board = new BoardItem;
board->setDiagonal(false);
addItem(board);
@ -45,71 +44,43 @@ GameScene::~GameScene()
delete board;
}
// 屏蔽掉Shift和Control按键事实证明没用按键事件未必由视图类处理
#if 0
void GameScene::keyPressEvent(QKeyEvent *keyEvent)
{
if(keyEvent->key() == Qt::Key_Shift || keyEvent->key() == Qt::Key_Control)
return;
QGraphicsScene::keyPressEvent(keyEvent);
}
#endif
void GameScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
//屏蔽双击事件
// Block double click events
mouseEvent->accept();
}
void GameScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
// 屏蔽鼠标按下事件
// Screen mouse down events
mouseEvent->accept();
#if 0
// 只处理左键事件
if(mouseEvent->button() != Qt::LeftButton)
return;
// 如果不是棋子则结束
QGraphicsItem *item = itemAt(mouseEvent->scenePos(), QTransform());
if (!item || item->type() != PieceItem::Type)
{
return;
}
// 调用默认事件处理函数
//QGraphicsScene::mousePressEvent(mouseEvent);
#endif
}
void GameScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
// 只处理左键事件
// Only handle left click events
if (mouseEvent->button() != Qt::LeftButton) {
mouseEvent->accept();
return;
}
// 如果是棋盘
// If it's a board
const QGraphicsItem *item = itemAt(mouseEvent->scenePos(), QTransform());
if (!item || item->type() == BoardItem::Type) {
QPointF p = mouseEvent->scenePos();
p = board->nearestPosition(p);
if (p != QPointF(0, 0))
// 发送鼠标点最近的落子点
// Send the nearest drop point of the mouse point
emit mouseReleased(p);
} // 如果是棋子
} // If it's a chess piece
else if (item->type() == PieceItem::Type) {
// 将当前棋子在场景中的位置发送出去
// Send out the position of the current piece in the scene
emit mouseReleased(item->scenePos());
}
mouseEvent->accept();
// 调用默认事件处理函数
//QGraphicsScene::mouseReleaseEvent(mouseEvent);
}
QPointF GameScene::polar2pos(File file, Rank rank)

View File

@ -33,19 +33,16 @@ public:
explicit GameScene(QObject *parent = nullptr);
~GameScene() override;
// 将模型的圈、位转化为落子点坐标
QPointF polar2pos(File file, Rank rank);
// 将落子点坐标转化为模型用的圈、位
bool pos2polar(QPointF pos, File &file, Rank &rank);
// 设置棋盘斜线
void setDiagonal(bool arg = true);
// 玩家1的己方棋盒及对方棋盒位置
// Position of player 1's own board and opponent's board
const QPointF pos_p1, pos_p1_g;
// 玩家2的己方棋盒及对方棋盒位置
// Position of player 2's own board and opponent's board
const QPointF pos_p2, pos_p2_g;
protected:
@ -60,7 +57,6 @@ signals:
public slots:
private:
// 棋盘对象
BoardItem *board {nullptr};
};

View File

@ -24,36 +24,31 @@ GameView::GameView(QWidget *parent) :
QGraphicsView(parent)
{
Q_UNUSED(parent)
/* 不使用下面的方法
// 初始化缩放因子为1.0
sx = 1.0;
sy = 1.0;
*/
}
GameView::~GameView() = default;
void GameView::flip()
{
// 视图上下翻转
/* 以下用到了很多图形变换矩阵方面的知识
* scale方法Qt的图形变换是针对坐标系的
*
// Flip view up and down
/* The following uses a lot of knowledge about graphic transformation matrix
* Do not use the scale method, QT graphics transformation is for the coordinate system
* Scale matrix to
* sx 0 0
* S = 0 sy 0
* 0 0 1
*
* The up and down flip should be multiplied by the following matrix on the basis of the original transformation matrix:
* 1 0 0
* 0 -1 0
* 0 0 1
*/
// 方法一: 直接在原变换矩阵基础上乘以上面的矩阵
// QMatrix只对变换矩阵前两列赋值
// Method 1: directly multiply the original transformation matrix by the above matrix
// QMatrix only assigns values to the first two columns of the transformation matrix
setMatrix(matrix() * QMatrix(1, 0, 0, -1, 0, 0));
/* 方法二: 人工计算好新的变换矩阵后再对场景赋值
*
/* Method 2: manually calculate the new transformation matrix and then assign a value to the scene
* The efficiency of this method is not necessarily high, and manual calculation is needed
QMatrix mt = matrix();
mt.setMatrix(-mt.m11(), mt.m12(), -mt.m21(), mt.m22(), -mt.dx(), mt.dy());
setMatrix(mt);
@ -62,8 +57,9 @@ void GameView::flip()
void GameView::mirror()
{
// 视图左右镜像
/* 左右镜像应在原变换矩阵基础上乘以一个如下的矩阵:
// Left and right mirror of view
/* The left and right mirror images shall be multiplied by the following matrix
on the basis of the original transformation matrix:
* -1 0 0
* 0 1 0
* 0 0 1
@ -73,13 +69,15 @@ void GameView::mirror()
void GameView::turnRight()
{
// 视图须时针旋转90°
/* 不要用scale方法视图镜像或翻转后它的转向会反过来
*
// The view must be rotated 90 degree clockwise
/* Don't use the scale method.
After the view is mirrored or flipped, its steering will be reversed
* The rotation matrix is
* cos(α) sin(α) 0
* R = -sin(α) cos(α) 0
* 0 0 1
* 90°
* The view must be rotated 90 degree clockwise and multiplied by
* the following matrix on the basis of the original transformation matrix:
* 0 1 0
* -1 0 0
* 0 0 1
@ -89,8 +87,10 @@ void GameView::turnRight()
void GameView::turnLeft()
{
// 视图逆时针旋转90°
/* 视图逆时针旋转90°应在原变换矩阵基础上乘以一个如下的矩阵
// View rotated 90 ° counterclockwise
/* When the view is rotated 90 degree counterclockwise,
* it should be multiplied by the following matrix
* on the basis of the original transformation matrix:
* 0 -1 0
* 1 0 0
* 0 0 1
@ -101,23 +101,6 @@ void GameView::turnLeft()
void GameView::resizeEvent(QResizeEvent *event)
{
#if 0
// 不使用下面的形式了
// 让场景适合视图
if (sceneRect().width() <= 0 || sceneRect().height() <= 0)
return;
// 恢复缩放前的大小
scale(1 / sx, 1 / sy);
// 设置缩放因子
sx = width() / sceneRect().width();
sy = height() / sceneRect().height();
sx = sx < sy ? sx : sy;
sy = sx;
// 缩放视图适合场景大小
scale(sx, sy);
#endif
// 使用如下形式,更简洁
QGraphicsView::resizeEvent(event);
fitInView(sceneRect(), Qt::KeepAspectRatio);
}

View File

@ -16,7 +16,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// 派生这个类主要是为了让视图适应场景大小及图像旋转镜像操作
#ifndef GRAPHICSVIEW_H
#define GRAPHICSVIEW_H
@ -24,6 +23,8 @@
#include "config.h"
// This class is mainly derived to make the view adapt to
// the scene size and image rotation mirror operation
class GameView : public QGraphicsView
{
Q_OBJECT
@ -40,10 +41,6 @@ public slots:
protected:
void resizeEvent(QResizeEvent *event) override;
private:
// 缩放因子,代码更新后不使用了
// qreal sx, sy;
};
#endif // GRAPHICSVIEW_H

View File

@ -52,45 +52,47 @@ MillGameWindow::MillGameWindow(QWidget * parent) :
{
ui.setupUi(this);
// 去掉标题栏
// Remove the title bar
//setWindowFlags(Qt::FramelessWindowHint);
// 设置透明(窗体标题栏不透明,背景透明,如果不去掉标题栏,背景就变为黑色)
// Set transparency
// (the title bar of the form is opaque and the background is transparent.
// If the title bar is not removed, the background will turn black)
//setAttribute(Qt::WA_TranslucentBackground);
// 设置全体透明度系数
// Set the overall transparency factor
//setWindowOpacity(0.7);
// 设置场景
// Set up the scene
scene = new GameScene(this);
// 设置场景尺寸大小为棋盘大小的1.08倍
// Set the scene size to 1.08 times the board size
scene->setSceneRect(-BOARD_SIZE * 0.54, -BOARD_SIZE * 0.54,
BOARD_SIZE * 1.08, BOARD_SIZE * 1.08);
// 初始化各个控件
// Initialize the controls
// 关联视图和场景
// Associate views and scenes
ui.gameView->setScene(scene);
// 视图反走样
// View anti aliasing
ui.gameView->setRenderHint(QPainter::Antialiasing, true);
// 视图反锯齿
// View anti aliasing
ui.gameView->setRenderHint(QPainter::Antialiasing);
// 因功能限制,使部分功能不可用,将来再添加
// Due to function limitation, some functions are not available and will be added in the future
ui.actionInternet_I->setDisabled(false);
ui.actionSetting_O->setDisabled(true);
// 初始化游戏规则菜单
// Initialize game rules menu
ui.menu_R->installEventFilter(this);
// 关联自动运行定时器
// Associated auto run timer
connect(&autoRunTimer, SIGNAL(timeout()),
this, SLOT(onAutoRunTimeOut()));
// 主窗口居中显示
// Center primary window
QRect deskTopRect = QGuiApplication::primaryScreen()->geometry();
const int unitw = (deskTopRect.width() - width()) / 2;
const int unith = (deskTopRect.height() - height()) / 2;
@ -102,14 +104,14 @@ MillGameWindow::MillGameWindow(QWidget * parent) :
#endif
#ifdef MOBILE_APP_UI
// 隐藏菜单栏、工具栏、状态栏等
// Hide menu bar, toolbar, status bar, etc
ui.menuBar->setVisible(false);
ui.mainToolBar->setVisible(false);
ui.dockWidget->setVisible(false);
ui.statusBar->setVisible(false);
#endif
// 游戏初始化
// Game initialization
initialize();
}
@ -128,7 +130,7 @@ void MillGameWindow::closeEvent(QCloseEvent *event)
if (file.isOpen())
file.close();
// 取消自动运行
// Cancel auto run
ui.actionAutoRun_A->setChecked(false);
loggerDebug("closed\n");
@ -138,7 +140,7 @@ void MillGameWindow::closeEvent(QCloseEvent *event)
bool MillGameWindow::eventFilter(QObject *watched, QEvent *event)
{
// 重载这个函数只是为了让规则菜单(动态)显示提示
// This function is overridded just to make the rules menu (dynamic) display prompts
if (watched == ui.menu_R &&
event->type() == QEvent::ToolTip) {
const auto *he = dynamic_cast <QHelpEvent *> (event);
@ -154,36 +156,36 @@ bool MillGameWindow::eventFilter(QObject *watched, QEvent *event)
void MillGameWindow::initialize()
{
// 初始化函数,仅执行一次
// Initialize the function and execute it only once
if (game)
return;
// 开辟一个新的游戏控制器
// New a new game controller
game = new Game(*scene, this);
// 添加新菜单栏动作
// Add a new menu bar action
map<int, QStringList> actions = game->getActions();
for (auto i = actions.begin(); i != actions.end(); i++) {
// map的key存放int索引值value存放规则名称和规则提示
// The key of map stores int index value, and value stores rule name and rule prompt
auto *ruleAction = new QAction(i->second.at(0), this);
ruleAction->setToolTip(i->second.at(1));
ruleAction->setCheckable(true);
// 索引值放在QAction的Data里
// The index value is put in the data of QAction
ruleAction->setData(i->first);
// 添加到动作列表
// Add to action list
ruleActionList.push_back(ruleAction);
// 添加到“规则”菜单
// Add to rules menu
ui.menu_R->addAction(ruleAction);
connect(ruleAction, SIGNAL(triggered()),
this, SLOT(actionRules_triggered()));
}
// 关联主窗口动作信号和控制器的槽
// The main window controller is associated with the action of the signal slot
connect(ui.actionResign_G, SIGNAL(triggered()),
game, SLOT(resign()));
@ -248,93 +250,74 @@ void MillGameWindow::initialize()
connect(ui.actionOpeningBook_O, SIGNAL(toggled(bool)),
game, SLOT(setOpeningBook(bool)));
// 视图上下翻转
connect(ui.actionFlip_F, &QAction::triggered,
game, &Game::flip);
// 视图左右镜像
connect(ui.actionMirror_M, &QAction::triggered,
game, &Game::mirror);
// 视图须时针旋转90°
connect(ui.actionTurnRight_R, &QAction::triggered,
game, &Game::turnRight);
// 视图逆时针旋转90°
connect(ui.actionTurnLeftt_L, &QAction::triggered,
game, &Game::turnLeft);
// 关联控制器的信号和主窗口控件的槽
// 更新LCD显示总盘数
connect(game, SIGNAL(nGamesPlayedChanged(QString)),
ui.scoreLcdNumber_GamesPlayed, SLOT(display(QString)));
// 更新LCD显示玩家1赢盘数
connect(game, SIGNAL(score1Changed(QString)),
ui.scoreLcdNumber_1, SLOT(display(QString)));
// 更新LCD显示玩家2赢盘数
connect(game, SIGNAL(score2Changed(QString)),
ui.scoreLcdNumber_2, SLOT(display(QString)));
// 更新LCD显示和棋数
connect(game, SIGNAL(scoreDrawChanged(QString)),
ui.scoreLcdNumber_draw, SLOT(display(QString)));
// 更新LCD显示玩家1赢盘率
connect(game, SIGNAL(winningRate1Changed(QString)),
ui.winningRateLcdNumber_1, SLOT(display(QString)));
// 更新LCD显示玩家2赢盘率
connect(game, SIGNAL(winningRate2Changed(QString)),
ui.winningRateLcdNumber_2, SLOT(display(QString)));
// 更新LCD显示和棋率
connect(game, SIGNAL(winningRateDrawChanged(QString)),
ui.winningRateLcdNumber_draw, SLOT(display(QString)));
// 更新LCD1显示玩家1用时
connect(game, SIGNAL(time1Changed(QString)),
ui.lcdNumber_1, SLOT(display(QString)));
// 更新LCD2显示玩家2用时
connect(game, SIGNAL(time2Changed(QString)),
ui.lcdNumber_2, SLOT(display(QString)));
// 关联场景的信号和控制器的槽
connect(scene, SIGNAL(mouseReleased(QPointF)),
game, SLOT(actionPiece(QPointF)));
// 为状态栏添加一个正常显示的标签
// Add a normal display label to the status bar
auto *statusBarlabel = new QLabel(this);
QFont statusBarFont;
statusBarFont.setPointSize(16);
statusBarlabel->setFont(statusBarFont);
ui.statusBar->addWidget(statusBarlabel);
// 更新状态栏
connect(game, SIGNAL(statusBarChanged(QString)),
connect(game, SIGNAL(statusBarChanged(QString)),
statusBarlabel, SLOT(setText(QString)));
// 默认规则
ruleNo = DEFAULT_RULE_NUMBER;
ruleActionList[ruleNo]->setChecked(true);
// 重置游戏规则
game->setRule(ruleNo);
// 更新规则显示
// Update rule display
ruleInfo();
// 关联列表视图和字符串列表模型
// List of associated models and string views
ui.listView->setModel(game->getManualListModel());
// 因为QListView的rowsInserted在setModel之后才能启动
// 第一次需手动初始化选中listView第一项
// Because QListView's rowsInserted can only be started after setModel,
// The first time you need to manually initialize, select the first item of listView
ui.listView->setCurrentIndex(ui.listView->model()->index(0, 0));
// 初始局面、前一步、后一步、最终局面的槽
// //The slot of the initial situation, the previous step, the next step and the final situation
connect(ui.actionBegin_S, &QAction::triggered,
this, &MillGameWindow::on_actionRowChange);
@ -356,14 +339,14 @@ void MillGameWindow::initialize()
connect(ui.actionEnd_E, &QAction::triggered,
this, &MillGameWindow::on_actionRowChange);
// 手动在listView里选择着法后更新的槽
// Manually select the updated slot in listView
connect(ui.listView, &ManualListView::currentChangedSignal,
this, &MillGameWindow::on_actionRowChange);
// 更新四个键的状态
// Update the status of the four keys
on_actionRowChange();
// 设置窗体大小
// Set form size
#ifdef MOBILE_APP_UI
#if 0
const int screen_iPhone_XS_Max[] = {1242, 2688};
@ -393,7 +376,6 @@ void MillGameWindow::initialize()
ui.pushButton_hint->setVisible(false);
#endif /* MOBILE_APP_UI */
// 窗口最大化
#ifdef SHOW_MAXIMIZED_ON_LOAD
showMaximized();
QWidget::setWindowFlags(Qt::WindowMaximizeButtonHint |
@ -422,18 +404,18 @@ void MillGameWindow::ruleInfo()
const int s = game->getStepsLimit();
const int t = game->getTimeLimit();
QString tl(" 不限时");
QString sl(" 不限步");
QString tl(" No Time Limit");
QString sl(" No Steps Limit");
if (s > 0)
sl = " " + QString::number(s) + "";
sl = " Limit" + QString::number(s) + "steps";
if (t > 0)
tl = " 限时" + QString::number(s) + "";
tl = " Limit" + QString::number(s) + "min";
// 规则显示
// Rule display
ui.labelRule->setText(tl + sl);
// 规则提示
// Rule tips
ui.labelInfo->setToolTip(QString(RULES[ruleNo].name) + "\n" +
RULES[ruleNo].description);
@ -455,15 +437,12 @@ void MillGameWindow::saveBook(const QString &path)
file.close();
}
// 文件对象
file.setFileName(path);
// 打开文件,只写方式打开
if (!(file.open(QFileDevice::WriteOnly | QFileDevice::Text))) {
return;
}
// 写文件
QTextStream textStream(&file);
auto *strlist = qobject_cast<QStringListModel *>(ui.listView->model());
@ -476,16 +455,9 @@ void MillGameWindow::saveBook(const QString &path)
void MillGameWindow::on_actionLimited_T_triggered()
{
/*
* uiQDialog派生个自己的对话框
*
*
* QDialog界面
*/
int gStep = game->getStepsLimit();
int gTime = game->getTimeLimit();
// 定义新对话框
auto *dialog = new QDialog(this);
dialog->setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint);
dialog->setObjectName(QStringLiteral("Dialog"));
@ -493,15 +465,14 @@ void MillGameWindow::on_actionLimited_T_triggered()
dialog->resize(256, 108);
dialog->setModal(true);
// 生成各个控件
auto *formLayout = new QFormLayout(dialog);
auto *label_step = new QLabel(dialog);
auto *label_time = new QLabel(dialog);
auto *comboBox_step = new QComboBox(dialog);
auto *comboBox_time = new QComboBox(dialog);
auto *buttonBox = new QDialogButtonBox(dialog);
#if 0
// 设置各个控件ObjectName不设也没关系
formLayout->setObjectName(QStringLiteral("formLayout"));
label_step->setObjectName(QStringLiteral("label_step"));
label_time->setObjectName(QStringLiteral("label_time"));
@ -509,7 +480,7 @@ void MillGameWindow::on_actionLimited_T_triggered()
comboBox_time->setObjectName(QStringLiteral("comboBox_time"));
buttonBox->setObjectName(QStringLiteral("buttonBox"));
#endif
// 设置各个控件数据
label_step->setText(tr("If the number of moves exceeds the limit, it will get a draw:"));
label_time->setText(tr("Either side loses if it times out:"));
comboBox_step->addItem(tr("Infinite"), 0);
@ -527,7 +498,6 @@ void MillGameWindow::on_actionLimited_T_triggered()
buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
// 布局
formLayout->setSpacing(6);
formLayout->setContentsMargins(11, 11, 11, 11);
formLayout->setWidget(0, QFormLayout::LabelRole, label_step);
@ -536,54 +506,46 @@ void MillGameWindow::on_actionLimited_T_triggered()
formLayout->setWidget(1, QFormLayout::FieldRole, comboBox_time);
formLayout->setWidget(2, QFormLayout::SpanningRole, buttonBox);
// 关联信号和槽函数
connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));
connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));
// 收集数据
if (dialog->exec() == QDialog::Accepted) {
int dStep = comboBox_step->currentData().toInt();
int dTime = comboBox_time->currentData().toInt();
if (gStep != dStep || gTime != dTime) {
// 重置游戏规则
game->setRule(ruleNo, static_cast<int>(dStep), dTime);
}
}
// 删除对话框,子控件会一并删除
dialog->disconnect();
delete dialog;
// 更新规则显示
ruleInfo();
}
void MillGameWindow::actionRules_triggered()
{
// 取消自动运行
ui.actionAutoRun_A->setChecked(false);
// 取消其它规则的选择
// Cancel the selection of other rules
for (QAction *action : ruleActionList)
action->setChecked(false);
// 选择当前规则
// Select current rule
auto *action = dynamic_cast<QAction *>(sender());
action->setChecked(true);
ruleNo = action->data().toInt();
// 如果游戏规则没变化,则返回
// If the rules of the game have not changed, return
if (ruleNo == game->getRuleIndex())
return;
// 取消AI设定
// Cancel AI setting
ui.actionEngine1_T->setChecked(false);
ui.actionEngine2_R->setChecked(false);
// 重置游戏规则
game->setRule(ruleNo);
// 更新规则显示
ruleInfo();
}
@ -591,7 +553,8 @@ void MillGameWindow::on_actionNew_N_triggered()
{
auto *strlist = qobject_cast<QStringListModel *>(ui.listView->model());
// 棋未下完,且已经走了若干步以上,则算对手得分
// If you have not finished playing game and have already taken more than a few steps,
// you will be lost
if (strlist->stringList().size() > 12) {
game->humanResign();
}
@ -623,19 +586,16 @@ void MillGameWindow::on_actionNew_N_triggered()
+ whoWin + "_" + strDateTime
+ ".txt";
// 下了一定步数之后新建游戏时才保存棋谱
// After a certain number of steps, save the score when creating a new game
if (strlist->stringList().size() > 18) {
saveBook(path);
}
#endif /* SAVE_GAMEBOOK_WHEN_ACTION_NEW_TRIGGERED */
// 取消自动运行
ui.actionAutoRun_A->setChecked(false);
// 重置游戏规则
game->gameReset();
// 重设AI设定
if (ui.actionEngine2_R->isChecked()) {
ui.actionEngine2_R->setChecked(false);
ui.actionEngine2_R->setChecked(true);
@ -649,7 +609,7 @@ void MillGameWindow::on_actionNew_N_triggered()
void MillGameWindow::on_actionOpen_O_triggered()
{
QString path = QFileDialog::getOpenFileName(this, tr("打开棋谱文件"), QDir::currentPath(), "TXT(*.txt)");
QString path = QFileDialog::getOpenFileName(this, tr("Open the move history file"), QDir::currentPath(), "TXT(*.txt)");
if (path.isEmpty()) {
return;
@ -659,36 +619,31 @@ void MillGameWindow::on_actionOpen_O_triggered()
file.close();
}
// 文件对象
file.setFileName(path);
// 不支持 1MB 以上的文件
// Files larger than 1MB are not supported
if (file.size() > 0x100000) {
// 定义新对话框
QMessageBox msgBox(QMessageBox::Warning,
tr("文件过大"), tr("不支持 1MB 以上文件"), QMessageBox::Ok);
tr("The file is too large"), tr("Files larger than 1MB are not supported"), QMessageBox::Ok);
msgBox.exec();
return;
}
// 打开文件,只读方式打开
if (!(file.open(QFileDevice::ReadOnly | QFileDevice::Text))) {
return;
}
// 取消AI设定
ui.actionEngine1_T->setChecked(false);
ui.actionEngine2_R->setChecked(false);
// 读文件
QTextStream textStream(&file);
QString cmd;
cmd = textStream.readLine();
// 读取并显示棋谱时,不必刷新棋局场景
// When reading and displaying the move history, there is no need to refresh the scene
if (!(game->command(cmd.toStdString(), false))) {
// 定义新对话框
QMessageBox msgBox(QMessageBox::Warning, tr("文件错误"), tr("不是正确的棋谱文件"), QMessageBox::Ok);
QMessageBox msgBox(QMessageBox::Warning, tr("File error"), tr("Not the correct move hisory file"), QMessageBox::Ok);
msgBox.exec();
return;
}
@ -698,7 +653,7 @@ void MillGameWindow::on_actionOpen_O_triggered()
game->command(cmd.toStdString(), false);
}
// 最后刷新棋局场景
// Finally, refresh the scene
game->updateScence();
}
@ -707,9 +662,7 @@ void MillGameWindow::on_actionSave_S_triggered()
if (file.isOpen()) {
file.close();
// 打开文件,只写方式打开
if (file.open(QFileDevice::WriteOnly | QFileDevice::Text)) {
// 写文件
QTextStream textStream(&file);
auto *strlist = qobject_cast<QStringListModel *>(ui.listView->model());
for (const QString &cmd : strlist->stringList())
@ -726,8 +679,8 @@ void MillGameWindow::on_actionSave_S_triggered()
void MillGameWindow::on_actionSaveAs_A_triggered()
{
QString path = QFileDialog::getSaveFileName(this,
tr("打开棋谱文件"),
QDir::currentPath() + tr("棋谱_") + QString::number(QDateTime::currentDateTimeUtc().toTime_t())+ ".txt", "TXT(*.txt)");
tr("Open the move history file"),
QDir::currentPath() + tr("MoveHistory_") + QString::number(QDateTime::currentDateTimeUtc().toTime_t())+ ".txt", "TXT(*.txt)");
saveBook(path);
}
@ -739,26 +692,23 @@ void MillGameWindow::on_actionEdit_E_toggled(bool arg1)
void MillGameWindow::on_actionInvert_I_toggled(bool arg1)
{
// 如果黑白反转
// If black and white are reversed
if (arg1) {
// 设置玩家1和玩家2的标识图
ui.actionEngine1_T->setIcon(QIcon(":/icon/Resources/icon/White.png"));
ui.actionEngine2_R->setIcon(QIcon(":/icon/Resources/icon/Black.png"));
ui.picLabel1->setPixmap(QPixmap(":/icon/Resources/icon/White.png"));
ui.picLabel2->setPixmap(QPixmap(":/icon/Resources/icon/Black.png"));
} else {
// 设置玩家1和玩家2的标识图
ui.actionEngine1_T->setIcon(QIcon(":/icon/Resources/icon/Black.png"));
ui.actionEngine2_R->setIcon(QIcon(":/icon/Resources/icon/White.png"));
ui.picLabel1->setPixmap(QPixmap(":/icon/Resources/icon/Black.png"));
ui.picLabel2->setPixmap(QPixmap(":/icon/Resources/icon/White.png"));
}
// 让控制器改变棋子颜色
// Let the controller change the color of the pieces
game->setInvert(arg1);
}
// 前后招的公共槽
void MillGameWindow::on_actionRowChange()
{
QAbstractItemModel *model = ui.listView->model();
@ -789,7 +739,7 @@ void MillGameWindow::on_actionRowChange()
currentRow = ui.listView->currentIndex().row();
}
// 更新动作状态
// Update action status
if (rows <= 1) {
ui.actionBegin_S->setEnabled(false);
ui.actionPrevious_B->setEnabled(false);
@ -818,29 +768,8 @@ void MillGameWindow::on_actionRowChange()
}
}
// 更新局面
// Update phrase
game->phaseChange(currentRow);
#if 0
// 下面的代码全部取消改用QTimer的方式实现
// 更新局面
bool changed = state->phaseChange(currentRow);
// 处理自动播放时的动画
if (changed && state->isAnimation()) {
// 不使用processEvents函数进行非阻塞延时频繁调用占用CPU较多
//QElapsedTimer et;
//et.start();
//while (et.elapsed() < waitTime) {
// qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
//}
int waitTime = state->getDurationTime() + 50;
// 使用QEventLoop进行非阻塞延时CPU占用低
QEventLoop loop;
QTimer::singleShot(waitTime, &loop, SLOT(quit()));
loop.exec();
}
#endif // 0
}
void MillGameWindow::onAutoRunTimeOut(QPrivateSignal signal)
@ -854,7 +783,7 @@ void MillGameWindow::onAutoRunTimeOut(QPrivateSignal signal)
return;
}
// 执行“下一招”
// Do the "next move"
if (currentRow >= rows - 1) {
ui.actionAutoRun_A->setChecked(false);
return;
@ -866,7 +795,7 @@ void MillGameWindow::onAutoRunTimeOut(QPrivateSignal signal)
currentRow = ui.listView->currentIndex().row();
// 更新动作状态
// Update action status
if (currentRow <= 0) {
ui.actionBegin_S->setEnabled(false);
ui.actionPrevious_B->setEnabled(false);
@ -887,25 +816,20 @@ void MillGameWindow::onAutoRunTimeOut(QPrivateSignal signal)
ui.actionAutoRun_A->setEnabled(true);
}
// 更新局面
// Renew the situation
game->phaseChange(currentRow);
}
// 自动运行
void MillGameWindow::on_actionAutoRun_A_toggled(bool arg1)
{
if (arg1) {
// 自动运行前禁用控件
ui.dockWidget->setEnabled(false);
ui.gameView->setEnabled(false);
// 启动定时器
autoRunTimer.start(game->getDurationTime() * 10 + 50);
} else {
// 关闭定时器
autoRunTimer.stop();
// 自动运行结束后启用控件
ui.dockWidget->setEnabled(true);
ui.gameView->setEnabled(true);
}
@ -944,7 +868,6 @@ void MillGameWindow::on_actionEngineFight_E_triggered()
void MillGameWindow::on_actionEngine_E_triggered()
{
// 定义新对话框
auto *dialog = new QDialog(this);
dialog->setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint);
dialog->setObjectName(QStringLiteral("Dialog"));
@ -952,7 +875,6 @@ void MillGameWindow::on_actionEngine_E_triggered()
dialog->resize(256, 188);
dialog->setModal(true);
// 生成各个控件
auto *vLayout = new QVBoxLayout(dialog);
auto *groupBox1 = new QGroupBox(dialog);
auto *groupBox2 = new QGroupBox(dialog);
@ -967,7 +889,6 @@ void MillGameWindow::on_actionEngine_E_triggered()
auto *buttonBox = new QDialogButtonBox(dialog);
// 设置各个控件数据
groupBox1->setTitle(tr("Player1 AI Settings"));
label_time1->setText(tr("Time limit"));
spinBox_time1->setMinimum(1);
@ -983,7 +904,6 @@ void MillGameWindow::on_actionEngine_E_triggered()
buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
// 布局控件
vLayout->addWidget(groupBox1);
vLayout->addWidget(groupBox2);
vLayout->addWidget(buttonBox);
@ -994,17 +914,14 @@ void MillGameWindow::on_actionEngine_E_triggered()
hLayout2->addWidget(label_time2);
hLayout2->addWidget(spinBox_time2);
// 关联信号和槽函数
connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));
connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));
// 目前数据
int time1, time2;
game->getAiDepthTime(time1, time2);
spinBox_time1->setValue(time1);
spinBox_time2->setValue(time2);
// 新设数据
if (dialog->exec() == QDialog::Accepted) {
int time1_new, time2_new;
@ -1013,12 +930,10 @@ void MillGameWindow::on_actionEngine_E_triggered()
if (time1 != time1_new ||
time2 != time2_new) {
// 重置AI
game->setAiDepthTime(time1_new, time2_new);
}
}
// 删除对话框,子控件会一并删除
dialog->disconnect();
delete dialog;
}
@ -1042,7 +957,6 @@ void MillGameWindow::on_actionAbout_A_triggered()
dialog->setWindowTitle(tr("The Mill Game"));
dialog->setModal(true);
// 生成各个控件
auto *vLayout = new QVBoxLayout(dialog);
auto *hLayout = new QHBoxLayout;
//QLabel *label_icon1 = new QLabel(dialog);
@ -1053,15 +967,16 @@ void MillGameWindow::on_actionAbout_A_triggered()
auto *label_text = new QLabel(dialog);
auto *label_image = new QLabel(dialog);
// 设置各个控件数据
//label_icon1->setPixmap(QPixmap(QString::fromUtf8(":/image/resources/image/black_piece.png")));
//label_icon2->setPixmap(QPixmap(QString::fromUtf8(":/image/resources/image/white_piece.png")));
//label_icon1->setAlignment(Qt::AlignCenter);
//label_icon2->setAlignment(Qt::AlignCenter);
//label_icon1->setFixedSize(32, 32);
//label_icon2->setFixedSize(32, 32);
//label_icon1->setScaledContents(true);
//label_icon2->setScaledContents(true);
#if 0
label_icon1->setPixmap(QPixmap(QString::fromUtf8(":/image/resources/image/black_piece.png")));
label_icon2->setPixmap(QPixmap(QString::fromUtf8(":/image/resources/image/white_piece.png")));
label_icon1->setAlignment(Qt::AlignCenter);
label_icon2->setAlignment(Qt::AlignCenter);
label_icon1->setFixedSize(32, 32);
label_icon2->setFixedSize(32, 32);
label_icon1->setScaledContents(true);
label_icon2->setScaledContents(true);
#endif
//date_text->setText(__DATE__);
QString versionText;
@ -1075,7 +990,6 @@ void MillGameWindow::on_actionAbout_A_triggered()
version_text->setText(versionText);
version_text->setAlignment(Qt::AlignLeft);
// 布局
vLayout->addLayout(hLayout);
//hLayout->addWidget(label_icon1);
//hLayout->addWidget(label_icon2);
@ -1085,10 +999,8 @@ void MillGameWindow::on_actionAbout_A_triggered()
vLayout->addWidget(donate_text);
vLayout->addWidget(label_image);
// 运行对话框
dialog->exec();
// 删除对话框
dialog->disconnect();
delete dialog;
}

View File

@ -57,37 +57,35 @@ protected:
#endif /* MOBILE_APP_UI */
private slots:
// 初始化
void initialize();
#ifdef MOBILE_APP_UI
// 上下文菜单
void ctxMenu(const QPoint &pos);
#endif /* MOBILE_APP_UI */
// 动态增加的菜单栏动作的槽函数
void actionRules_triggered();
// 更新规则标签
void ruleInfo();
// 自动运行定时处理函数
void onAutoRunTimeOut(QPrivateSignal signal);
// 下面是各动作的槽函数
// 注释掉的是已在UI管理器或主窗口初始化函数中连接好的
// The slot function for each action
// Remove functions have been connected in UI manager or main window initialization function
void on_actionNew_N_triggered();
void on_actionOpen_O_triggered();
void on_actionSave_S_triggered();
void on_actionSaveAs_A_triggered();
//void on_actionExit_X_triggered();
#if 0
void on_actionExit_X_triggered();
#endif
void on_actionEdit_E_toggled(bool arg1);
//void on_actionFlip_F_triggered();
//void on_actionMirror_M_triggered();
//void on_actionTurnRight_R_triggered();
//void on_actionTurnLeftt_L_triggered();
#if 0
void on_actionFlip_F_triggered();
void on_actionMirror_M_triggered();
void on_actionTurnRight_R_triggered();
void on_actionTurnLeftt_L_triggered();
#endif
void on_actionInvert_I_toggled(bool arg1);
// 前后招的公共槽
void on_actionRowChange();
void on_actionAutoRun_A_toggled(bool arg1);
//void on_actionResign_G_triggered();
@ -96,14 +94,16 @@ private slots:
void on_actionEngineFight_E_triggered();
void on_actionInternet_I_triggered();
void on_actionEngine_E_triggered();
//void on_actionEngine1_R_toggled(bool arg1);
//void on_actionEngine2_T_toggled(bool arg1);
//void on_actionSetting_O_triggered();
//void on_actionToolBar_T_toggled(bool arg1);
//void on_actionDockBar_D_toggled(bool arg1);
//void on_actionSound_S_toggled(bool arg1);
//void on_actionAnimation_A_toggled(bool arg1);
//void on_actionAutoRestart_A_triggered();
#if 0
void on_actionEngine1_R_toggled(bool arg1);
void on_actionEngine2_T_toggled(bool arg1);
void on_actionSetting_O_triggered();
void on_actionToolBar_T_toggled(bool arg1);
void on_actionDockBar_D_toggled(bool arg1);
void on_actionSound_S_toggled(bool arg1);
void on_actionAnimation_A_toggled(bool arg1);
void on_actionAutoRestart_A_triggered();
#endif
void on_actionViewHelp_V_triggered();
void on_actionWeb_W_triggered();
void on_actionAbout_A_triggered();
@ -112,25 +112,12 @@ protected:
void saveBook(const QString &path);
private:
// 界面文件
Ui::MillGameWindowClass ui {};
// 视图场景
GameScene *scene {nullptr};
// 控制器
Game *game {nullptr};
// 动态增加的菜单栏动作列表
vector<QAction *> ruleActionList;
// 游戏的规则号,涉及菜单项和对话框,所以要有
int ruleNo {-1};
// 文件
QFile file;
// 定时器
QTimer autoRunTimer;
#ifdef MOBILE_APP_UI

View File

@ -16,22 +16,21 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// 定义绘图相关常量的头文件
#ifndef GRAPHICSCONST
#define GRAPHICSCONST
#include "config.h"
#ifdef MOBILE_APP_UI
constexpr short BOARD_SIZE = 500; // 棋盘大小
constexpr short BOARD_SIZE = 500;
#else
constexpr short BOARD_SIZE = 600; // 棋盘大小
constexpr short BOARD_SIZE = 600;
#endif /* MOBILE_APP_UI */
constexpr short BOARD_MINISIZE = 150; // 最小宽高即1/4大小
constexpr short PIECE_SIZE = 56; // 棋子大小
constexpr short LINE_INTERVAL = 72; // 线间距
constexpr short LINE_WEIGHT = 3; // 线宽
constexpr short BOARD_MINISIZE = 150; // Minimum width and height, i.e. 1 / 4 Size
constexpr short PIECE_SIZE = 56;
constexpr short LINE_INTERVAL = 72;
constexpr short LINE_WEIGHT = 3;
#endif // GRAPHICSCONST

View File

@ -16,16 +16,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* QListView派生类
* sizeHint函数
*
* QDockWidget没有很好的控制初始大小的方法resize函数没效果
* 使
* ui.listView->setFixedWidth(108);
*
*/
#ifndef MANUALLISTVIEW
#define MANUALLISTVIEW
@ -34,6 +24,16 @@
#include "config.h"
/*
* QlistView derived class
* The reason for deriving this class is to overload the sizeHint function
* Just to make the docking bar(parent window) not too wide and ugly at the beginning
* QDockWidget does not have a good way to control the initial size, and the reset function has no effect
* If you don't use derived classes, you can use a fixed width, as shown below
* ui.listView->setFixedWidth(108);
* But it doesn't look good after adjusting the width of the dock
*/
class ManualListView : public QListView
{
Q_OBJECT
@ -48,21 +48,22 @@ public:
{
QSize size = QListView::sizeHint();
// 缺省宽度设为128这样就不太宽了
// The default width is 128, so it's not too wide
size.setWidth(128);
return size;
}
signals:
// 需要一个currentChanged信号但默认没有需要把这个槽改造成信号
// A currentChanged signal is required, but not by default.
// This slot needs to be transformed into a signal
void currentChangedSignal(const QModelIndex &current, const QModelIndex &previous);
protected slots:
// 屏蔽掉双击编辑功能
// Block double-click editing feature
void mouseDoubleClickEvent(QMouseEvent *mouseEvent) override
{
//屏蔽双击事件
// Block double click events
mouseEvent->accept();
}
@ -74,30 +75,13 @@ protected slots:
newEmptyRow = true;
}
#if 0
/* 本来重载rowsInserted函数用于在插入新行后自动选中最后一行
Model的insertRow执行后rowsInserted会被立即执行
Model的setData还未被执行
*/
void rowsInserted(const QModelIndex &parent, int start, int end) {
// 调用父类函数为使滚动条更新否则scrollToBottom不能正确执行。
QListView::rowsInserted(parent, start, end);
QModelIndex color = model()->square(end, 0);
setCurrentIndex(color);
scrollToBottom();
}
#endif
// 采用判断最后一个元素是否改变来选中之
// Select by judging whether the last element has changed
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
const QVector<int> &roles = QVector<int>()) override
{
// 调用父类默认函数
QListView::dataChanged(topLeft, bottomRight, roles);
// 如果包含model
if (model()) {
// 判断
const QModelIndex square = model()->index(model()->rowCount() - 1, 0);
if (square == bottomRight && newEmptyRow) {
setCurrentIndex(square);
@ -107,8 +91,10 @@ protected slots:
}
}
// 需要一个currentChanged信号但默认没有需要把这个槽改造成信号
// activated信号需要按下回车才发出selectedChanged和clicked信号也不合适
// A currentChanged signal is required, but not by default.
// This slot needs to be transformed into a signal
// The activated signal needs to press enter to send out,
// and the selectedChanged and clicked signals are not appropriate
void currentChanged(const QModelIndex &current, const QModelIndex &previous) override
{
QListView::currentChanged(current, previous);
@ -116,7 +102,7 @@ protected slots:
}
private:
// 添加了新空行的标识
// The identity of the new blank line is added
bool newEmptyRow {false};
};

View File

@ -27,44 +27,33 @@ PieceItem::PieceItem(QGraphicsItem *parent) :
num(0)
{
Q_UNUSED(parent)
// 允许选择和移动
setFlags(ItemIsSelectable
// | ItemIsMovable
);
// 设置缓存模式
setCacheMode(DeviceCoordinateCache);
// 鼠标放在棋子上时显示为伸开的手形
setCursor(Qt::OpenHandCursor);
// 只接受左键事件
//setAcceptedMouseButtons(Qt::LeftButton);
// 不接受鼠标事件
setAcceptedMouseButtons(nullptr);
//setAcceptHoverEvents(true);
// 默认模型为没有棋子
model = Models::noPiece;
// 棋子尺寸
size = PIECE_SIZE;
// 选中子标识线宽度
selectLineWeight = LINE_WEIGHT;
// 删除线宽度
removeLineWeight = LINE_WEIGHT * 5;
// 选中线为黄色
#ifdef MOBILE_APP_UI
selectLineColor = Qt::gray;
#else
selectLineColor = Qt::darkYellow;
#endif /* MOBILE_APP_UI */
// 删除线颜色
removeLineColor = QColor(227, 23, 13);
removeLineColor.setAlphaF(0.9);
}
@ -90,11 +79,11 @@ void PieceItem::paint(QPainter *painter,
Q_UNUSED(option)
Q_UNUSED(widget)
// 空模型不画棋子
// Empty models don't draw pieces
switch (model) {
case Models::blackPiece:
// 如果模型为黑色,则画黑色棋子
// If the model is black, draw black pieces
#ifdef MOBILE_APP_UI
painter->setPen(Qt::NoPen);
painter->setBrush(QColor(0, 93, 172));
@ -106,7 +95,7 @@ void PieceItem::paint(QPainter *painter,
break;
case Models::whitePiece:
// 如果模型为白色,则画白色棋子
// If the model is white, draw white pieces
#ifdef MOBILE_APP_UI
painter->setPen(Qt::NoPen);
painter->setBrush(QColor(231, 36, 46));
@ -120,29 +109,25 @@ void PieceItem::paint(QPainter *painter,
break;
}
// 如果模型要求显示序号
// If the model requires the serial number to be displayed
if (showNum) {
// 如果模型为黑色,用白色笔画序号
if (model == Models::blackPiece)
painter->setPen(QColor(255, 255, 255));
// 如果模型为白色,用白色笔画序号
if (model == Models::whitePiece)
painter->setPen(QColor(0, 0, 0));
// 字体
QFont font;
font.setFamily("Arial");
font.setPointSize(size / 3);
painter->setFont(font);
// 画序号,默认中间位置偏下,需微调
painter->drawText(boundingRect().adjusted(0, 0, 0, -size / 12),
Qt::AlignCenter, QString::number(num));
}
// 如果模型为选中状态,则画上四个小直角
// If the model is selected, draw four small right angles
if (isSelected()) {
QPen pen(selectLineColor, selectLineWeight, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin);
painter->setPen(pen);
@ -158,7 +143,7 @@ void PieceItem::paint(QPainter *painter,
painter->drawLine(-xy, xy, -xy / 2, xy);
}
// 如果模型为删除状态,则画上叉号
// If the model is deleted, cross it
if (deleted) {
QPen pen(removeLineColor, removeLineWeight, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin);
painter->setPen(pen);
@ -170,7 +155,7 @@ void PieceItem::paint(QPainter *painter,
void PieceItem::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
// 鼠标按下时变为握住的手形
// When the mouse is pressed, it becomes the shape of the hand it holds
setCursor(Qt::ClosedHandCursor);
QGraphicsItem::mousePressEvent(mouseEvent);
}
@ -182,7 +167,7 @@ void PieceItem::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
void PieceItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
// 鼠标松开时变为伸开的手形
// When the mouse is released, it becomes an extended hand
setCursor(Qt::OpenHandCursor);
QGraphicsItem::mouseReleaseEvent(mouseEvent);
}

View File

@ -28,8 +28,6 @@ class PieceItem : public QObject, public QGraphicsItem
{
Q_OBJECT
Q_INTERFACES(QGraphicsItem)
// 位置属性
Q_PROPERTY(QPointF pos READ pos WRITE setPos)
public:
@ -44,8 +42,10 @@ public:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget = nullptr) override;
// 用UserType+2表示棋子用qgraphicsitem_cast()判断是否为PieceItem类的对象
// 还有一个方式是把类名放在Data的0key位置setData(0, "PieceItem")然后用data(0)来判断
// Use UserType + 2 to represent pieces,
// and use qgraphicsitems_cast() determines whether it is an object of the pieceitem class
// Another way is to put the class name in the 0key position of data,
// setData(0, "pieceitem"), and then use data(0) to judge
enum
{
Type = UserType + 2
@ -56,12 +56,11 @@ public:
return Type;
}
// 模型状态枚举,用位运算标明
enum class Models
{
noPiece = 0x1, // 空棋子
blackPiece = 0x2, // 黑色棋子
whitePiece = 0x4, // 白色棋子
noPiece = 0x1,
blackPiece = 0x2,
whitePiece = 0x4,
};
enum Models getModel() noexcept
@ -110,31 +109,24 @@ protected:
void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
private:
// 棋子本质
enum Models model;
// 棋子序号黑白都从1开始
// Piece number, black and white all start from 1
int num {1};
// 棋子尺寸
int size {0};
// 有无删除线
// Is there a delete line
bool deleted {false};
// 显示序号
bool showNum {false};
// 选中子标识线宽度
int selectLineWeight {0};
// 删除线宽度
int removeLineWeight {0};
// 选中线颜色
QColor selectLineColor;
// 删除线颜色
QColor removeLineColor;
};