From 2eb0872597241a952f31c5d691ec4979f6d2d0fd Mon Sep 17 00:00:00 2001 From: CalciteM Team Date: Sat, 14 Sep 2019 03:06:48 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=B0=86=20=5F1=20=E5=92=8C=20=5F2?= =?UTF-8?q?=20=E5=BD=A2=E5=BC=8F=E6=94=B9=E4=B8=BA=E6=95=B0=E7=BB=84?= =?UTF-8?q?=E5=BD=A2=E5=BC=8F=20[1]=20=E5=92=8C=20[2]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- millgame.pro | 2 + src/ai/evaluate.cpp | 29 +-- src/ai/movegen.cpp | 19 +- src/ai/search.cpp | 38 ++-- src/ai/search.h | 1 - src/base/thread.cpp | 3 +- src/base/thread.h | 5 +- src/game/board.cpp | 61 ++----- src/game/board.h | 11 +- src/game/player.h | 93 ++++++---- src/game/position.cpp | 292 ++++++++++++------------------- src/game/position.h | 38 ++-- src/ui/qt/gamecontroller.cpp | 329 +++++++++++++++++------------------ src/ui/qt/gamecontroller.h | 25 +-- 14 files changed, 413 insertions(+), 533 deletions(-) diff --git a/millgame.pro b/millgame.pro index 64ad33ac..feb2c609 100644 --- a/millgame.pro +++ b/millgame.pro @@ -27,6 +27,7 @@ SOURCES += \ src/ai/tt.cpp \ src/base/misc.cpp \ src/game/board.cpp \ + src/game/player.cpp \ src/game/position.cpp \ src/game/rule.cpp \ src/main.cpp \ @@ -59,6 +60,7 @@ HEADERS += \ src/ai/search.h \ src/ai/zobrist.h \ src/game/board.h \ + src/game/player.h \ src/game/position.h \ src/game/rule.h \ src/game/types.h \ diff --git a/src/ai/evaluate.cpp b/src/ai/evaluate.cpp index 1bb70423..dca80c98 100644 --- a/src/ai/evaluate.cpp +++ b/src/ai/evaluate.cpp @@ -87,12 +87,12 @@ value_t Evaluation::getValue(Position &dummyPosition, PositionContext *positionC #endif /* EVALUATE_MOBILITY */ switch (positionContext->action) { - // 选子和落子使用相同的评价方法 + // 选子和落子使用相同的评价方法 case ACTION_CHOOSE: case ACTION_PLACE: break; - // 如果形成去子状态,每有一个可去的子,算128分 + // 如果形成去子状态,每有一个可去的子,算128分 case ACTION_CAPTURE: nPiecesNeedRemove = (positionContext->turn == PLAYER_1) ? positionContext->nPiecesNeedRemove : -(positionContext->nPiecesNeedRemove); @@ -107,17 +107,13 @@ value_t Evaluation::getValue(Position &dummyPosition, PositionContext *positionC break; - // 终局评价最简单 + // 终局评价最简单 case PHASE_GAMEOVER: // 布局阶段闷棋判断 if (positionContext->nPiecesOnBoard[1] + positionContext->nPiecesOnBoard[2] >= Board::N_SEATS * Board::N_RINGS) { if (dummyPosition.getRule()->isStartingPlayerLoseWhenBoardFull) { - // winner = PLAYER_2; value -= VALUE_WIN; -#ifdef DEBUG_AB_TREE - node->result = -3; -#endif } else { value = VALUE_DRAW; } @@ -128,30 +124,15 @@ value_t Evaluation::getValue(Position &dummyPosition, PositionContext *positionC dummyPosition.context.board.isAllSurrounded(positionContext->turn, dummyPosition.currentRule, positionContext->nPiecesOnBoard, positionContext->turn) && dummyPosition.getRule()->isLoseWhenNoWay) { // 规则要求被“闷”判负,则对手获胜 - if (positionContext->turn == PLAYER_1) { - value -= VALUE_WIN; -#ifdef DEBUG_AB_TREE - node->result = -2; -#endif - } else { - value += VALUE_WIN; -#ifdef DEBUG_AB_TREE - node->result = 2; -#endif - } + value_t delta = positionContext->turn == PLAYER_1 ? -VALUE_WIN : VALUE_WIN; + value += delta; } // 剩余棋子个数判断 if (positionContext->nPiecesOnBoard[1] < dummyPosition.getRule()->nPiecesAtLeast) { value -= VALUE_WIN; -#ifdef DEBUG_AB_TREE - node->result = -1; -#endif } else if (positionContext->nPiecesOnBoard[2] < dummyPosition.getRule()->nPiecesAtLeast) { value += VALUE_WIN; -#ifdef DEBUG_AB_TREE - node->result = 1; -#endif } break; diff --git a/src/ai/movegen.cpp b/src/ai/movegen.cpp index 3d6cdc66..d00dd078 100644 --- a/src/ai/movegen.cpp +++ b/src/ai/movegen.cpp @@ -22,6 +22,7 @@ #include #include "movegen.h" +#include "player.h" #include "misc.h" void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, Position &dummyPosition, @@ -36,20 +37,14 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, Position &dummyPosition, switch (dummyPosition.getPhase()) { case PHASE_PLACING: if (dummyPosition.getAction() == ACTION_CAPTURE) { - if (dummyPosition.whosTurn() == PLAYER_1) - newCapacity = static_cast(dummyPosition.getPiecesOnBoardCount(2)); - else - newCapacity = static_cast(dummyPosition.getPiecesOnBoardCount(1)); + newCapacity = static_cast(dummyPosition.getPiecesOnBoardCount(dummyPosition.context.opponentId)); } else { newCapacity = static_cast(dummyPosition.getPiecesInHandCount(1) + dummyPosition.getPiecesInHandCount(2)); } break; case PHASE_MOVING: if (dummyPosition.getAction() == ACTION_CAPTURE) { - if (dummyPosition.whosTurn() == PLAYER_1) - newCapacity = static_cast(dummyPosition.getPiecesOnBoardCount(2)); - else - newCapacity = static_cast(dummyPosition.getPiecesOnBoardCount(1)); + newCapacity = static_cast(dummyPosition.getPiecesOnBoardCount(dummyPosition.context.opponentId)); } else { newCapacity = 6; } @@ -70,7 +65,7 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, Position &dummyPosition, } // 对手 - player_t opponent = Position::getOpponent(dummyPosition.context.turn); + player_t opponent = Player::getOpponent(dummyPosition.context.turn); // 列出所有合法的下一招 switch (dummyPosition.context.action) { @@ -110,10 +105,8 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, Position &dummyPosition, continue; } - if ((dummyPosition.context.turn == PLAYER_1 && - (dummyPosition.context.nPiecesOnBoard[1] > dummyPosition.currentRule.nPiecesAtLeast || !dummyPosition.currentRule.allowFlyWhenRemainThreePieces)) || - (dummyPosition.context.turn == PLAYER_2 && - (dummyPosition.context.nPiecesOnBoard[2] > dummyPosition.currentRule.nPiecesAtLeast || !dummyPosition.currentRule.allowFlyWhenRemainThreePieces))) { + if (dummyPosition.context.nPiecesOnBoard[dummyPosition.context.turnId] > dummyPosition.currentRule.nPiecesAtLeast || + !dummyPosition.currentRule.allowFlyWhenRemainThreePieces) { // 对于棋盘上还有3个子以上,或不允许飞子的情况,要求必须在着法表中 for (int direction = DIRECTION_CLOCKWISE; direction <= DIRECTION_OUTWARD; direction++) { // 对于原有位置,遍历四个方向的着法,如果棋盘上为空位就加到结点列表中 diff --git a/src/ai/search.cpp b/src/ai/search.cpp index b3efc547..9be152e0 100644 --- a/src/ai/search.cpp +++ b/src/ai/search.cpp @@ -149,7 +149,6 @@ struct MillGameAi_ab::Node *MillGameAi_ab::addNode( newNode->nPiecesNeedRemove = INT_MAX; newNode->alpha = -VALUE_INFINITE; newNode->beta = VALUE_INFINITE; - newNode->result = 0; newNode->visited = false; int r, s; @@ -233,11 +232,9 @@ void MillGameAi_ab::sortLegalMoves(Node *node) { // 这个函数对效率的影响很大,排序好的话,剪枝较早,节省时间,但不能在此函数耗费太多时间 - if (dummyPosition.whosTurn() == PLAYER_1) { - std::stable_sort(node->children.begin(), node->children.end(), nodeGreater); - } else { - std::stable_sort(node->children.begin(), node->children.end(), nodeLess); - } + auto cmp = dummyPosition.context.turn == PLAYER_1 ? nodeGreater : nodeLess; + + std::stable_sort(node->children.begin(), node->children.end(), cmp); } void MillGameAi_ab::deleteTree(Node *node) @@ -490,11 +487,8 @@ value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t alpha, value_t be evaluatedNodeCount++; // 为争取速胜,value 值 +- 深度 (有必要?) - if (positionContext->turn == PLAYER_1) { - node->value += depth; - } else { - node->value -= depth; - } + value_t delta = value_t(positionContext->turn == PLAYER_1 ? depth : -depth); + node->value += delta; #ifdef DEBUG_AB_TREE if (requiredQuit) { @@ -505,8 +499,8 @@ value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t alpha, value_t be #ifdef BOOK_LEARNING // 检索开局库 if (positionContext->phase == GAME_PLACING && findBookHash(hash, hashValue)) { - if (positionContext->turn == PLAYER_2) { - // 是否需对后手扣分 // TODO: 先后手都处理 + if (positionContext->turn == ???) { + // TODO: node->value += 1; } } @@ -525,7 +519,7 @@ value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t alpha, value_t be // 根据演算模型执行 MiniMax 检索,对先手,搜索 Max, 对后手,搜索 Min - minMax = dummyPosition.whosTurn() == PLAYER_1 ? -VALUE_INFINITE : VALUE_INFINITE; + minMax = dummyPosition.context.turn == PLAYER_1 ? -VALUE_INFINITE : VALUE_INFINITE; for (auto child : node->children) { // 上下文入栈保存,以便后续撤销着法 @@ -556,7 +550,7 @@ value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t alpha, value_t be dummyPosition.context = contextStack.top(); contextStack.pop(); - if (dummyPosition.whosTurn() == PLAYER_1) { + if (dummyPosition.context.turn == PLAYER_1) { // 为走棋一方的层, 局面对走棋的一方来说是以 α 为评价 // 取最大值 @@ -689,12 +683,11 @@ const char* MillGameAi_ab::bestMove() if (position_.getGiveUpIfMostLose() == true) { bool isMostLose = true; // 是否必败 - player_t whosTurn = position_.whosTurn(); + player_t whosTurn = position_.context.turn; for (auto child : rootNode->children) { - // TODO: 使用常量代替 - if ((whosTurn == PLAYER_1 && child->value > -10000) || - (whosTurn == PLAYER_2 && child->value < 10000)) { + if ((whosTurn == PLAYER_1 && child->value > -VALUE_WIN) || + (whosTurn == PLAYER_2 && child->value < VALUE_WIN)) { isMostLose = false; break; } @@ -702,12 +695,7 @@ const char* MillGameAi_ab::bestMove() // 自动认输 if (isMostLose) { - if (whosTurn == PLAYER_1) { - sprintf(cmdline, "Player1 give up!"); - } else if (whosTurn == PLAYER_2) { - sprintf(cmdline, "Player2 give up!"); - } - + sprintf(cmdline, "Player%d give up!", position_.context.turnChar); return cmdline; } } diff --git a/src/ai/search.h b/src/ai/search.h index 7c0939bb..4a18934a 100644 --- a/src/ai/search.h +++ b/src/ai/search.h @@ -80,7 +80,6 @@ public: int nPiecesOnBoardDiff; // 场上棋子个数和对手的差值 int nPiecesInHandDiff; // 手中的棋子个数和对手的差值 int nPiecesNeedRemove; // 手中有多少可去的子,如对手有可去的子则为负数 - int result; // 终局结果,-1为负,0为未到终局,1为胜,走棋阶段被闷棋则为 -2/2,布局阶段闷棋为 -3 struct Node* root; // 根节点 #ifdef TRANSPOSITION_TABLE_ENABLE bool isHash; // 是否从 Hash 读取 diff --git a/src/base/thread.cpp b/src/base/thread.cpp index e6ab8b43..356b3430 100644 --- a/src/base/thread.cpp +++ b/src/base/thread.cpp @@ -22,6 +22,7 @@ #include #include "thread.h" #include "tt.h" +#include "player.h" AiThread::AiThread(int id, QObject *parent) : QThread(parent), @@ -107,7 +108,7 @@ void AiThread::run() while (!isInterruptionRequested()) { mutex.lock(); - i = Position::playerToId(position_->whosTurn()); + i = Player::toId(position_->context.turn); if (i != id || waiting_) { pauseCondition.wait(&mutex); diff --git a/src/base/thread.h b/src/base/thread.h index 054dd042..d36216b8 100644 --- a/src/base/thread.h +++ b/src/base/thread.h @@ -36,6 +36,7 @@ class AiThread : public QThread Q_OBJECT public: + explicit AiThread(QObject *parent = nullptr); explicit AiThread(int id, QObject *parent = nullptr); ~AiThread() override; @@ -90,10 +91,12 @@ public slots: // 发射着法信号 void emitCommand(); -private: +public: // 玩家ID int id; +private: + // 发射的指令 const char* strCommand {}; diff --git a/src/game/board.cpp b/src/game/board.cpp index c95eda9b..d1fedda2 100644 --- a/src/game/board.cpp +++ b/src/game/board.cpp @@ -302,10 +302,10 @@ int Board::addMills(const Rule ¤tRule, int location) return n; } -bool Board::isAllInMills(char ch) +bool Board::isAllInMills(enum player_t player) { for (int i = LOCATION_BEGIN; i < LOCATION_END; i++) { - if (locations[i] & ch) { + if (locations[i] & (uint8_t)player) { if (!inHowManyMills(i)) { return false; } @@ -315,29 +315,14 @@ bool Board::isAllInMills(char ch) return true; } -bool Board::isAllInMills(enum player_t player) -{ - char ch = 0x00; - - if (player == PLAYER_1) - ch = 0x10; - else if (player == PLAYER_2) - ch = 0x20; - else - return true; - - return isAllInMills(ch); -} - // 判断玩家的棋子周围有几个空位 -int Board::getSurroundedEmptyLocationCount(enum player_t turn, const Rule ¤tRule, int nPiecesOnBoard[], int location, bool includeFobidden) +int Board::getSurroundedEmptyLocationCount(int turnId, const Rule ¤tRule, int nPiecesOnBoard[], + int location, bool includeFobidden) { int count = 0; - if ((turn == PLAYER_1 && - (nPiecesOnBoard[1] > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces)) || - (turn == PLAYER_2 && - (nPiecesOnBoard[2] > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces))) { + if (nPiecesOnBoard[turnId] > currentRule.nPiecesAtLeast || + !currentRule.allowFlyWhenRemainThreePieces) { int moveLocation; for (direction_t d = DIRECTION_BEGIN; d < DIRECTIONS_COUNT; d = (direction_t)(d + 1)) { moveLocation = MoveList::moveTable[location][d]; @@ -354,13 +339,11 @@ int Board::getSurroundedEmptyLocationCount(enum player_t turn, const Rule &curre } // 判断玩家的棋子是否被围 -bool Board::isSurrounded(enum player_t turn, const Rule ¤tRule, int nPiecesOnBoard[], int location) +bool Board::isSurrounded(int turnId, const Rule ¤tRule, int nPiecesOnBoard[], int location) { // 判断location处的棋子是否被“闷” - if ((turn == PLAYER_1 && - (nPiecesOnBoard[1] > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces)) || - (turn == PLAYER_2 && - (nPiecesOnBoard[2] > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces))) { + if (nPiecesOnBoard[turnId] > currentRule.nPiecesAtLeast || + !currentRule.allowFlyWhenRemainThreePieces) { int i, moveLocation; for (i = 0; i < 4; i++) { moveLocation = MoveList::moveTable[location][i]; @@ -375,17 +358,15 @@ bool Board::isSurrounded(enum player_t turn, const Rule ¤tRule, int nPiece return false; } -bool Board::isAllSurrounded(enum player_t turn, const Rule ¤tRule, int nPiecesOnBoard[], char ch) +bool Board::isAllSurrounded(int turnId, const Rule ¤tRule, int nPiecesOnBoard[], char ch) { // 如果摆满 if (nPiecesOnBoard[1] + nPiecesOnBoard[2] >= N_SEATS * N_RINGS) return true; // 判断是否可以飞子 - if ((turn == PLAYER_1 && - (nPiecesOnBoard[1] <= currentRule.nPiecesAtLeast && currentRule.allowFlyWhenRemainThreePieces)) || - (turn == PLAYER_2 && - (nPiecesOnBoard[2] <= currentRule.nPiecesAtLeast && currentRule.allowFlyWhenRemainThreePieces))) { + if (nPiecesOnBoard[turnId] <= currentRule.nPiecesAtLeast && + currentRule.allowFlyWhenRemainThreePieces) { return false; } @@ -407,32 +388,27 @@ bool Board::isAllSurrounded(enum player_t turn, const Rule ¤tRule, int nPi } // 判断玩家的棋子是否全部被围 -bool Board::isAllSurrounded(enum player_t turn, const Rule ¤tRule, int nPiecesOnBoard[], enum player_t ply) +bool Board::isAllSurrounded(int turnId, const Rule ¤tRule, int nPiecesOnBoard[], enum player_t ply) { - char t = '\x30'; + char t = 0x30 & ply; - if (ply == PLAYER_1) - t &= '\x10'; - else if (ply == PLAYER_2) - t &= '\x20'; - - return isAllSurrounded(turn, currentRule, nPiecesOnBoard, t); + return isAllSurrounded(turnId, currentRule, nPiecesOnBoard, t); } +#if 0 enum player_t Board::getWhosPiece(int r, int s) { int location = polarToLocation(r, s); - if (locations[location] & '\x10') + if (locations[location] & PLAYER_1) return PLAYER_1; - if (locations[location] & '\x20') + if (locations[location] & PLAYER_2) return PLAYER_2; return PLAYER_NOBODY; } -// Unused bool Board::getPieceRS(const player_t &player, const int &number, int &r, int &s, struct Rule ¤tRule) { int piece; @@ -480,6 +456,7 @@ bool Board::getCurrentPiece(player_t &player, int &number, int location) return true; } +#endif bool Board::isStarLocation(int location) { diff --git a/src/game/board.h b/src/game/board.h index c6cba117..9380763e 100644 --- a/src/game/board.h +++ b/src/game/board.h @@ -78,27 +78,28 @@ public: int inHowManyMills(int location); // 判断玩家的所有棋子是否都处于“三连”状态 - bool isAllInMills(char ch); bool isAllInMills(enum player_t); // 判断玩家的棋子周围有几个空位 - int getSurroundedEmptyLocationCount(enum player_t turn, const Rule ¤tRule, int nPiecesOnBoard[], int location, bool includeFobidden); + int getSurroundedEmptyLocationCount(int turnId, const Rule ¤tRule, int nPiecesOnBoard[], int location, bool includeFobidden); // 判断玩家的棋子是否被围 - bool isSurrounded(enum player_t turn, const Rule ¤tRule, int nPiecesOnBoard[], int location); + bool isSurrounded(int turnId, const Rule ¤tRule, int nPiecesOnBoard[], int location); // 判断玩家的棋子是否全部被围 - bool isAllSurrounded(enum player_t turn, const Rule ¤tRule, int nPiecesOnBoard[], char ch); + bool isAllSurrounded(int turnId, const Rule ¤tRule, int nPiecesOnBoard[], char ch); - bool isAllSurrounded(enum player_t turn, const Rule ¤tRule, int nPiecesOnBoard[], enum player_t ply); + bool isAllSurrounded(int turnId, const Rule ¤tRule, int nPiecesOnBoard[], enum player_t ply); // 三连加入列表 int addMills(const Rule ¤tRule, int location); +#if 0 // 获取位置点棋子的归属人 enum player_t getWhosPiece(int r, int s); bool getPieceRS(const player_t &player, const int &number, int &r, int &s, struct Rule ¤tRule); +#endif // 获取当前棋子 bool getCurrentPiece(player_t &player, int &number, int currentPos); diff --git a/src/game/player.h b/src/game/player.h index b8054241..ea925c94 100644 --- a/src/game/player.h +++ b/src/game/player.h @@ -20,35 +20,66 @@ *****************************************************************************/ #ifndef PLAYER_H -#define PLAYER_H - -#include "config.h" -#include "types.h" +#define PLAYER_H -class Player -{ -public: - explicit Player(); - virtual ~Player(); - - const player_t getPlayer() const - { - return who; - } - - int getId() const - { - return id; - } - - inline static int toId(player_t who) - { - return int((int)who >> PLAYER_SHIFT); - } - -private: - player_t who; - int id; -}; - -#endif // PLAYER_H +#include + +#include "config.h" +#include "types.h" + +class Player +{ +public: + explicit Player(); + virtual ~Player(); + + player_t getPlayer() const + { + return who; + } + + int getId() const + { + return id; + } + + inline static int toId(player_t who) + { + return int(static_cast(who) >> PLAYER_SHIFT); + } + + inline static player_t idToPlayer(int id) + { + return player_t(id << PLAYER_SHIFT); + } + + inline static char idToCh(int id) + { + return static_cast('0' + id); + } + + inline static std::string chToStr(char ch) + { + if (ch == '1') { + return "1"; + } else { + return "2"; + } + } + + inline static player_t getOpponent(player_t player) + { + return player == PLAYER_1 ? PLAYER_2 : PLAYER_1; + } + + inline static int getOpponentById(int id) + { + return id == 1 ? 2 : 1; + } + +private: + player_t who; + int id; +}; + +#endif // PLAYER_H diff --git a/src/game/position.cpp b/src/game/position.cpp index cf9eddb3..3bebbe1c 100644 --- a/src/game/position.cpp +++ b/src/game/position.cpp @@ -23,6 +23,7 @@ #include "position.h" #include "search.h" #include "movegen.h" +#include "player.h" Position::Position() { @@ -41,7 +42,7 @@ Position::Position() setContext(&RULES[1]); // 比分归零 - score_1 = score_2 = score_draw = 0; + score[1] = score[2] = score_draw = 0; } Position::~Position() = default; @@ -67,8 +68,8 @@ Position &Position::operator= (const Position &position) winner = position.winner; startTime = position.startTime; currentTime = position.currentTime; - elapsedSeconds_1 = position.elapsedSeconds_1; - elapsedSeconds_2 = position.elapsedSeconds_2; + elapsedSeconds[1] = position.elapsedSeconds[1]; + elapsedSeconds[2] = position.elapsedSeconds[2]; move_ = position.move_; memcpy(cmdline, position.cmdline, sizeof(cmdline)); cmdlist = position.cmdlist; @@ -77,31 +78,6 @@ Position &Position::operator= (const Position &position) return *this; } -int Position::playerToId(enum player_t player) -{ - if (player == PLAYER_1) - return 1; - else if (player == PLAYER_2) - return 2; - - return 0; -} - -player_t Position::getOpponent(enum player_t player) -{ - switch (player) - { - case PLAYER_1: - return PLAYER_2; - case PLAYER_2: - return PLAYER_1; - default: - break; - } - - return PLAYER_NOBODY; -} - // 设置配置 bool Position::configure(bool giveUpIfMostLose, bool randomMove) { @@ -141,7 +117,7 @@ bool Position::setContext(const struct Rule *rule, step_t maxStepsLedToDraw, int context.phase = phase; // 轮流状态标识 - context.turn = turn; + setTurn(turn); // 动作状态标识 context.action = action; @@ -221,7 +197,7 @@ bool Position::setContext(const struct Rule *rule, step_t maxStepsLedToDraw, int currentLocation = 0; // 用时置零 - elapsedSeconds_1 = elapsedSeconds_2 = 0; + elapsedSeconds[1] = elapsedSeconds[2] = 0; // 提示 setTips(); @@ -244,25 +220,10 @@ bool Position::setContext(const struct Rule *rule, step_t maxStepsLedToDraw, int return false; } -void Position::getContext(struct Rule &rule, step_t &step, - phase_t &phase, player_t &turn, action_t &action, - int *&locations, int &nPiecesInHand_1, int &nPiecesInHand_2, int &nPiecesNeedRemove) -{ - rule = this->currentRule; - step = this->currentStep; - phase = context.phase; - turn = context.turn; - action = context.action; - boardLocations = locations; - nPiecesInHand_1 = context.nPiecesInHand[1]; - nPiecesInHand_2 = context.nPiecesInHand[2]; - nPiecesNeedRemove = context.nPiecesNeedRemove; -} - bool Position::reset() { if (context.phase == PHASE_NOTSTARTED && - elapsedSeconds_1 == elapsedSeconds_2 == 0) { + elapsedSeconds[1] == elapsedSeconds[2] == 0) { return true; } @@ -273,8 +234,8 @@ bool Position::reset() // 局面阶段标识 context.phase = PHASE_NOTSTARTED; - // 轮流状态标识 - context.turn = PLAYER_1; + // 设置轮流状态 + setTurn(PLAYER_1); // 动作状态标识 context.action = ACTION_PLACE; @@ -303,7 +264,7 @@ bool Position::reset() currentLocation = 0; // 用时置零 - elapsedSeconds_1 = elapsedSeconds_2 = 0; + elapsedSeconds[1] = elapsedSeconds[2] = 0; // 哈希归零 context.hash = 0; @@ -386,18 +347,10 @@ bool Position::place(int location, int time_p, int8_t rs) int n = 0; if (context.phase == PHASE_PLACING) { - // 先手下 - if (context.turn == PLAYER_1) { - piece = '\x11' + currentRule.nTotalPiecesEachSide - context.nPiecesInHand[1]; - context.nPiecesInHand[1]--; - context.nPiecesOnBoard[1]++; - } - // 后手下 - else { - piece = '\x21' + currentRule.nTotalPiecesEachSide - context.nPiecesInHand[2]; - context.nPiecesInHand[2]--; - context.nPiecesOnBoard[2]++; - } + int playerId = Player::toId(context.turn); + piece = (0x01 | context.turn) + currentRule.nTotalPiecesEachSide - context.nPiecesInHand[playerId]; + context.nPiecesInHand[playerId]--; + context.nPiecesOnBoard[playerId]++; boardLocations[location] = piece; @@ -432,9 +385,9 @@ bool Position::place(int location, int time_p, int8_t rs) // 设置轮到谁走 if (currentRule.isDefenderMoveFirst) { - context.turn = PLAYER_2; + setTurn(PLAYER_2); } else { - context.turn = PLAYER_1; + setTurn(PLAYER_1); } // 再决胜负 @@ -468,11 +421,8 @@ bool Position::place(int location, int time_p, int8_t rs) // 对于中局落子 (ontext.phase == GAME_MOVING) // 如果落子不合法 - if ((context.turn == PLAYER_1 && - (context.nPiecesOnBoard[1] > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces)) || - (context.turn == PLAYER_2 && - (context.nPiecesOnBoard[2] > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces))) { - + if (context.nPiecesOnBoard[context.turnId] > currentRule.nPiecesAtLeast || + !currentRule.allowFlyWhenRemainThreePieces) { int i; for (i = 0; i < 4; i++) { if (location == MoveList::moveTable[currentLocation][i]) @@ -576,8 +526,7 @@ bool Position::capture(int location, int time_p, int8_t cp) // 时间的临时变量 int player_ms = -1; - // 对手 - char opponent = context.turn == PLAYER_1 ? 0x20 : 0x10; + player_t opponent = Player::getOpponent(context.turn); // 判断去子是不是对手棋 if (!(opponent & boardLocations[location])) @@ -600,10 +549,7 @@ bool Position::capture(int location, int time_p, int8_t cp) boardLocations[location] = '\x00'; } - if (context.turn == PLAYER_1) - context.nPiecesOnBoard[2]--; - else if (context.turn == PLAYER_2) - context.nPiecesOnBoard[1]--; + context.nPiecesOnBoard[context.opponentId]--; move_ = static_cast(-location); @@ -650,9 +596,9 @@ bool Position::capture(int location, int time_p, int8_t cp) // 设置轮到谁走 if (currentRule.isDefenderMoveFirst) { - context.turn = PLAYER_2; + setTurn(PLAYER_2); } else { - context.turn = PLAYER_1; + setTurn(PLAYER_1); } // 再决胜负 @@ -706,12 +652,10 @@ bool Position::choose(int location) if (context.action != ACTION_CHOOSE && context.action != ACTION_PLACE) return false; - char t = context.turn == PLAYER_1 ? 0x10 : 0x20; - // 判断选子是否可选 - if (boardLocations[location] & t) { + if (boardLocations[location] & context.turn) { // 判断location处的棋子是否被“闷” - if (context.board.isSurrounded(context.turn, currentRule, context.nPiecesOnBoard, location)) { + if (context.board.isSurrounded(context.turnId, currentRule, context.nPiecesOnBoard, location)) { return false; } @@ -742,17 +686,14 @@ bool Position::giveup(player_t loser) context.phase = PHASE_GAMEOVER; - if (loser == PLAYER_1) { - winner = PLAYER_2; - tips = "玩家1投子认负。"; - sprintf(cmdline, "Player1 give up!"); - score_2++; - } else if (loser == PLAYER_2) { - winner = PLAYER_1; - tips = "玩家2投子认负。"; - sprintf(cmdline, "Player2 give up!"); - score_1++; - } + int loserId = Player::toId(loser); + char loserCh = Player::idToCh(loserId); + string loserStr = Player::chToStr(loserCh); + + winner = Player::getOpponent(loser); + tips = "玩家" + loserStr + "投子认负"; + sprintf(cmdline, "Player%d give up!", loserId); + score[Player::toId(winner)]++; cmdlist.emplace_back(string(cmdline)); @@ -818,12 +759,7 @@ bool Position::command(const char *cmd) args = sscanf(cmd, "Player%1u give up!", &t); if (args == 1) { - if (t == 1) { - return giveup(PLAYER_1); - } - if (t == 2) { - return giveup(PLAYER_2); - } + return giveup(Player::idToPlayer(t)); } #ifdef THREEFOLD_REPETITION @@ -865,8 +801,8 @@ bool Position::command(int move) inline int Position::update(int time_p /*= -1*/) { int ret = -1; - time_t *player_ms = (context.turn == PLAYER_1 ? &elapsedSeconds_1 : &elapsedSeconds_2); - time_t playerNext_ms = (context.turn == PLAYER_1 ? elapsedSeconds_2 : elapsedSeconds_1); + time_t *player_ms = &elapsedSeconds[context.turnId]; + time_t playerNext_ms = elapsedSeconds[context.opponentId]; // 根据局面调整计时器 @@ -879,7 +815,7 @@ inline int Position::update(int time_p /*= -1*/) // 更新时间 if (time_p >= *player_ms) { *player_ms = ret = time_p; - startTime = currentTime - (elapsedSeconds_1 + elapsedSeconds_2); + startTime = currentTime - (elapsedSeconds[1] + elapsedSeconds[2]); } else { *player_ms = ret = currentTime - startTime - playerNext_ms; } @@ -914,22 +850,18 @@ bool Position::win(bool forceDraw) context.phase = PHASE_GAMEOVER; // 这里不能update更新时间,否则会形成循环嵌套 - // 如果玩家1超时 - if (elapsedSeconds_1 > currentRule.maxTimeLedToLose * 60) { - elapsedSeconds_1 = currentRule.maxTimeLedToLose * 60; - winner = PLAYER_2; - tips = "玩家1超时判负。"; - sprintf(cmdline, "Time over. Player2 win!"); - } - // 如果玩家2超时 - else if (elapsedSeconds_2 > currentRule.maxTimeLedToLose * 60) { - elapsedSeconds_2 = currentRule.maxTimeLedToLose * 60; - winner = PLAYER_1; - tips = "玩家2超时判负。"; - sprintf(cmdline, "Time over. Player1 win!"); + for (int i = 1; i <= 2; i++) + { + if (elapsedSeconds[i] > currentRule.maxTimeLedToLose * 60) { + elapsedSeconds[i] = currentRule.maxTimeLedToLose * 60; + winner = Player::idToPlayer(Player::getOpponentById(i)); + tips = "玩家" + Player::chToStr(Player::idToCh(i)) + "超时判负。"; + sprintf(cmdline, "Time over. Player%d win!", Player::getOpponentById(i)); + } } cmdlist.emplace_back(string(cmdline)); + return true; } @@ -943,25 +875,21 @@ bool Position::win(bool forceDraw) return true; } - // 如果玩家1子数小于赛点,则玩家2获胜 - if (context.nPiecesOnBoard[1] + context.nPiecesInHand[1] < currentRule.nPiecesAtLeast) { - winner = PLAYER_2; - context.phase = PHASE_GAMEOVER; - sprintf(cmdline, "Player2 win!"); - cmdlist.emplace_back(string(cmdline)); - return true; - } - - // 如果玩家2子数小于赛点,则玩家1获胜 - if (context.nPiecesOnBoard[2] + context.nPiecesInHand[2] < currentRule.nPiecesAtLeast) { - winner = PLAYER_1; - context.phase = PHASE_GAMEOVER; - sprintf(cmdline, "Player1 win!"); - cmdlist.emplace_back(string(cmdline)); + // 如果玩家子数小于赛点,则对方获胜 + for (int i = 1; i <= 2; i++) + { + if (context.nPiecesOnBoard[i] + context.nPiecesInHand[i] < currentRule.nPiecesAtLeast) { + int o = Player::getOpponentById(i); + winner = Player::idToPlayer(o); + context.phase = PHASE_GAMEOVER; + sprintf(cmdline, "Player%d win!", o); + cmdlist.emplace_back(string(cmdline)); #ifdef BOOK_LEARNING - MillGameAi_ab::recordOpeningBookToHashMap(); // 暂时只对后手的失败记录到开局库 + MillGameAi_ab::recordOpeningBookToHashMap(); // TODO: 目前是对"双方"失败都记录到开局库 #endif /* BOOK_LEARNING */ - return true; + + return true; + } } // 如果摆满了,根据规则判断胜负 @@ -977,6 +905,7 @@ bool Position::win(bool forceDraw) } cmdlist.emplace_back(string(cmdline)); + return true; } @@ -986,26 +915,21 @@ bool Position::win(bool forceDraw) context.phase = PHASE_GAMEOVER; if (currentRule.isLoseWhenNoWay) { - if (context.turn == PLAYER_1) { - tips = "玩家1无子可走被闷。"; - winner = PLAYER_2; - sprintf(cmdline, "Player1 no way to go. Player2 win!"); - cmdlist.emplace_back(string(cmdline)); - } else { - tips = "玩家2无子可走被闷。"; - winner = PLAYER_1; - sprintf(cmdline, "Player2 no way to go. Player1 win!"); - cmdlist.emplace_back(string(cmdline)); + tips = "玩家" + Player::chToStr(context.turnChar) + "无子可走被闷"; + winner = Player::getOpponent(context.turn); + int winnerId = Player::toId(winner); + sprintf(cmdline, "Player%d no way to go. Player%d win!", context.turnId, winnerId); + cmdlist.emplace_back(string(cmdline)); #ifdef BOOK_LEARNING - MillGameAi_ab::recordOpeningBookToHashMap(); // 暂时只对后手的失败记录到开局库 + MillGameAi_ab::recordOpeningBookToHashMap(); // TODO: 目前是对所有的失败记录到开局库 #endif /* BOOK_LEARNING */ - } return true; } // 否则让棋,由对手走 changeTurn(); + return false; } @@ -1064,71 +988,73 @@ void Position::cleanForbiddenLocations() } } -enum player_t Position::changeTurn() +void Position::setTurn(player_t player) { // 设置轮到谁走 - context.turn = (context.turn == PLAYER_1) ? PLAYER_2 : PLAYER_1; + context.turn = player; - return context.turn; + context.turnId = Player::toId(context.turn); + context.turnChar = Player::idToCh(context.turnId); + //context.turnStr = Player::chToStr(context.turnChar); + + context.opponent = Player::getOpponent(player); + + context.opponentId = Player::toId(context.opponent); + context.opponentChar = Player::idToCh(context.opponentId); + //context.opponentStr = Player::chToStr(context.opponentChar); +} + +void Position::changeTurn() +{ + setTurn(Player::getOpponent(context.turn)); } void Position::setTips() { + string winnerStr, t; + int winnerId; + string turnStr = Player::chToStr(context.turnChar); + switch (context.phase) { case PHASE_NOTSTARTED: tips = "轮到玩家1落子,剩余" + std::to_string(context.nPiecesInHand[1]) + "子" + - " 比分 " + to_string(score_1) + ":" + to_string(score_2) + ", 和棋 " + to_string(score_draw); + " 比分 " + to_string(score[1]) + ":" + to_string(score[2]) + ", 和棋 " + to_string(score_draw); break; case PHASE_PLACING: if (context.action == ACTION_PLACE) { - if (context.turn == PLAYER_1) { - tips = "轮到玩家1落子,剩余" + std::to_string(context.nPiecesInHand[1]) + "子"; - } else if (context.turn == PLAYER_2) { - tips = "轮到玩家2落子,剩余" + std::to_string(context.nPiecesInHand[2]) + "子"; - } + tips = "轮到玩家" + turnStr + "落子,剩余" + std::to_string(context.nPiecesInHand[context.turnId]) + "子"; } else if (context.action == ACTION_CAPTURE) { - if (context.turn == PLAYER_1) { - tips = "成三!轮到玩家1去子,需去" + std::to_string(context.nPiecesNeedRemove) + "子"; - } else if (context.turn == PLAYER_2) { - tips = "成三!轮到玩家2去子,需去" + std::to_string(context.nPiecesNeedRemove) + "子"; - } + tips = "成三!轮到玩家" + turnStr + "去子,需去" + std::to_string(context.nPiecesNeedRemove) + "子"; } break; case PHASE_MOVING: if (context.action == ACTION_PLACE || context.action == ACTION_CHOOSE) { - if (context.turn == PLAYER_1) { - tips = "轮到玩家1选子移动"; - } else if (context.turn == PLAYER_2) { - tips = "轮到玩家2选子移动"; - } + tips = "轮到玩家" + turnStr + "选子移动"; } else if (context.action == ACTION_CAPTURE) { - if (context.turn == PLAYER_1) { - tips = "成三!轮到玩家1去子,需去" + std::to_string(context.nPiecesNeedRemove) + "子"; - } else if (context.turn == PLAYER_2) { - tips = "成三!轮到玩家2去子,需去" + std::to_string(context.nPiecesNeedRemove) + "子"; - } + tips = "成三!轮到玩家" + turnStr + "去子,需去" + std::to_string(context.nPiecesNeedRemove) + "子"; } break; - case PHASE_GAMEOVER: + case PHASE_GAMEOVER: if (winner == PLAYER_DRAW) { score_draw++; - tips = "双方平局!比分 " + to_string(score_1) + ":" + to_string(score_2) + ", 和棋 " + to_string(score_draw); - } - else if (winner == PLAYER_1) { - score_1++; - if (tips.find("无子可走") != string::npos) - tips += "玩家1获胜!比分 " + to_string(score_1) + ":" + to_string(score_2) + ", 和棋 " + to_string(score_draw); - else - tips = "玩家1获胜!比分 " + to_string(score_1) + ":" + to_string(score_2) + ", 和棋 " + to_string(score_draw); - } else if (winner == PLAYER_2) { - score_2++; - if (tips.find("无子可走") != string::npos) - tips += "玩家2获胜!比分 " + to_string(score_1) + ":" + to_string(score_2) + ", 和棋 " + to_string(score_draw); - else - tips = "玩家2获胜!比分 " + to_string(score_1) + ":" + to_string(score_2) + ", 和棋 " + to_string(score_draw); + tips = "双方平局!比分 " + to_string(score[1]) + ":" + to_string(score[2]) + ", 和棋 " + to_string(score_draw); + break; + } + + winnerId = Player::toId(winner); + winnerStr = Player::chToStr(Player::idToCh(winnerId)); + + score[winnerId]++; + + t = "玩家" + winnerStr + "获胜!比分 " + to_string(score[1]) + ":" + to_string(score[2]) + ", 和棋 " + to_string(score_draw); + + if (tips.find("无子可走") != string::npos) { + tips += t; + } else { + tips = t; } break; @@ -1142,8 +1068,8 @@ void Position::getElapsedTime(time_t &p1_ms, time_t &p2_ms) { update(); - p1_ms = elapsedSeconds_1; - p2_ms = elapsedSeconds_2; + p1_ms = elapsedSeconds[1]; + p2_ms = elapsedSeconds[2]; } /* diff --git a/src/game/position.h b/src/game/position.h index 99d5160d..ce816325 100644 --- a/src/game/position.h +++ b/src/game/position.h @@ -51,6 +51,13 @@ public: // 轮流状态标识 enum player_t turn; + int turnId; + char turnChar; + //string turnStr; + enum player_t opponent; + int opponentId; + char opponentChar; + //string opponentStr; // 动作状态标识 enum action_t action @@ -77,14 +84,9 @@ class Position public: // 赢盘数 - int score_1 {}; - int score_2 {}; + int score[3]; int score_draw {}; - static int playerToId(enum player_t player); - - static player_t getOpponent(enum player_t player); - private: // 创建哈希值 @@ -115,12 +117,6 @@ public: int nPiecesNeedRemove = 0 // 尚待去除的子数 ); - // 获取棋局状态和棋盘上下文 - void getContext(struct Rule &rule, step_t &step, - phase_t &phase, player_t &turn, action_t &action, - int *&board, - int &nPiecesInHand_1, int &nPiecesInHand_2, int &nPiecesNeedRemove); - // 获取当前规则 const struct Rule *getRule() const { @@ -169,12 +165,6 @@ public: return context.phase; } - // 获取轮流状态标识 - enum player_t whosTurn() const - { - return context.turn; - } - // 获取动作状态标识 enum action_t getAction() const { @@ -272,8 +262,11 @@ public: // 清除所有禁点 void cleanForbiddenLocations(); + // 设置轮流 + void setTurn(player_t player); + // 改变轮流 - enum player_t changeTurn(); + void changeTurn(); // 设置提示 void setTips(); @@ -358,11 +351,8 @@ private: // 当前游戏时间 time_t currentTime {}; - // 玩家1用时(秒) - time_t elapsedSeconds_1 {}; - - // 玩家2用时(秒) - time_t elapsedSeconds_2 {}; + // 玩家用时(秒) + time_t elapsedSeconds[3]; // 当前棋局的字符提示 string tips; diff --git a/src/ui/qt/gamecontroller.cpp b/src/ui/qt/gamecontroller.cpp index 92f295f8..b53e230a 100644 --- a/src/ui/qt/gamecontroller.cpp +++ b/src/ui/qt/gamecontroller.cpp @@ -37,15 +37,11 @@ GameController::GameController(GameScene & scene, QObject * parent) : QObject(parent), - ai1(1), - ai2(2), scene(scene), currentPiece(nullptr), currentRow(-1), isEditing(false), isInverted(false), - isAiPlayer_1(false), - isAiPlayer_2(false), hasAnimation(true), durationTime(500), hasSound(true), @@ -64,16 +60,22 @@ GameController::GameController(GameScene & scene, QObject * parent) : scene.setBackgroundBrush(QColor(239, 239, 239)); #endif /* MOBILE_APP_UI */ + isAiPlayer[1] = false, + isAiPlayer[2] = false, + + ai[1] = new AiThread(1); + ai[2] = new AiThread(2); + gameReset(); // 关联AI和控制器的着法命令行 - connect(&ai1, SIGNAL(command(const QString &, bool)), + connect(ai[1], SIGNAL(command(const QString &, bool)), this, SLOT(command(const QString &, bool))); - connect(&ai2, SIGNAL(command(const QString &, bool)), + connect(ai[2], SIGNAL(command(const QString &, bool)), this, SLOT(command(const QString &, bool))); // 关联AI和网络类的着法命令行 - connect(ai1.getClient(), SIGNAL(command(const QString &, bool)), + connect(ai[1]->getClient(), SIGNAL(command(const QString &, bool)), this, SLOT(command(const QString &, bool))); // 安装事件过滤器监视scene的各个事件, @@ -88,10 +90,13 @@ GameController::~GameController() killTimer(timeID); // 停掉线程 - ai1.stop(); - ai2.stop(); - ai1.wait(); - ai2.wait(); + ai[1]->stop(); + ai[2]->stop(); + ai[1]->wait(); + ai[2]->wait(); + + delete ai[1]; + delete ai[2]; #ifdef BOOK_LEARNING MillGameAi_ab::recordOpeningBookHashMapToFile(); @@ -149,10 +154,10 @@ void GameController::gameReset() // 停掉线程 if (!isAutoRestart) { - ai1.stop(); - ai2.stop(); - isAiPlayer_1 = false; - isAiPlayer_2 = false; + ai[1]->stop(); + ai[2]->stop(); + isAiPlayer[1] = false; + isAiPlayer[2] = false; } // 清除棋子 @@ -206,10 +211,10 @@ void GameController::gameReset() // 如果规则不要求计时,则time1和time2表示已用时间 if (timeLimit <= 0) { // 将玩家的已用时间清零 - remainingTime1 = remainingTime2 = 0; + remainingTime[1] = remainingTime[2] = 0; } else { // 将玩家的剩余时间置为限定时间 - remainingTime1 = remainingTime2 = timeLimit * 60; + remainingTime[1] = remainingTime[2] = timeLimit * 60; } // 更新棋谱 @@ -219,7 +224,7 @@ void GameController::gameReset() currentRow = 0; // 发出信号通知主窗口更新LCD显示 - QTime qtime = QTime(0, 0, 0, 0).addSecs(remainingTime1); + QTime qtime = QTime(0, 0, 0, 0).addSecs(remainingTime[1]); emit time1Changed(qtime.toString("hh:mm:ss")); emit time2Changed(qtime.toString("hh:mm:ss")); @@ -228,8 +233,8 @@ void GameController::gameReset() emit statusBarChanged(message); // 更新比分 LCD 显示 - emit score1Changed(QString::number(position_.score_1, 10)); - emit score2Changed(QString::number(position_.score_2, 10)); + emit score1Changed(QString::number(position_.score[1], 10)); + emit score2Changed(QString::number(position_.score[2], 10)); emit scoreDrawChanged(QString::number(position_.score_draw, 10)); // 播放音效 @@ -282,64 +287,59 @@ void GameController::setRule(int ruleNo, step_t stepLimited /*= -1*/, int timeLi gameReset(); } -void GameController::setEngine1(bool arg) +void GameController::setEngine(int id, bool arg) { position_.configure(giveUpIfMostLose_, randomMove_); - isAiPlayer_1 = arg; + isAiPlayer[id] = arg; + if (arg) { - ai1.setAi(position_); - if (ai1.isRunning()) - ai1.resume(); + ai[id]->setAi(position_); + if (ai[id]->isRunning()) + ai[id]->resume(); else - ai1.start(); + ai[id]->start(); } else { - ai1.stop(); + ai[id]->stop(); } } +void GameController::setEngine1(bool arg) +{ + setEngine(1, arg); +} + void GameController::setEngine2(bool arg) { - position_.configure(giveUpIfMostLose_, randomMove_); - - isAiPlayer_2 = arg; - if (arg) { - ai2.setAi(position_); - if (ai2.isRunning()) - ai2.resume(); - else - ai2.start(); - } else { - ai2.stop(); - } + setEngine(2, arg); } void GameController::setAiDepthTime(depth_t depth1, int time1, depth_t depth2, int time2) { - if (isAiPlayer_1) { - ai1.stop(); - ai1.wait(); + if (isAiPlayer[1]) { + ai[1]->stop(); + ai[1]->wait(); } - if (isAiPlayer_2) { - ai2.stop(); - ai2.wait(); + if (isAiPlayer[2]) { + ai[2]->stop(); + ai[2]->wait(); } - ai1.setAi(position_, depth1, time1); - ai2.setAi(position_, depth2, time2); + ai[1]->setAi(position_, depth1, time1); + ai[2]->setAi(position_, depth2, time2); - if (isAiPlayer_1) { - ai1.start(); + if (isAiPlayer[1]) { + ai[1]->start(); } - if (isAiPlayer_2) { - ai2.start(); + if (isAiPlayer[2]) { + ai[2]->start(); } } void GameController::getAiDepthTime(depth_t &depth1, int &time1, depth_t &depth2, int &time2) { - ai1.getDepthTime(depth1, time1); - ai2.getDepthTime(depth2, time2); + ai[1]->getDepthTime(depth1, time1); + ai[2]->getDepthTime(depth2, time2); } void GameController::setAnimation(bool arg) @@ -389,13 +389,13 @@ void GameController::setRandomMove(bool arg) // 上下翻转 void GameController::flip() { - if (isAiPlayer_1) { - ai1.stop(); - ai1.wait(); + if (isAiPlayer[1]) { + ai[1]->stop(); + ai[1]->wait(); } - if (isAiPlayer_2) { - ai2.stop(); - ai2.wait(); + if (isAiPlayer[2]) { + ai[2]->stop(); + ai[2]->wait(); } position_.context.board.mirror(position_.cmdlist, position_.cmdline, position_.move_, position_.currentRule, position_.currentLocation); @@ -414,28 +414,28 @@ void GameController::flip() else phaseChange(currentRow, true); - ai1.setAi(position_); - ai2.setAi(position_); + ai[1]->setAi(position_); + ai[2]->setAi(position_); - if (isAiPlayer_1) { - ai1.start(); + if (isAiPlayer[1]) { + ai[1]->start(); } - if (isAiPlayer_2) { - ai2.start(); + if (isAiPlayer[2]) { + ai[2]->start(); } } // 左右镜像 void GameController::mirror() { - if (isAiPlayer_1) { - ai1.stop(); - ai1.wait(); + if (isAiPlayer[1]) { + ai[1]->stop(); + ai[1]->wait(); } - if (isAiPlayer_2) { - ai2.stop(); - ai2.wait(); + if (isAiPlayer[2]) { + ai[2]->stop(); + ai[2]->wait(); } position_.context.board.mirror(position_.cmdlist, position_.cmdline, position_.move_, position_.currentRule, position_.currentLocation); @@ -456,28 +456,28 @@ void GameController::mirror() else phaseChange(currentRow, true); - ai1.setAi(position_); - ai2.setAi(position_); + ai[1]->setAi(position_); + ai[2]->setAi(position_); - if (isAiPlayer_1) { - ai1.start(); + if (isAiPlayer[1]) { + ai[1]->start(); } - if (isAiPlayer_2) { - ai2.start(); + if (isAiPlayer[2]) { + ai[2]->start(); } } // 视图须时针旋转90° void GameController::turnRight() { - if (isAiPlayer_1) { - ai1.stop(); - ai1.wait(); + if (isAiPlayer[1]) { + ai[1]->stop(); + ai[1]->wait(); } - if (isAiPlayer_2) { - ai2.stop(); - ai2.wait(); + if (isAiPlayer[2]) { + ai[2]->stop(); + ai[2]->wait(); } position_.context.board.rotate(-90, position_.cmdlist, position_.cmdline, position_.move_, position_.currentRule, position_.currentLocation); @@ -496,28 +496,28 @@ void GameController::turnRight() else phaseChange(currentRow, true); - ai1.setAi(position_); - ai2.setAi(position_); + ai[1]->setAi(position_); + ai[2]->setAi(position_); - if (isAiPlayer_1) { - ai1.start(); + if (isAiPlayer[1]) { + ai[1]->start(); } - if (isAiPlayer_2) { - ai2.start(); + if (isAiPlayer[2]) { + ai[2]->start(); } } // 视图逆时针旋转90° void GameController::turnLeft() { - if (isAiPlayer_1) { - ai1.stop(); - ai1.wait(); + if (isAiPlayer[1]) { + ai[1]->stop(); + ai[1]->wait(); } - if (isAiPlayer_2) { - ai2.stop(); - ai2.wait(); + if (isAiPlayer[2]) { + ai[2]->stop(); + ai[2]->wait(); } position_.context.board.rotate(90, position_.cmdlist, position_.cmdline, position_.move_, position_.currentRule, position_.currentLocation); @@ -532,13 +532,13 @@ void GameController::turnLeft() // 刷新显示 updateScence(); - ai1.setAi(position_); - ai2.setAi(position_); - if (isAiPlayer_1) { - ai1.start(); + ai[1]->setAi(position_); + ai[2]->setAi(position_); + if (isAiPlayer[1]) { + ai[1]->start(); } - if (isAiPlayer_2) { - ai2.start(); + if (isAiPlayer[2]) { + ai[2]->start(); } } @@ -548,17 +548,17 @@ void GameController::timerEvent(QTimerEvent *event) static QTime qt1, qt2; // 玩家的已用时间 - position_.getElapsedTime(remainingTime1, remainingTime2); + position_.getElapsedTime(remainingTime[1], remainingTime[2]); // 如果规则要求计时,则time1和time2表示倒计时 if (timeLimit > 0) { // 玩家的剩余时间 - remainingTime1 = timeLimit * 60 - remainingTime1; - remainingTime2 = timeLimit * 60 - remainingTime2; + remainingTime[1] = timeLimit * 60 - remainingTime[1]; + remainingTime[2] = timeLimit * 60 - remainingTime[2]; } - qt1 = QTime(0, 0, 0, 0).addSecs(remainingTime1); - qt2 = QTime(0, 0, 0, 0).addSecs(remainingTime2); + qt1 = QTime(0, 0, 0, 0).addSecs(remainingTime[1]); + qt2 = QTime(0, 0, 0, 0).addSecs(remainingTime[2]); emit time1Changed(qt1.toString("hh:mm:ss")); emit time2Changed(qt2.toString("hh:mm:ss")); @@ -609,8 +609,7 @@ void GameController::timerEvent(QTimerEvent *event) bool GameController::isAIsTurn() { - return ((position_.whosTurn() == PLAYER_1 && isAiPlayer_1) || - (position_.whosTurn() == PLAYER_2 && isAiPlayer_2)); + return isAiPlayer[position_.context.turnId]; } // 关键槽函数,根据QGraphicsScene的信号和状态来执行选子、落子或去子 @@ -748,24 +747,24 @@ bool GameController::actionPiece(QPointF pos) if (&position_ == &(this->position_)) { // 如果还未决出胜负 if (position_.whoWin() == PLAYER_NOBODY) { - if (position_.whosTurn() == PLAYER_1) { - if (isAiPlayer_1) { - ai1.resume(); + if (position_.context.turn == PLAYER_1) { + if (isAiPlayer[1]) { + ai[1]->resume(); } - if (isAiPlayer_2) - ai2.pause(); + if (isAiPlayer[2]) + ai[2]->pause(); } else { - if (isAiPlayer_1) - ai1.pause(); - if (isAiPlayer_2) { - ai2.resume(); + if (isAiPlayer[1]) + ai[1]->pause(); + if (isAiPlayer[2]) { + ai[2]->resume(); } } } // 如果已经决出胜负 else { - ai1.stop(); - ai2.stop(); + ai[1]->stop(); + ai[2]->stop(); // 弹框 //message = QString::fromStdString(position_.getTips()); @@ -780,32 +779,28 @@ bool GameController::actionPiece(QPointF pos) bool GameController::giveUp() { - bool result = false; - - if (position_.whosTurn() == PLAYER_1) { - result = position_.giveup(PLAYER_1); - } - else if (position_.whosTurn() == PLAYER_2) { - result = position_.giveup(PLAYER_2); - } + bool result = position_.giveup(position_.context.turn); - if (result) { - // 将新增的棋谱行插入到ListModel - currentRow = manualListModel.rowCount() - 1; - int k = 0; - - // 输出命令行 - for (const auto & i : *(position_.getCmdList())) { - // 跳过已添加的,因标准list容器没有下标 - if (k++ <= currentRow) - continue; - manualListModel.insertRow(++currentRow); - manualListModel.setData(manualListModel.index(currentRow), i.c_str()); - } - if (position_.whoWin() != PLAYER_NOBODY) - playSound(":/sound/resources/sound/loss.wav"); + if (!result) { + return false; } + // 将新增的棋谱行插入到ListModel + currentRow = manualListModel.rowCount() - 1; + int k = 0; + + // 输出命令行 + for (const auto & i : *(position_.getCmdList())) { + // 跳过已添加的,因标准list容器没有下标 + if (k++ <= currentRow) + continue; + manualListModel.insertRow(++currentRow); + manualListModel.setData(manualListModel.index(currentRow), i.c_str()); + } + + if (position_.whoWin() != PLAYER_NOBODY) + playSound(":/sound/resources/sound/loss.wav"); + return result; } @@ -815,10 +810,10 @@ bool GameController::command(const QString &cmd, bool update /* = true */) Q_UNUSED(hasSound) // 防止接收滞后结束的线程发送的指令 - if (sender() == &ai1 && !isAiPlayer_1) + if (sender() == ai[1] && !isAiPlayer[1]) return false; - if (sender() == &ai2 && !isAiPlayer_2) + if (sender() == ai[2] && !isAiPlayer[2]) return false; // 声音 @@ -892,34 +887,34 @@ bool GameController::command(const QString &cmd, bool update /* = true */) if (&position_ == &(this->position_)) { // 如果还未决出胜负 if (position_.whoWin() == PLAYER_NOBODY) { - if (position_.whosTurn() == PLAYER_1) { - if (isAiPlayer_1) { - ai1.resume(); + if (position_.context.turn == PLAYER_1) { + if (isAiPlayer[1]) { + ai[1]->resume(); } - if (isAiPlayer_2) - ai2.pause(); + if (isAiPlayer[2]) + ai[2]->pause(); } else { - if (isAiPlayer_1) - ai1.pause(); - if (isAiPlayer_2) { - ai2.resume(); + if (isAiPlayer[1]) + ai[1]->pause(); + if (isAiPlayer[2]) { + ai[2]->resume(); } } } // 如果已经决出胜负 else { - ai1.stop(); - ai2.stop(); + ai[1]->stop(); + ai[2]->stop(); if (isAutoRestart) { gameReset(); gameStart(); - if (isAiPlayer_1) { - setEngine1(true); + if (isAiPlayer[1]) { + setEngine(1, true); } - if (isAiPlayer_2) { - setEngine2(true); + if (isAiPlayer[2]) { + setEngine(2, true); } } @@ -932,11 +927,11 @@ bool GameController::command(const QString &cmd, bool update /* = true */) } // 网络: 将着法放到服务器的发送列表中 - if (isAiPlayer_1) + if (isAiPlayer[1]) { - ai1.getServer()->setAction(cmd); - } else if (isAiPlayer_2) { - ai1.getServer()->setAction(cmd); // 注意: 同样是AI1 + ai[1]->getServer()->setAction(cmd); + } else if (isAiPlayer[2]) { + ai[1]->getServer()->setAction(cmd); // 注意: 同样是AI1 } return true; @@ -1106,8 +1101,8 @@ bool GameController::updateScence(Position &game) animationGroup->start(QAbstractAnimation::DeleteWhenStopped); // 更新比分 LCD 显示 - emit score1Changed(QString::number(game.score_1, 10)); - emit score2Changed(QString::number(game.score_2, 10)); + emit score1Changed(QString::number(game.score[1], 10)); + emit score2Changed(QString::number(game.score[2], 10)); emit scoreDrawChanged(QString::number(game.score_draw, 10)); return true; @@ -1115,6 +1110,6 @@ bool GameController::updateScence(Position &game) void GameController::showNetworkWindow() { - ai1.getServer()->show(); - ai1.getClient()->show(); + ai[1]->getServer()->show(); + ai[1]->getClient()->show(); } diff --git a/src/ui/qt/gamecontroller.h b/src/ui/qt/gamecontroller.h index 9f448eba..a361b330 100644 --- a/src/ui/qt/gamecontroller.h +++ b/src/ui/qt/gamecontroller.h @@ -140,11 +140,10 @@ public slots: // 设置黑白反转状态 void setInvert(bool arg = true); - // 让电脑执先手 - void setEngine1(bool arg = true); - - // 让电脑执后手 - void setEngine2(bool arg = true); + // id为1时让电脑执先手, id为2时让的电脑执后手 + void setEngine(int id, bool arg = true); + void setEngine1(bool arg); + void setEngine2(bool arg); // 是否有落子动画 void setAnimation(bool arg = true); @@ -210,7 +209,7 @@ private: Position dummyPosition; // 2个AI的线程 - AiThread ai1, ai2; + AiThread *ai[3]; // 棋局的场景类 GameScene &scene; @@ -230,11 +229,8 @@ private: // 是否黑白反转 bool isInverted; - // 是否电脑执先手 - bool isAiPlayer_1; - - // 是否电脑执后手 - bool isAiPlayer_2; + // 电脑执先手时为 true + bool isAiPlayer[3]; // 是否有落子动画 bool hasAnimation; @@ -266,11 +262,8 @@ private: // 规则限步数 step_t stepsLimit; - // 玩家1剩余时间(秒) - time_t remainingTime1; - - // 玩家2剩余时间(秒) - time_t remainingTime2; + // 玩家剩余时间(秒) + time_t remainingTime[3]; // 用于主窗口状态栏显示的字符串 QString message;