From dc90db249c879fd7f16ce9b38ee849c5dc659865 Mon Sep 17 00:00:00 2001 From: CalciteM Date: Sun, 8 Sep 2019 15:15:21 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=88=9B=E5=BB=BA=20board=20?= =?UTF-8?q?=E5=92=8C=20rule=20=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 将部分逻辑转移到这两个 class; * 创建了 types.h 将部分类型转移到此文件; * 将 millList 由 list 改为 vector, 否则容易出现段错误. --- include/config.h | 2 +- millgame.pro | 5 + millgame.vcxproj | 5 + millgame.vcxproj.filters | 15 + src/ai/evaluate.cpp | 36 +- src/ai/evaluate.h | 18 +- src/ai/movegen.cpp | 50 +- src/ai/movegen.h | 8 +- src/ai/search.cpp | 91 +-- src/ai/search.h | 35 +- src/ai/zobrist.h | 2 +- src/base/hashmap.h | 4 +- src/base/thread.cpp | 2 +- src/base/thread.h | 6 +- src/game/board.cpp | 992 +++++++++++++++++++++++++++++ src/game/board.h | 145 +++++ src/game/millgame.cpp | 1149 ++-------------------------------- src/game/millgame.h | 391 +++--------- src/game/rule.cpp | 114 ++++ src/game/rule.h | 82 +++ src/game/types.h | 87 +++ src/ui/qt/boarditem.cpp | 4 +- src/ui/qt/gamecontroller.cpp | 96 +-- src/ui/qt/gamecontroller.h | 8 +- src/ui/qt/gamewindow.cpp | 18 +- 25 files changed, 1775 insertions(+), 1590 deletions(-) create mode 100644 src/game/board.cpp create mode 100644 src/game/board.h create mode 100644 src/game/rule.cpp create mode 100644 src/game/rule.h create mode 100644 src/game/types.h diff --git a/include/config.h b/include/config.h index 5561e17a..c044dfa8 100644 --- a/include/config.h +++ b/include/config.h @@ -121,7 +121,7 @@ #endif #ifdef DEBUG_MODE -#define DRAW_SEAT_NUMBER +#define PLAYER_DRAW_SEAT_NUMBER #endif #ifndef MOBILE_APP_UI diff --git a/millgame.pro b/millgame.pro index 6bb894c2..b18d07b4 100644 --- a/millgame.pro +++ b/millgame.pro @@ -24,7 +24,9 @@ INCLUDEPATH += src/ui/qt SOURCES += \ src/ai/evaluate.cpp \ src/ai/movegen.cpp \ + src/game/board.cpp \ src/game/millgame.cpp \ + src/game/rule.cpp \ src/main.cpp \ src/base/thread.cpp \ src/ai/search.cpp \ @@ -52,7 +54,10 @@ HEADERS += \ src/base/thread.h \ src/ai/search.h \ src/ai/zobrist.h \ + src/game/board.h \ src/game/millgame.h \ + src/game/rule.h \ + src/game/types.h \ src/ui/qt/client.h \ src/ui/qt/gamecontroller.h \ src/ui/qt/gamescene.h \ diff --git a/millgame.vcxproj b/millgame.vcxproj index af96e730..928598b1 100644 --- a/millgame.vcxproj +++ b/millgame.vcxproj @@ -451,7 +451,10 @@ + + + @@ -696,7 +699,9 @@ + + diff --git a/millgame.vcxproj.filters b/millgame.vcxproj.filters index 8cf3eb51..e0ad3b67 100644 --- a/millgame.vcxproj.filters +++ b/millgame.vcxproj.filters @@ -102,6 +102,15 @@ ai + + game + + + game + + + game + @@ -316,6 +325,12 @@ ai + + game + + + game + diff --git a/src/ai/evaluate.cpp b/src/ai/evaluate.cpp index 91d2daae..cad42d87 100644 --- a/src/ai/evaluate.cpp +++ b/src/ai/evaluate.cpp @@ -1,6 +1,6 @@ #include "evaluate.h" -Evaluation::value_t Evaluation::getValue(MillGame &chessTemp, MillGame::ChessContext *chessContext, MillGameAi_ab::Node *node) +value_t Evaluation::getValue(MillGame &chessTemp, ChessContext *chessContext, MillGameAi_ab::Node *node) { // 初始评估值为0,对先手有利则增大,对后手有利则减小 value_t value = 0; @@ -16,10 +16,10 @@ Evaluation::value_t Evaluation::getValue(MillGame &chessTemp, MillGame::ChessCon #endif switch (chessContext->stage) { - case MillGame::GAME_NOTSTARTED: + case GAME_NOTSTARTED: break; - case MillGame::GAME_PLACING: + case GAME_PLACING: // 按手中的棋子计分,不要break; nPiecesInHandDiff = chessContext->nPiecesInHand_1 - chessContext->nPiecesInHand_2; value += nPiecesInHandDiff * 50; @@ -36,13 +36,13 @@ Evaluation::value_t Evaluation::getValue(MillGame &chessTemp, MillGame::ChessCon switch (chessContext->action) { // 选子和落子使用相同的评价方法 - case MillGame::ACTION_CHOOSE: - case MillGame::ACTION_PLACE: + case ACTION_CHOOSE: + case ACTION_PLACE: break; // 如果形成去子状态,每有一个可去的子,算100分 - case MillGame::ACTION_CAPTURE: - nPiecesNeedRemove = (chessContext->turn == MillGame::PLAYER1) ? + case ACTION_CAPTURE: + nPiecesNeedRemove = (chessContext->turn == PLAYER1) ? chessContext->nPiecesNeedRemove : -(chessContext->nPiecesNeedRemove); value += nPiecesNeedRemove * 100; #ifdef DEBUG_AB_TREE @@ -55,24 +55,24 @@ Evaluation::value_t Evaluation::getValue(MillGame &chessTemp, MillGame::ChessCon break; - case MillGame::GAME_MOVING: + case GAME_MOVING: // 按场上棋子计分 value += chessContext->nPiecesOnBoard_1 * 100 - chessContext->nPiecesOnBoard_2 * 100; #ifdef EVALUATE_MOBILITY // 按棋子活动能力计分 - value += chessTemp.getMobilityDiff(false) * 10; + value += chessTemp.getMobilityDiff(chessContext->turn, chessTemp.currentRule, chessContext->nPiecesInHand_1, chessContext->nPiecesInHand_2, false) * 10; #endif /* EVALUATE_MOBILITY */ switch (chessContext->action) { // 选子和落子使用相同的评价方法 - case MillGame::ACTION_CHOOSE: - case MillGame::ACTION_PLACE: + case ACTION_CHOOSE: + case ACTION_PLACE: break; // 如果形成去子状态,每有一个可去的子,算128分 - case MillGame::ACTION_CAPTURE: - nPiecesNeedRemove = (chessContext->turn == MillGame::PLAYER1) ? + case ACTION_CAPTURE: + nPiecesNeedRemove = (chessContext->turn == PLAYER1) ? chessContext->nPiecesNeedRemove : -(chessContext->nPiecesNeedRemove); value += nPiecesNeedRemove * 128; #ifdef DEBUG_AB_TREE @@ -86,10 +86,10 @@ Evaluation::value_t Evaluation::getValue(MillGame &chessTemp, MillGame::ChessCon break; // 终局评价最简单 - case MillGame::GAME_OVER: + case GAME_OVER: // 布局阶段闷棋判断 if (chessContext->nPiecesOnBoard_1 + chessContext->nPiecesOnBoard_2 >= - MillGame::N_SEATS * MillGame::N_RINGS) { + Board::N_SEATS * Board::N_RINGS) { if (chessTemp.getRule()->isStartingPlayerLoseWhenBoardFull) { // winner = PLAYER2; value -= 10000; @@ -102,11 +102,11 @@ Evaluation::value_t Evaluation::getValue(MillGame &chessTemp, MillGame::ChessCon } // 走棋阶段被闷判断 - if (chessContext->action == MillGame::ACTION_CHOOSE && - chessTemp.isAllSurrounded(chessContext->turn) && + if (chessContext->action == ACTION_CHOOSE && + chessTemp.context.board.isAllSurrounded(chessContext->turn, chessTemp.currentRule, chessContext->nPiecesOnBoard_1, chessContext->nPiecesOnBoard_2, chessContext->turn) && chessTemp.getRule()->isLoseWhenNoWay) { // 规则要求被“闷”判负,则对手获胜 - if (chessContext->turn == MillGame::PLAYER1) { + if (chessContext->turn == PLAYER1) { value -= 10000; #ifdef DEBUG_AB_TREE node->result = -2; diff --git a/src/ai/evaluate.h b/src/ai/evaluate.h index 452f170d..03a0ffa6 100644 --- a/src/ai/evaluate.h +++ b/src/ai/evaluate.h @@ -13,57 +13,55 @@ public: Evaluation &operator=(const Evaluation &) = delete; - using value_t = MillGameAi_ab::value_t; - - static value_t getValue(MillGame &chessTemp, MillGame::ChessContext *chessContext, MillGameAi_ab::Node *node); + static value_t getValue(MillGame &chessTemp, ChessContext *chessContext, MillGameAi_ab::Node *node); // #ifdef EVALUATE_ENABLE #ifdef EVALUATE_MATERIAL - static value_t evaluateMaterial(Node *node) + static value_t evaluateMaterial(MillGameAi_ab::Node *node) { return 0; } #endif #ifdef EVALUATE_SPACE - static value_t evaluateSpace(Node *node) + static value_t evaluateSpace(MillGameAi_ab::Node *node) { return 0; } #endif #ifdef EVALUATE_MOBILITY - static value_t evaluateMobility(Node *node) + static value_t evaluateMobility(MillGameAi_ab::Node *node) { return 0; } #endif #ifdef EVALUATE_TEMPO - static value_t evaluateTempo(Node *node) + static value_t evaluateTempo(MillGameAi_ab::Node *node) { return 0; } #endif #ifdef EVALUATE_THREAT - static value_t evaluateThreat(Node *node) + static value_t evaluateThreat(MillGameAi_ab::Node *node) { return 0; } #endif #ifdef EVALUATE_SHAPE - static value_t evaluateShape(Node *node) + static value_t evaluateShape(MillGameAi_ab::Node *node) { return 0; } #endif #ifdef EVALUATE_MOTIF - static value_t MillGameAi_ab::evaluateMotif(Node *node) + static value_t MillGameAi_ab::evaluateMotif(MillGameAi_ab::Node *node) { return 0; } diff --git a/src/ai/movegen.cpp b/src/ai/movegen.cpp index aad8908f..3571d94b 100644 --- a/src/ai/movegen.cpp +++ b/src/ai/movegen.cpp @@ -6,15 +6,15 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &chessTemp, MillGameAi_ab::Node *node, MillGameAi_ab::Node *rootNode, move_t bestMove) { - const int MOVE_PRIORITY_TABLE_SIZE = MillGame::N_RINGS * MillGame::N_SEATS; + const int MOVE_PRIORITY_TABLE_SIZE = Board::N_RINGS * Board::N_SEATS; int pos = 0; size_t newCapacity = 24; // 留足余量空间避免多次重新分配,此动作本身也占用 CPU/内存 开销 switch (chessTemp.getStage()) { - case MillGame::GAME_PLACING: - if (chessTemp.getAction() == MillGame::ACTION_CAPTURE) { - if (chessTemp.whosTurn() == MillGame::PLAYER1) + case GAME_PLACING: + if (chessTemp.getAction() == ACTION_CAPTURE) { + if (chessTemp.whosTurn() == PLAYER1) newCapacity = static_cast(chessTemp.getPiecesOnBoardCount_2()); else newCapacity = static_cast(chessTemp.getPiecesOnBoardCount_1()); @@ -22,9 +22,9 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &chessTemp, newCapacity = static_cast(chessTemp.getPiecesInHandCount_1() + chessTemp.getPiecesInHandCount_2()); } break; - case MillGame::GAME_MOVING: - if (chessTemp.getAction() == MillGame::ACTION_CAPTURE) { - if (chessTemp.whosTurn() == MillGame::PLAYER1) + case GAME_MOVING: + if (chessTemp.getAction() == ACTION_CAPTURE) { + if (chessTemp.whosTurn() == PLAYER1) newCapacity = static_cast(chessTemp.getPiecesOnBoardCount_2()); else newCapacity = static_cast(chessTemp.getPiecesOnBoardCount_1()); @@ -32,7 +32,7 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &chessTemp, newCapacity = 6; } break; - case MillGame::GAME_NOTSTARTED: + case GAME_NOTSTARTED: newCapacity = 24; break; default: @@ -48,15 +48,15 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &chessTemp, } // 对手 - MillGame::Player opponent = MillGame::getOpponent(chessTemp.context.turn); + Player opponent = MillGame::getOpponent(chessTemp.context.turn); // 列出所有合法的下一招 switch (chessTemp.context.action) { // 对于选子和落子动作 - case MillGame::ACTION_CHOOSE: - case MillGame::ACTION_PLACE: + case ACTION_CHOOSE: + case ACTION_PLACE: // 对于摆子阶段 - if (chessTemp.context.stage & (MillGame::GAME_PLACING | MillGame::GAME_NOTSTARTED)) { + if (chessTemp.context.stage & (GAME_PLACING | GAME_NOTSTARTED)) { for (int i : movePriorityTable) { pos = i; @@ -64,7 +64,7 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &chessTemp, continue; } - if (chessTemp.context.stage != MillGame::GAME_NOTSTARTED || node != rootNode) { + if (chessTemp.context.stage != GAME_NOTSTARTED || node != rootNode) { ai_ab.addNode(node, 0, pos, bestMove, chessTemp.context.turn); } else { // 若为先手,则抢占星位 @@ -77,7 +77,7 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &chessTemp, } // 对于移子阶段 - if (chessTemp.context.stage & MillGame::GAME_MOVING) { + if (chessTemp.context.stage & GAME_MOVING) { int newPos, oldPos; // 尽量走理论上较差的位置的棋子 @@ -88,12 +88,12 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &chessTemp, continue; } - if ((chessTemp.context.turn == MillGame::PLAYER1 && + if ((chessTemp.context.turn == PLAYER1 && (chessTemp.context.nPiecesOnBoard_1 > chessTemp.currentRule.nPiecesAtLeast || !chessTemp.currentRule.allowFlyWhenRemainThreePieces)) || - (chessTemp.context.turn == MillGame::PLAYER2 && + (chessTemp.context.turn == PLAYER2 && (chessTemp.context.nPiecesOnBoard_2 > chessTemp.currentRule.nPiecesAtLeast || !chessTemp.currentRule.allowFlyWhenRemainThreePieces))) { // 对于棋盘上还有3个子以上,或不允许飞子的情况,要求必须在着法表中 - for (int moveDirection = MillGame::MOVE_DIRECTION_CLOCKWISE; moveDirection <= MillGame::MOVE_DIRECTION_OUTWARD; moveDirection++) { + for (int moveDirection = MOVE_DIRECTION_CLOCKWISE; moveDirection <= MOVE_DIRECTION_OUTWARD; moveDirection++) { // 对于原有位置,遍历四个方向的着法,如果棋盘上为空位就加到结点列表中 newPos = moveTable[oldPos][moveDirection]; if (newPos && !chessTemp.board_[newPos]) { @@ -103,7 +103,7 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &chessTemp, } } else { // 对于棋盘上还有不到3个字,但允许飞子的情况,不要求在着法表中,是空位就行 - for (newPos = MillGame::POS_BEGIN; newPos < MillGame::POS_END; newPos++) { + for (newPos = Board::POS_BEGIN; newPos < Board::POS_END; newPos++) { if (!chessTemp.board_[newPos]) { int move = (oldPos << 8) + newPos; ai_ab.addNode(node, 0, move, bestMove, chessTemp.context.turn); @@ -115,8 +115,8 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &chessTemp, break; // 对于吃子动作 - case MillGame::ACTION_CAPTURE: - if (chessTemp.isAllInMills(opponent)) { + case ACTION_CAPTURE: + if (chessTemp.context.board.isAllInMills(opponent)) { // 全成三的情况 for (int i = MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) { pos = movePriorityTable[i]; @@ -131,7 +131,7 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &chessTemp, for (int i = MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) { pos = movePriorityTable[i]; if (chessTemp.board_[pos] & opponent) { - if (chessTemp.getRule()->allowRemoveMill || !chessTemp.isInMills(pos)) { + if (chessTemp.getRule()->allowRemoveMill || !chessTemp.context.board.isInMills(pos)) { ai_ab.addNode(node, 0, -pos, bestMove, chessTemp.context.turn); } } @@ -147,7 +147,7 @@ void MoveList::createMoveTable(MillGame &chess) { #ifdef CONST_MOVE_TABLE #if 1 - const int moveTable_obliqueLine[MillGame::N_POINTS][MillGame::N_MOVE_DIRECTIONS] = { + const int moveTable_obliqueLine[Board::N_POINTS][N_MOVE_DIRECTIONS] = { /* 0 */ {0, 0, 0, 0}, /* 1 */ {0, 0, 0, 0}, /* 2 */ {0, 0, 0, 0}, @@ -194,7 +194,7 @@ void MoveList::createMoveTable(MillGame &chess) /* 39 */ {0, 0, 0, 0}, }; - const int moveTable_noObliqueLine[MillGame::N_POINTS][MillGame::N_MOVE_DIRECTIONS] = { + const int moveTable_noObliqueLine[Board::N_POINTS][N_MOVE_DIRECTIONS] = { /* 0 */ {0, 0, 0, 0}, /* 1 */ {0, 0, 0, 0}, /* 2 */ {0, 0, 0, 0}, @@ -241,7 +241,7 @@ void MoveList::createMoveTable(MillGame &chess) /* 39 */ {0, 0, 0, 0}, }; #else - const int moveTable_obliqueLine[MillGame::N_POINTS][MillGame::N_MOVE_DIRECTIONS] = { + const int moveTable_obliqueLine[Board::N_POINTS][N_MOVE_DIRECTIONS] = { {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, @@ -288,7 +288,7 @@ void MoveList::createMoveTable(MillGame &chess) {0, 0, 0, 0} }; - const int moveTable_noObliqueLine[MillGame::N_POINTS][MillGame::N_MOVE_DIRECTIONS] = { + const int moveTable_noObliqueLine[Board::N_POINTS][N_MOVE_DIRECTIONS] = { /* 0 */ {0, 0, 0, 0}, /* 1 */ {0, 0, 0, 0}, /* 2 */ {0, 0, 0, 0}, diff --git a/src/ai/movegen.h b/src/ai/movegen.h index 1c11aac3..83622c0f 100644 --- a/src/ai/movegen.h +++ b/src/ai/movegen.h @@ -11,9 +11,7 @@ public: MoveList() = delete; MoveList &operator=(const MoveList &) = delete; - - using move_t = MillGameAi_ab::move_t; - + // 生成所有合法的着法并建立子节点 static void generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &chessTemp, MillGameAi_ab::Node *node, MillGameAi_ab::Node *rootNode, @@ -26,11 +24,11 @@ public: static void shuffleMovePriorityTable(MillGame &chess); // 着法表 // TODO: Move to private - inline static int moveTable[MillGame::N_POINTS][MillGame::N_MOVE_DIRECTIONS] = { {0} }; + inline static int moveTable[Board::N_POINTS][N_MOVE_DIRECTIONS] = { {0} }; private: // 着法顺序表, 后续会被打乱 - inline static array movePriorityTable { + inline static array movePriorityTable { 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, diff --git a/src/ai/search.cpp b/src/ai/search.cpp index e6603403..3e8be900 100644 --- a/src/ai/search.cpp +++ b/src/ai/search.cpp @@ -28,22 +28,23 @@ #include "evaluate.h" #include "movegen.h" #include "hashmap.h" +#include "types.h" using namespace CTSL; #ifdef HASH_MAP_ENABLE static constexpr int hashsize = 0x2000000; // 8-128M:102s, 4-64M:93s 2-32M:91s 1-16M: 冲突 -HashMap hashmap(hashsize); +HashMap hashmap(hashsize); #endif // HASH_MAP_ENABLE #ifdef BOOK_LEARNING static constexpr int bookHashsize = 0x1000000; // 16M -HashMap bookHashMap(bookHashsize); -vector openingBook; +HashMap bookHashMap(bookHashsize); +vector openingBook; #endif // BOOK_LEARNING #ifdef THREEFOLD_REPETITION -vector positions; +vector positions; #endif MillGameAi_ab::MillGameAi_ab() @@ -57,11 +58,11 @@ MillGameAi_ab::~MillGameAi_ab() rootNode = nullptr; } -MillGameAi_ab::depth_t MillGameAi_ab::changeDepth(depth_t originalDepth) +depth_t MillGameAi_ab::changeDepth(depth_t originalDepth) { depth_t newDepth = originalDepth; - if ((chessTemp.context.stage) & (MillGame::GAME_PLACING)) { + if ((chessTemp.context.stage) & (GAME_PLACING)) { #ifdef GAME_PLACING_DYNAMIC_DEPTH #ifdef DEAL_WITH_HORIZON_EFFECT #ifdef HASH_MAP_ENABLE @@ -89,7 +90,7 @@ MillGameAi_ab::depth_t MillGameAi_ab::changeDepth(depth_t originalDepth) #ifdef GAME_MOVING_FIXED_DEPTH // 走棋阶段将深度调整 - if ((chessTemp.context.stage) & (MillGame::GAME_MOVING)) { + if ((chessTemp.context.stage) & (GAME_MOVING)) { newDepth = GAME_MOVING_FIXED_DEPTH; } #endif /* GAME_MOVING_FIXED_DEPTH */ @@ -101,7 +102,7 @@ MillGameAi_ab::depth_t MillGameAi_ab::changeDepth(depth_t originalDepth) void MillGameAi_ab::buildRoot() { - rootNode = addNode(nullptr, 0, 0, 0, MillGame::NOBODY); + rootNode = addNode(nullptr, 0, 0, 0, PLAYER_NOBODY); } struct MillGameAi_ab::Node *MillGameAi_ab::addNode( @@ -109,7 +110,7 @@ struct MillGameAi_ab::Node *MillGameAi_ab::addNode( value_t value, move_t move, move_t bestMove, - enum MillGame::Player player + enum Player player ) { #ifdef MEMORY_POOL @@ -162,15 +163,15 @@ struct MillGameAi_ab::Node *MillGameAi_ab::addNode( char cmd[32] = { 0 }; if (move < 0) { - chessTemp.pos2rs(-move, r, s); + chessTemp.context.board.pos2rs(-move, r, s); sprintf(cmd, "-(%1u,%1u)", r, s); } else if (move & 0x7f00) { int r1, s1; - chessTemp.pos2rs(move >> 8, r1, s1); - chessTemp.pos2rs(move & 0x00ff, r, s); + chessTemp.context.board.pos2rs(move >> 8, r1, s1); + chessTemp.context.board.pos2rs(move & 0x00ff, r, s); sprintf(cmd, "(%1u,%1u)->(%1u,%1u)", r1, s1, r, s); } else { - chessTemp.pos2rs(move & 0x007f, r, s); + chessTemp.context.board.pos2rs(move & 0x007f, r, s); sprintf(cmd, "(%1u,%1u)", r, s); } @@ -182,7 +183,7 @@ struct MillGameAi_ab::Node *MillGameAi_ab::addNode( if (bestMove == 0 || move != bestMove) { #ifdef MILL_FIRST // 优先成三 - if (chessTemp.getStage() == MillGame::GAME_PLACING && move > 0 && chessTemp.isInMills(move, true)) { + if (chessTemp.getStage() == GAME_PLACING && move > 0 && chessTemp.context.board.isInMills(move, true)) { parent->children.insert(parent->children.begin(), newNode); } else { parent->children.push_back(newNode); @@ -239,7 +240,7 @@ void MillGameAi_ab::sortLegalMoves(Node *node) { // 这个函数对效率的影响很大,排序好的话,剪枝较早,节省时间,但不能在此函数耗费太多时间 - if (chessTemp.whosTurn() == MillGame::PLAYER1) { + if (chessTemp.whosTurn() == PLAYER1) { std::stable_sort(node->children.begin(), node->children.end(), nodeGreater); } else { std::stable_sort(node->children.begin(), node->children.end(), nodeLess); @@ -302,8 +303,8 @@ void MillGameAi_ab::setChess(const MillGame &chess) rootNode->pruned = false; #endif #ifdef DEBUG_AB_TREE - rootNode->action = MillGame::ACTION_NONE; - rootNode->stage = MillGame::GAME_NONE; + rootNode->action = ACTION_NONE; + rootNode->stage = GAME_NONE; rootNode->root = rootNode; #endif } @@ -321,7 +322,7 @@ int MillGameAi_ab::alphaBetaPruning(depth_t depth) chrono::steady_clock::time_point timeEnd; #ifdef BOOK_LEARNING - if (chess_.getStage() == MillGame::GAME_PLACING) + if (chess_.getStage() == GAME_PLACING) { if (chess_.context.nPiecesInHand_1 <= 10) { // 开局库只记录摆棋阶段最后的局面 @@ -336,8 +337,8 @@ int MillGameAi_ab::alphaBetaPruning(depth_t depth) #ifdef THREEFOLD_REPETITION static int nRepetition = 0; - if (chess_.getStage() == MillGame::GAME_MOVING) { - MillGame::hash_t hash = chess_.getHash(); + if (chess_.getStage() == GAME_MOVING) { + hash_t hash = chess_.getHash(); if (std::find(positions.begin(), positions.end(), hash) != positions.end()) { nRepetition++; @@ -350,7 +351,7 @@ int MillGameAi_ab::alphaBetaPruning(depth_t depth) } } - if (chess_.getStage() == MillGame::GAME_PLACING) { + if (chess_.getStage() == GAME_PLACING) { positions.clear(); } #endif // THREEFOLD_REPETITION @@ -389,7 +390,7 @@ int MillGameAi_ab::alphaBetaPruning(depth_t depth) return 0; } -MillGameAi_ab::value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t alpha, value_t beta, Node *node) +value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t alpha, value_t beta, Node *node) { // 评价值 value_t value; @@ -412,7 +413,7 @@ MillGameAi_ab::value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t al enum HashType hashf = hashfALPHA; // 获取哈希值 - MillGame::hash_t hash = chessTemp.getHash(); + hash_t hash = chessTemp.getHash(); #ifdef DEBUG_AB_TREE node->hash = hash; #endif @@ -443,7 +444,7 @@ MillGameAi_ab::value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t al #if 0 // TODO: 有必要针对深度微调 value? - if (chessContext->turn == MillGame::PLAYER1) + if (chessContext->turn == PLAYER1) node->value += hashValue.depth - depth; else node->value -= hashValue.depth - depth; @@ -470,7 +471,7 @@ MillGameAi_ab::value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t al #endif // DEBUG_AB_TREE // 搜索到叶子节点(决胜局面) // TODO: 对哈希进行特殊处理 - if (chessContext->stage == MillGame::GAME_OVER) { + if (chessContext->stage == GAME_OVER) { // 局面评估 node->value = Evaluation::getValue(chessTemp, chessContext, node); evaluatedNodeCount++; @@ -501,7 +502,7 @@ MillGameAi_ab::value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t al evaluatedNodeCount++; // 为争取速胜,value 值 +- 深度 (有必要?) - if (chessContext->turn == MillGame::PLAYER1) { + if (chessContext->turn == PLAYER1) { node->value += depth; } else { node->value -= depth; @@ -515,8 +516,8 @@ MillGameAi_ab::value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t al #ifdef BOOK_LEARNING // 检索开局库 - if (chessContext->stage == MillGame::GAME_PLACING && findBookHash(hash, hashValue)) { - if (chessContext->turn == MillGame::PLAYER2) { + if (chessContext->stage == GAME_PLACING && findBookHash(hash, hashValue)) { + if (chessContext->turn == PLAYER2) { // 是否需对后手扣分 // TODO: 先后手都处理 node->value += 1; } @@ -536,7 +537,7 @@ MillGameAi_ab::value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t al // 根据演算模型执行 MiniMax 检索,对先手,搜索 Max, 对后手,搜索 Min - minMax = chessTemp.whosTurn() == MillGame::PLAYER1 ? -INF_VALUE : INF_VALUE; + minMax = chessTemp.whosTurn() == PLAYER1 ? -INF_VALUE : INF_VALUE; for (auto child : node->children) { // 上下文入栈保存,以便后续撤销着法 @@ -567,7 +568,7 @@ MillGameAi_ab::value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t al chessTemp.context = contextStack.top(); contextStack.pop(); - if (chessTemp.whosTurn() == MillGame::PLAYER1) { + if (chessTemp.whosTurn() == PLAYER1) { // 为走棋一方的层, 局面对走棋的一方来说是以 α 为评价 // 取最大值 @@ -698,12 +699,12 @@ const char* MillGameAi_ab::bestMove() // 检查是否必败 - MillGame::Player whosTurn = chess_.whosTurn(); + Player whosTurn = chess_.whosTurn(); for (auto child : rootNode->children) { // TODO: 使用常量代替 - if (whosTurn == MillGame::PLAYER1 && child->value > -10000 || - whosTurn == MillGame::PLAYER2 && child->value < 10000) { + if ((whosTurn == PLAYER1 && child->value > -10000) || + (whosTurn == PLAYER2 && child->value < 10000)) { isMostLose = false; break; } @@ -711,9 +712,9 @@ const char* MillGameAi_ab::bestMove() // 自动认输 if (isMostLose) { - if (whosTurn == MillGame::PLAYER1) { + if (whosTurn == PLAYER1) { sprintf(cmdline, "Player1 give up!"); - } else if (whosTurn == MillGame::PLAYER2) { + } else if (whosTurn == PLAYER2) { sprintf(cmdline, "Player2 give up!"); } @@ -758,15 +759,15 @@ const char *MillGameAi_ab::move2string(move_t move) int r, s; if (move < 0) { - chessTemp.pos2rs(-move, r, s); + chessTemp.context.board.pos2rs(-move, r, s); sprintf(cmdline, "-(%1u,%1u)", r, s); } else if (move & 0x7f00) { int r1, s1; - chessTemp.pos2rs(move >> 8, r1, s1); - chessTemp.pos2rs(move & 0x00ff, r, s); + chessTemp.context.board.pos2rs(move >> 8, r1, s1); + chessTemp.context.board.pos2rs(move & 0x00ff, r, s); sprintf(cmdline, "(%1u,%1u)->(%1u,%1u)", r1, s1, r, s); } else { - chessTemp.pos2rs(move & 0x007f, r, s); + chessTemp.context.board.pos2rs(move & 0x007f, r, s); sprintf(cmdline, "(%1u,%1u)", r, s); } @@ -774,7 +775,7 @@ const char *MillGameAi_ab::move2string(move_t move) } #ifdef HASH_MAP_ENABLE -MillGameAi_ab::value_t MillGameAi_ab::probeHash(MillGame::hash_t hash, +value_t MillGameAi_ab::probeHash(hash_t hash, depth_t depth, value_t alpha, value_t beta, move_t &bestMove, HashType &type) { @@ -810,7 +811,7 @@ out: return valUNKNOWN; } -bool MillGameAi_ab::findHash(MillGame::hash_t hash, HashValue &hashValue) +bool MillGameAi_ab::findHash(hash_t hash, HashValue &hashValue) { return hashmap.find(hash, hashValue); @@ -839,7 +840,7 @@ bool MillGameAi_ab::findHash(MillGame::hash_t hash, HashValue &hashValue) #endif } -int MillGameAi_ab::recordHash(value_t value, depth_t depth, HashType type, MillGame::hash_t hash, move_t bestMove) +int MillGameAi_ab::recordHash(value_t value, depth_t depth, HashType type, hash_t hash, move_t bestMove) { // 同样深度或更深时替换 // 注意: 每走一步以前都必须把散列表中所有的标志项置为 hashfEMPTY @@ -876,12 +877,12 @@ void MillGameAi_ab::clearHashMap() #ifdef BOOK_LEARNING -bool MillGameAi_ab::findBookHash(MillGame::hash_t hash, HashValue &hashValue) +bool MillGameAi_ab::findBookHash(hash_t hash, HashValue &hashValue) { return bookHashMap.find(hash, hashValue); } -int MillGameAi_ab::recordBookHash(MillGame::hash_t hash, const HashValue &hashValue) +int MillGameAi_ab::recordBookHash(hash_t hash, const HashValue &hashValue) { //hashMapMutex.lock(); bookHashMap.insert(hash, hashValue); @@ -900,7 +901,7 @@ void MillGameAi_ab::clearBookHashMap() void MillGameAi_ab::recordOpeningBookToHashMap() { HashValue hashValue; - MillGame::hash_t hash = 0; + hash_t hash = 0; for (auto iter = openingBook.begin(); iter != openingBook.end(); ++iter) { diff --git a/src/ai/search.h b/src/ai/search.h index db9e04a4..52f3328a 100644 --- a/src/ai/search.h +++ b/src/ai/search.h @@ -52,11 +52,6 @@ using namespace CTSL; class MillGameAi_ab { public: - // 定义类型 - using depth_t = uint8_t; - using value_t = int16_t; - using move_t = MillGame::move_t; - // 定义一个节点结构体 struct Node { @@ -65,7 +60,7 @@ public: struct Node* parent {}; // 父节点 move_t move {}; // 着法的命令行指令,图上标示为节点前的连线 value_t value {}; // 节点的值 - enum MillGame::Player player; // 此着是谁下的 (目前仅调试用) + enum Player player; // 此着是谁下的 (目前仅调试用) #ifdef SORT_CONSIDER_PRUNED bool pruned {}; // 是否在此处剪枝 #endif @@ -80,8 +75,8 @@ public: bool isTimeout; // 是否遍历到此结点时因为超时而被迫退出 bool isLeaf; // 是否为叶子结点, 叶子结点是决胜局面 bool visited; // 是否在遍历时访问过 - MillGame::GameStage stage; // 摆棋阶段还是走棋阶段 - MillGame::Action action; // 动作状态 + GameStage stage; // 摆棋阶段还是走棋阶段 + Action action; // 动作状态 int nPiecesOnBoardDiff; // 场上棋子个数和对手的差值 int nPiecesInHandDiff; // 手中的棋子个数和对手的差值 int nPiecesNeedRemove; // 手中有多少可去的子,如对手有可去的子则为负数 @@ -91,7 +86,7 @@ public: bool isHash; // 是否从 Hash 读取 #endif /* HASH_MAP_ENABLE */ #if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) - MillGame::hash_t hash; // 哈希值 + hash_t hash; // 哈希值 #endif #endif /* DEBUG_AB_TREE */ }; @@ -148,8 +143,8 @@ public: static bool nodeGreater(const Node *first, const Node *second); #ifdef BOOK_LEARNING - bool findBookHash(MillGame::hash_t hash, HashValue &hashValue); - static int recordBookHash(MillGame::hash_t hash, const HashValue &hashValue); + bool findBookHash(hash_t hash, HashValue &hashValue); + static int recordBookHash(hash_t hash, const HashValue &hashValue); void clearBookHashMap(); static void recordOpeningBookToHashMap(); static void recordOpeningBookHashMapToFile(); @@ -160,7 +155,7 @@ public: /* TODO: Move to private or protected */ // 增加新节点 struct Node *addNode(Node *parent, value_t value, move_t move, move_t bestMove, - enum MillGame::Player player); + enum Player player); // 定义极大值 static const value_t INF_VALUE = 0x1 << 14; @@ -202,7 +197,7 @@ protected: #endif /* EVALUATE_ENABLE */ // Alpha-Beta剪枝算法 - MillGameAi_ab::value_t alphaBetaPruning(depth_t depth, value_t alpha, value_t beta, Node *node); + value_t alphaBetaPruning(depth_t depth, value_t alpha, value_t beta, Node *node); // 返回着法的命令行 const char *move2string(move_t move); @@ -212,11 +207,11 @@ protected: #ifdef HASH_MAP_ENABLE // 查找哈希表 - bool findHash(MillGame::hash_t hash, HashValue &hashValue); - value_t probeHash(MillGame::hash_t hash, depth_t depth, value_t alpha, value_t beta, move_t &bestMove, HashType &type); + bool findHash(hash_t hash, HashValue &hashValue); + value_t probeHash(hash_t hash, depth_t depth, value_t alpha, value_t beta, move_t &bestMove, HashType &type); // 插入哈希表 - int recordHash(value_t value, depth_t depth, HashType type, MillGame::hash_t hash, move_t bestMove); + int recordHash(value_t value, depth_t depth, HashType type, hash_t hash, move_t bestMove); #endif // HASH_MAP_ENABLE private: @@ -226,7 +221,7 @@ private: // 演算用的模型 MillGame chessTemp; - MillGame::ChessContext *chessContext {}; + ChessContext *chessContext {}; // hash 计算时,各种转换用的模型 MillGame chessTempShift; @@ -256,7 +251,7 @@ private: //#ifdef MEMORY_POOL // StackAlloc > contextStack; //#else - stack contextStack; + stack contextStack; //#endif // 标识,用于跳出剪枝算法,立即返回 @@ -268,11 +263,11 @@ private: }; #ifdef HASH_MAP_ENABLE -extern HashMap hashmap; +extern HashMap hashmap; #endif /* #ifdef HASH_MAP_ENABLE */ #ifdef THREEFOLD_REPETITION -extern vector positions; +extern vector positions; #endif #endif diff --git a/src/ai/zobrist.h b/src/ai/zobrist.h index 7fc73190..f49fb0fd 100644 --- a/src/ai/zobrist.h +++ b/src/ai/zobrist.h @@ -22,7 +22,7 @@ #include "millgame.h" -static const MillGame::hash_t zobrist0[MillGame::N_POINTS][MillGame::POINT_TYPE_COUNT] = { +static const hash_t zobrist0[Board::N_POINTS][POINT_TYPE_COUNT] = { #ifdef HASH_MAP_CUTDOWN {0x4E421A00, 0x3962FF00, 0x6DB6EE00, 0x219AE100}, {0x1F3DE200, 0xD9AACB00, 0xD5173300, 0xD3F9EA00}, diff --git a/src/base/hashmap.h b/src/base/hashmap.h index 69519967..c5934197 100644 --- a/src/base/hashmap.h +++ b/src/base/hashmap.h @@ -123,7 +123,7 @@ namespace CTSL //Concurrent Thread Safe Library #ifdef DISABLE_HASHBUCKET QFile file(filename); file.open(QIODevice::WriteOnly); - file.write(static_cast(hashTable), sizeof(HashNode) * hashSize); + file.write((char *)(hashTable), sizeof(HashNode) * hashSize); file.close(); #endif } @@ -134,7 +134,7 @@ namespace CTSL //Concurrent Thread Safe Library #ifdef DISABLE_HASHBUCKET QFile file(filename); file.open(QIODevice::ReadOnly); - file.read(static_cast(hashTable), sizeof(HashNode) * hashSize); + file.read((char *)(hashTable), sizeof(HashNode) * hashSize); file.close(); #endif } diff --git a/src/base/thread.cpp b/src/base/thread.cpp index e8f215a1..c08740af 100644 --- a/src/base/thread.cpp +++ b/src/base/thread.cpp @@ -76,7 +76,7 @@ void AiThread::setAi(const MillGame &chess) mutex.unlock(); } -void AiThread::setAi(const MillGame &chess, MillGameAi_ab::depth_t depth, int time) +void AiThread::setAi(const MillGame &chess, depth_t depth, int time) { mutex.lock(); this->chess_ = &chess; diff --git a/src/base/thread.h b/src/base/thread.h index de8c5d38..13caf8dd 100644 --- a/src/base/thread.h +++ b/src/base/thread.h @@ -55,7 +55,7 @@ protected: public: // AI设置 void setAi(const MillGame &chess); - void setAi(const MillGame &chess, MillGameAi_ab::depth_t depth, int time); + void setAi(const MillGame &chess, depth_t depth, int time); Server *getServer() { @@ -68,7 +68,7 @@ public: } // 深度和限时 - void getDepthTime(MillGameAi_ab::depth_t &depth, int &time) + void getDepthTime(depth_t &depth, int &time) { depth = aiDepth; time = aiTime; @@ -113,7 +113,7 @@ private: MillGameAi_ab ai_ab; // AI的层数 - MillGameAi_ab::depth_t aiDepth; + depth_t aiDepth; // AI的限时 int aiTime; diff --git a/src/game/board.cpp b/src/game/board.cpp new file mode 100644 index 00000000..24f1bb9d --- /dev/null +++ b/src/game/board.cpp @@ -0,0 +1,992 @@ +/***************************************************************************** + * Copyright (C) 2018-2019 MillGame authors + * + * Authors: liuweilhy + * Calcitem + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include "board.h" +#include "movegen.h" + + // 名义上是个数组,实际上相当于一个判断是否在棋盘上的函数 +const int Board::onBoard[N_POINTS] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +// 成三表 +int Board::millTable[N_POINTS][N_DIRECTIONS][N_RINGS - 1] = { {{0}} }; + +Board::Board() +{ +} + +Board::~Board() +{ + if (!millList.empty()) { + millList.clear(); + } +} + +Board &Board::operator= (const Board &other) +{ + if (this == &other) + return *this; + + memcpy(this->board_, other.board_, sizeof(this->board_)); + + if (!millList.empty()) { + millList.clear(); + } + + if (!other.millList.empty()) { + for (auto i : other.millList) { + millList.push_back(i); + } + } + + return *this; +} + +void Board::createMillTable(const Rule ¤tRule) +{ +#ifdef CONST_MILL_TABLE + const int millTable_noObliqueLine[Board::N_POINTS][Board::N_DIRECTIONS][2] = { + /* 0 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 1 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 2 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 3 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 4 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 5 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 6 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 7 */ {{0, 0}, {0, 0}, {0, 0}}, + + /* 8 */ {{16, 24}, {9, 15}, {0, 0}}, + /* 9 */ {{0, 0}, {15, 8}, {10, 11}}, + /* 10 */ {{18, 26}, {11, 9}, {0, 0}}, + /* 11 */ {{0, 0}, {9, 10}, {12, 13}}, + /* 12 */ {{20, 28}, {13, 11}, {0, 0}}, + /* 13 */ {{0, 0}, {11, 12}, {14, 15}}, + /* 14 */ {{22, 30}, {15, 13}, {0, 0}}, + /* 15 */ {{0, 0}, {13, 14}, {8, 9}}, + + /* 16 */ {{8, 24}, {17, 23}, {0, 0}}, + /* 17 */ {{0, 0}, {23, 16}, {18, 19}}, + /* 18 */ {{10, 26}, {19, 17}, {0, 0}}, + /* 19 */ {{0, 0}, {17, 18}, {20, 21}}, + /* 20 */ {{12, 28}, {21, 19}, {0, 0}}, + /* 21 */ {{0, 0}, {19, 20}, {22, 23}}, + /* 22 */ {{14, 30}, {23, 21}, {0, 0}}, + /* 23 */ {{0, 0}, {21, 22}, {16, 17}}, + + /* 24 */ {{8, 16}, {25, 31}, {0, 0}}, + /* 25 */ {{0, 0}, {31, 24}, {26, 27}}, + /* 26 */ {{10, 18}, {27, 25}, {0, 0}}, + /* 27 */ {{0, 0}, {25, 26}, {28, 29}}, + /* 28 */ {{12, 20}, {29, 27}, {0, 0}}, + /* 29 */ {{0, 0}, {27, 28}, {30, 31}}, + /* 30 */ {{14, 22}, {31, 29}, {0, 0}}, + /* 31 */ {{0, 0}, {29, 30}, {24, 25}}, + + /* 32 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 33 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 34 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 35 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 36 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 37 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 38 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 39 */ {{0, 0}, {0, 0}, {0, 0}} + }; + + const int millTable_hasObliqueLines[Board::N_POINTS][Board::N_DIRECTIONS][2] = { + /* 0 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 1 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 2 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 3 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 4 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 5 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 6 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 7 */ {{0, 0}, {0, 0}, {0, 0}}, + + /* 8 */ {{16, 24}, {9, 15}, {0, 0}}, + /* 9 */ {{17, 25}, {15, 8}, {10, 11}}, + /* 10 */ {{18, 26}, {11, 9}, {0, 0}}, + /* 11 */ {{19, 27}, {9, 10}, {12, 13}}, + /* 12 */ {{20, 28}, {13, 11}, {0, 0}}, + /* 13 */ {{21, 29}, {11, 12}, {14, 15}}, + /* 14 */ {{22, 30}, {15, 13}, {0, 0}}, + /* 15 */ {{23, 31}, {13, 14}, {8, 9}}, + + /* 16 */ {{8, 24}, {17, 23}, {0, 0}}, + /* 17 */ {{9, 25}, {23, 16}, {18, 19}}, + /* 18 */ {{10, 26}, {19, 17}, {0, 0}}, + /* 19 */ {{11, 27}, {17, 18}, {20, 21}}, + /* 20 */ {{12, 28}, {21, 19}, {0, 0}}, + /* 21 */ {{13, 29}, {19, 20}, {22, 23}}, + /* 22 */ {{14, 30}, {23, 21}, {0, 0}}, + /* 23 */ {{15, 31}, {21, 22}, {16, 17}}, + + /* 24 */ {{8, 16}, {25, 31}, {0, 0}}, + /* 25 */ {{9, 17}, {31, 24}, {26, 27}}, + /* 26 */ {{10, 18}, {27, 25}, {0, 0}}, + /* 27 */ {{11, 19}, {25, 26}, {28, 29}}, + /* 28 */ {{12, 20}, {29, 27}, {0, 0}}, + /* 29 */ {{13, 21}, {27, 28}, {30, 31}}, + /* 30 */ {{14, 22}, {31, 29}, {0, 0}}, + /* 31 */ {{15, 23}, {29, 30}, {24, 25}}, + + /* 32 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 33 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 34 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 35 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 36 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 37 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 38 */ {{0, 0}, {0, 0}, {0, 0}}, + /* 39 */ {{0, 0}, {0, 0}, {0, 0}} + }; + + if (currentRule.hasObliqueLines) { + memcpy(millTable, millTable_hasObliqueLines, sizeof(millTable)); + } else { + memcpy(millTable, millTable_noObliqueLine, sizeof(millTable)); + } +#else /* CONST_MILL_TABLE */ + for (int i = 0; i < N_SEATS; i++) { + // 内外方向的“成三” + // 如果是0、2、4、6位(偶数位)或是有斜线 + if (!(i & 1) || this->currentRule.hasObliqueLines) { + millTable[1 * N_SEATS + i][0][0] = 2 * N_SEATS + i; + millTable[1 * N_SEATS + i][0][1] = 3 * N_SEATS + i; + + millTable[2 * N_SEATS + i][0][0] = 1 * N_SEATS + i; + millTable[2 * N_SEATS + i][0][1] = 3 * N_SEATS + i; + + millTable[3 * N_SEATS + i][0][0] = 1 * N_SEATS + i; + millTable[3 * N_SEATS + i][0][1] = 2 * N_SEATS + i; + } + // 对于无斜线情况下的1、3、5、7位(奇数位) + else { + // 置空该组“成三” + millTable[1 * N_SEATS + i][0][0] = 0; + millTable[1 * N_SEATS + i][0][1] = 0; + + millTable[2 * N_SEATS + i][0][0] = 0; + millTable[2 * N_SEATS + i][0][1] = 0; + + millTable[3 * N_SEATS + i][0][0] = 0; + millTable[3 * N_SEATS + i][0][1] = 0; + } + + // 当前圈上的“成三” + // 如果是0、2、4、6位 + if (!(i & 1)) { + millTable[1 * N_SEATS + i][1][0] = 1 * N_SEATS + (i + 1) % N_SEATS; + millTable[1 * N_SEATS + i][1][1] = 1 * N_SEATS + (i + N_SEATS - 1) % N_SEATS; + + millTable[2 * N_SEATS + i][1][0] = 2 * N_SEATS + (i + 1) % N_SEATS; + millTable[2 * N_SEATS + i][1][1] = 2 * N_SEATS + (i + N_SEATS - 1) % N_SEATS; + + millTable[3 * N_SEATS + i][1][0] = 3 * N_SEATS + (i + 1) % N_SEATS; + millTable[3 * N_SEATS + i][1][1] = 3 * N_SEATS + (i + N_SEATS - 1) % N_SEATS; + // 置空另一组“成三” + millTable[1 * N_SEATS + i][2][0] = 0; + millTable[1 * N_SEATS + i][2][1] = 0; + + millTable[2 * N_SEATS + i][2][0] = 0; + millTable[2 * N_SEATS + i][2][1] = 0; + + millTable[3 * N_SEATS + i][2][0] = 0; + millTable[3 * N_SEATS + i][2][1] = 0; + } + // 对于1、3、5、7位(奇数位) + else { + // 当前圈上逆时针的“成三” + millTable[1 * N_SEATS + i][1][0] = 1 * N_SEATS + (i + N_SEATS - 2) % N_SEATS; + millTable[1 * N_SEATS + i][1][1] = 1 * N_SEATS + (i + N_SEATS - 1) % N_SEATS; + + millTable[2 * N_SEATS + i][1][0] = 2 * N_SEATS + (i + N_SEATS - 2) % N_SEATS; + millTable[2 * N_SEATS + i][1][1] = 2 * N_SEATS + (i + N_SEATS - 1) % N_SEATS; + + millTable[3 * N_SEATS + i][1][0] = 3 * N_SEATS + (i + N_SEATS - 2) % N_SEATS; + millTable[3 * N_SEATS + i][1][1] = 3 * N_SEATS + (i + N_SEATS - 1) % N_SEATS; + + // 当前圈上顺时针的“成三” + millTable[1 * N_SEATS + i][2][0] = 1 * N_SEATS + (i + 1) % N_SEATS; + millTable[1 * N_SEATS + i][2][1] = 1 * N_SEATS + (i + 2) % N_SEATS; + + millTable[2 * N_SEATS + i][2][0] = 2 * N_SEATS + (i + 1) % N_SEATS; + millTable[2 * N_SEATS + i][2][1] = 2 * N_SEATS + (i + 2) % N_SEATS; + + millTable[3 * N_SEATS + i][2][0] = 3 * N_SEATS + (i + 1) % N_SEATS; + millTable[3 * N_SEATS + i][2][1] = 3 * N_SEATS + (i + 2) % N_SEATS; + } + } +#endif /* CONST_MILL_TABLE */ + +#if 0 + for (int i = 0; i < N_POINTS; i++) { + printf("/* %d */ {", i); + for (int j = 0; j < N_DIRECTIONS; j++) { + printf("{"); + for (int k = 0; k < 2; k++) { + if (k == 0) { + printf("%d, ", millTable[i][j][k]); + } else { + printf("%d", millTable[i][j][k]); + } + + } + if (j == 2) + printf("}"); + else + printf("}, "); + } + printf("},\n"); + } + + printf("======== millTable End =========\n"); + +#endif +} + +void Board::pos2rs(const int pos, int &r, int &s) +{ + //r = pos / N_SEATS; + //s = pos % N_SEATS + 1; + r = pos >> 3; + s = (pos & 0x07) + 1; +} + +int Board::rs2Pos(int r, int s) +{ + if (r < 1 || r > N_RINGS || s < 1 || s > N_SEATS) + return 0; + + return r * N_SEATS + s - 1; +} + + +int Board::isInMills(int pos, bool test) +{ + int n = 0; + int pos1, pos2; + int m = test ? INT32_MAX : board_[pos] & '\x30'; + + for (int i = 0; i < 3; i++) { + pos1 = millTable[pos][i][0]; + pos2 = millTable[pos][i][1]; + if (m & board_[pos1] & board_[pos2]) + n++; + } + + return n; +} + +int Board::addMills(const Rule ¤tRule, int pos) +{ + // 成三用一个64位整数了,规则如下 + // 0x 00 00 00 00 00 00 00 00 + // unused unused piece1 pos1 piece2 pos2 piece3 pos3 + // piece1、piece2、piece3按照序号从小到大顺序排放 + uint64_t mill = 0; + int n = 0; + int p[3], min, temp; + char m = board_[pos] & '\x30'; + + for (int i = 0; i < 3; i++) { + p[0] = pos; + p[1] = millTable[pos][i][0]; + p[2] = millTable[pos][i][1]; + + // 如果没有成三 + if (!(m & board_[p[1]] & board_[p[2]])) { + continue; + } + + // 如果成三 + + // 排序 + for (int j = 0; j < 2; j++) { + min = j; + + for (int k = j + 1; k < 3; k++) { + if (p[min] > p[k]) + min = k; + } + + if (min == j) { + continue; + } + + temp = p[min]; + p[min] = p[j]; + p[j] = temp; + } + + // 成三 + mill = (static_cast(board_[p[0]]) << 40) + + (static_cast(p[0]) << 32) + + (static_cast(board_[p[1]]) << 24) + + (static_cast(p[1]) << 16) + + (static_cast(board_[p[2]]) << 8) + + static_cast(p[2]); + + // 如果允许相同三连反复去子 + if (currentRule.allowRemovePiecesRepeatedly) { + n++; + continue; + } + + // 如果不允许相同三连反复去子 + + // 迭代器 + auto iter = millList.begin(); + + // 遍历 + for (iter = millList.begin(); iter != millList.end(); iter++) { + if (mill == *iter) + break; + } + + // 如果没找到历史项 + if (iter == millList.end()) { + n++; + millList.push_back(mill); + } + } + + return n; +} + +bool Board::isAllInMills(char ch) +{ + for (int i = POS_BEGIN; i < POS_END; i++) { + if (board_[i] & ch) { + if (!isInMills(i)) { + return false; + } + } + } + + return true; +} + +bool Board::isAllInMills(enum Player player) +{ + char ch = 0x00; + + if (player == PLAYER1) + ch = 0x10; + else if (player == PLAYER2) + ch = 0x20; + else + return true; + + return isAllInMills(ch); +} + +// 判断玩家的棋子周围有几个空位 +int Board::getSurroundedEmptyPosCount(enum Player turn, const Rule ¤tRule, int nPiecesOnBoard_1, int nPiecesOnBoard_2, int pos, bool includeFobidden) +{ + int count = 0; + + if ((turn == PLAYER1 && + (nPiecesOnBoard_1 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces)) || + (turn == PLAYER2 && + (nPiecesOnBoard_2 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces))) { + int d, movePos; + for (d = 0; d < N_MOVE_DIRECTIONS; d++) { + movePos = MoveList::moveTable[pos][d]; + if (movePos) { + if (board_[movePos] == 0x00 || + (includeFobidden && board_[movePos] == 0x0F)) { + count++; + } + } + } + } + + return count; +} + +// 判断玩家的棋子是否被围 +bool Board::isSurrounded(enum Player turn, const Rule ¤tRule, int nPiecesOnBoard_1, int nPiecesOnBoard_2, int pos) +{ + // 判断pos处的棋子是否被“闷” + if ((turn == PLAYER1 && + (nPiecesOnBoard_1 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces)) || + (turn == PLAYER2 && + (nPiecesOnBoard_2 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces))) { + int i, movePos; + for (i = 0; i < 4; i++) { + movePos = MoveList::moveTable[pos][i]; + if (movePos && !board_[movePos]) + break; + } + // 被围住 + if (i == 4) + return true; + } + // 没被围住 + return false; +} + +bool Board::isAllSurrounded(enum Player turn, const Rule ¤tRule, int nPiecesOnBoard_1, int nPiecesOnBoard_2, char ch) +{ + // 如果摆满 + if (nPiecesOnBoard_1 + nPiecesOnBoard_2 >= N_SEATS * N_RINGS) + return true; + + // 判断是否可以飞子 + if ((turn == PLAYER1 && + (nPiecesOnBoard_1 <= currentRule.nPiecesAtLeast && currentRule.allowFlyWhenRemainThreePieces)) || + (turn == PLAYER2 && + (nPiecesOnBoard_2 <= currentRule.nPiecesAtLeast && currentRule.allowFlyWhenRemainThreePieces))) { + return false; + } + + // 查询整个棋盘 + int movePos; + for (int i = 1; i < N_SEATS * (N_RINGS + 1); i++) { + if (!(ch & board_[i])) { + continue; + } + + for (int d = 0; d < N_MOVE_DIRECTIONS; d++) { + movePos = MoveList::moveTable[i][d]; + if (movePos && !board_[movePos]) + return false; + } + } + + return true; +} + +// 判断玩家的棋子是否全部被围 +bool Board::isAllSurrounded(enum Player turn, const Rule ¤tRule, int nPiecesOnBoard_1, int nPiecesOnBoard_2, enum Player ply) +{ + char t = '\x30'; + + if (ply == PLAYER1) + t &= '\x10'; + else if (ply == PLAYER2) + t &= '\x20'; + + return isAllSurrounded(turn, currentRule, nPiecesOnBoard_1, nPiecesOnBoard_2, t); +} + +enum Player Board::getWhosPiece(int r, int s) +{ + int pos = rs2Pos(r, s); + + if (board_[pos] & '\x10') + return PLAYER1; + + if (board_[pos] & '\x20') + return PLAYER2; + + return PLAYER_NOBODY; +} + +// Unused +bool Board::getPieceRS(const Player &player, const int &number, int &r, int &s, struct Rule ¤tRule) +{ + int piece; + + if (player == PLAYER1) { + piece = 0x10; + } else if (player == PLAYER2) { + piece = 0x20; + } else { + return false; + } + + if (number > 0 && number <= currentRule.nTotalPiecesEachSide) + piece &= number; + else + return false; + + for (int i = POS_BEGIN; i < POS_END; i++) { + if (board_[i] == piece) { + pos2rs(i, r, s); + return true; + } + } + + return false; +} + +// 获取当前棋子 +bool Board::getCurrentPiece(Player &player, int &number, int currentPos) +{ + if (!onBoard[currentPos]) + return false; + + int p = board_[currentPos]; + + if (p & 0x10) { + player = PLAYER1; + number = p - 0x10; + } else if (p & 0x20) { + player = PLAYER2; + number = p - 0x20; + } else { + return false; + } + + return true; +} + +void Board::mirror(list &cmdlist, char* cmdline, int32_t move_, struct Rule ¤tRule, int currentPos, bool cmdChange /*= true*/) +{ + int ch; + int r, s; + int i; + + for (r = 1; r <= N_RINGS; r++) { + for (s = 1; s < N_SEATS / 2; s++) { + ch = board_[r * N_SEATS + s]; + board_[r * N_SEATS + s] = board_[(r + 1) * N_SEATS - s]; + //updateHash(i * N_SEATS + j); + board_[(r + 1) * N_SEATS - s] = ch; + //updateHash((i + 1) * N_SEATS - j); + } + } + + uint64_t llp[3] = { 0 }; + + if (move_ < 0) { + r = (-move_) / N_SEATS; + s = (-move_) % N_SEATS; + s = (N_SEATS - s) % N_SEATS; + move_ = -(r * N_SEATS + s); + } else { + llp[0] = static_cast(move_ >> 8); + llp[1] = move_ & 0x00ff; + + for (i = 0; i < 2; i++) { + r = static_cast(llp[i]) / N_SEATS; + s = static_cast(llp[i]) % N_SEATS; + s = (N_SEATS - s) % N_SEATS; + llp[i] = (static_cast(r) * N_SEATS + s); + } + + move_ = static_cast(((llp[0] << 8) | llp[1])); + } + + if (currentPos != 0) { + r = currentPos / N_SEATS; + s = currentPos % N_SEATS; + s = (N_SEATS - s) % N_SEATS; + currentPos = r * N_SEATS + s; + } + + if (currentRule.allowRemovePiecesRepeatedly) { + for (auto &mill : millList) { + llp[0] = (mill & 0x000000ff00000000) >> 32; + llp[1] = (mill & 0x0000000000ff0000) >> 16; + llp[2] = (mill & 0x00000000000000ff); + + for (i = 0; i < 3; i++) { + r = static_cast(llp[i]) / N_SEATS; + s = static_cast(llp[i]) % N_SEATS; + s = (N_SEATS - s) % N_SEATS; + llp[i] = static_cast(r * N_SEATS + s); + } + + mill &= 0xffffff00ff00ff00; + mill |= (llp[0] << 32) | (llp[1] << 16) | llp[2]; + } + } + + // 命令行解析 + if (cmdChange) { + int r1, s1, r2, s2; + int args = 0; + int mm = 0, ss = 0; + + args = sscanf(cmdline, "(%1u,%1u)->(%1u,%1u) %2u:%2u", &r1, &s1, &r2, &s2, &mm, &ss); + if (args >= 4) { + s1 = (N_SEATS - s1 + 1) % N_SEATS; + s2 = (N_SEATS - s2 + 1) % N_SEATS; + cmdline[3] = '1' + static_cast(s1); + cmdline[10] = '1' + static_cast(s2); + } else { + args = sscanf(cmdline, "-(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); + if (args >= 2) { + s1 = (N_SEATS - s1 + 1) % N_SEATS; + cmdline[4] = '1' + static_cast(s1); + } else { + args = sscanf(cmdline, "(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); + if (args >= 2) { + s1 = (N_SEATS - s1 + 1) % N_SEATS; + cmdline[3] = '1' + static_cast(s1); + } + } + } + + for (auto &iter : cmdlist) { + args = sscanf(iter.c_str(), "(%1u,%1u)->(%1u,%1u) %2u:%2u", &r1, &s1, &r2, &s2, &mm, &ss); + if (args >= 4) { + s1 = (N_SEATS - s1 + 1) % N_SEATS; + s2 = (N_SEATS - s2 + 1) % N_SEATS; + iter[3] = '1' + static_cast(s1); + iter[10] = '1' + static_cast(s2); + } else { + args = sscanf(iter.c_str(), "-(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); + if (args >= 2) { + s1 = (N_SEATS - s1 + 1) % N_SEATS; + iter[4] = '1' + static_cast(s1); + } else { + args = sscanf(iter.c_str(), "(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); + if (args >= 2) { + s1 = (N_SEATS - s1 + 1) % N_SEATS; + iter[3] = '1' + static_cast(s1); + } + } + } + } + } +} + +void Board::turn(list &cmdlist, char *cmdline, int32_t move_, const Rule ¤tRule, int currentPos, bool cmdChange /*= true*/) +{ + int ch; + int r, s; + int i; + + for (s = 0; s < N_SEATS; s++) { + ch = board_[N_SEATS + s]; + board_[N_SEATS + s] = board_[N_SEATS * N_RINGS + s]; + //updateHash(N_SEATS + s); + board_[N_SEATS * N_RINGS + s] = ch; + //updateHash(N_SEATS * N_RINGS + s); + } + + uint64_t llp[3] = { 0 }; + + if (move_ < 0) { + r = (-move_) / N_SEATS; + s = (-move_) % N_SEATS; + + if (r == 1) + r = N_RINGS; + else if (r == N_RINGS) + r = 1; + + move_ = -(r * N_SEATS + s); + } else { + llp[0] = static_cast(move_ >> 8); + llp[1] = move_ & 0x00ff; + + for (i = 0; i < 2; i++) { + r = static_cast(llp[i]) / N_SEATS; + s = static_cast(llp[i]) % N_SEATS; + + if (r == 1) + r = N_RINGS; + else if (r == N_RINGS) + r = 1; + + llp[i] = static_cast(r * N_SEATS + s); + } + + move_ = static_cast(((llp[0] << 8) | llp[1])); + } + + if (currentPos != 0) { + r = currentPos / N_SEATS; + s = currentPos % N_SEATS; + + if (r == 1) + r = N_RINGS; + else if (r == N_RINGS) + r = 1; + + currentPos = r * N_SEATS + s; + } + + if (currentRule.allowRemovePiecesRepeatedly) { + for (auto &mill : millList) { + llp[0] = (mill & 0x000000ff00000000) >> 32; + llp[1] = (mill & 0x0000000000ff0000) >> 16; + llp[2] = (mill & 0x00000000000000ff); + + for (i = 0; i < 3; i++) { + r = static_cast(llp[i]) / N_SEATS; + s = static_cast(llp[i]) % N_SEATS; + + if (r == 1) + r = N_RINGS; + else if (r == N_RINGS) + r = 1; + + llp[i] = static_cast(r * N_SEATS + s); + } + + mill &= 0xffffff00ff00ff00; + mill |= (llp[0] << 32) | (llp[1] << 16) | llp[2]; + } + } + + // 命令行解析 + if (cmdChange) { + int r1, s1, r2, s2; + int args = 0; + int mm = 0, ss = 0; + + args = sscanf(cmdline, "(%1u,%1u)->(%1u,%1u) %2u:%2u", + &r1, &s1, &r2, &s2, &mm, &ss); + + if (args >= 4) { + if (r1 == 1) + r1 = N_RINGS; + else if (r1 == N_RINGS) + r1 = 1; + + if (r2 == 1) + r2 = N_RINGS; + else if (r2 == N_RINGS) + r2 = 1; + + cmdline[1] = '0' + static_cast(r1); + cmdline[8] = '0' + static_cast(r2); + } else { + args = sscanf(cmdline, "-(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); + if (args >= 2) { + if (r1 == 1) + r1 = N_RINGS; + else if (r1 == N_RINGS) + r1 = 1; + cmdline[2] = '0' + static_cast(r1); + } else { + args = sscanf(cmdline, "(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); + if (args >= 2) { + if (r1 == 1) + r1 = N_RINGS; + else if (r1 == N_RINGS) + r1 = 1; + cmdline[1] = '0' + static_cast(r1); + } + } + } + + for (auto &iter : cmdlist) { + args = sscanf(iter.c_str(), + "(%1u,%1u)->(%1u,%1u) %2u:%2u", + &r1, &s1, &r2, &s2, &mm, &ss); + + if (args >= 4) { + if (r1 == 1) + r1 = N_RINGS; + else if (r1 == N_RINGS) + r1 = 1; + + if (r2 == 1) + r2 = N_RINGS; + else if (r2 == N_RINGS) + r2 = 1; + + iter[1] = '0' + static_cast(r1); + iter[8] = '0' + static_cast(r2); + } else { + args = sscanf(iter.c_str(), "-(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); + if (args >= 2) { + if (r1 == 1) + r1 = N_RINGS; + else if (r1 == N_RINGS) + r1 = 1; + + iter[2] = '0' + static_cast(r1); + } else { + args = sscanf(iter.c_str(), "(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); + if (args >= 2) { + if (r1 == 1) + r1 = N_RINGS; + else if (r1 == N_RINGS) + r1 = 1; + + iter[1] = '0' + static_cast(r1); + } + } + } + } + } +} + +void Board::rotate(int degrees, list &cmdlist, char *cmdline, int32_t move_, const Rule ¤tRule, int currentPos, bool cmdChange /*= true*/) +{ + // 将degrees转化为0~359之间的数 + degrees = degrees % 360; + + if (degrees < 0) + degrees += 360; + + if (degrees == 0 || degrees % 90) + return; + + degrees /= 45; + + int ch1, ch2; + int r, s; + int i; + + if (degrees == 2) { + for (r = 1; r <= N_RINGS; r++) { + ch1 = board_[r * N_SEATS]; + ch2 = board_[r * N_SEATS + 1]; + + for (s = 0; s < N_SEATS - 2; s++) { + board_[r * N_SEATS + s] = board_[r * N_SEATS + s + 2]; + } + + board_[r * N_SEATS + 6] = ch1; + //updateHash(i * N_SEATS + 6); + board_[r * N_SEATS + 7] = ch2; + //updateHash(i * N_SEATS + 7); + } + } else if (degrees == 6) { + for (r = 1; r <= N_RINGS; r++) { + ch1 = board_[r * N_SEATS + 7]; + ch2 = board_[r * N_SEATS + 6]; + + for (s = N_SEATS - 1; s >= 2; s--) { + board_[r * N_SEATS + s] = board_[r * N_SEATS + s - 2]; + //updateHash(i * N_SEATS + j); + } + + board_[r * N_SEATS + 1] = ch1; + //updateHash(i * N_SEATS + 1); + board_[r * N_SEATS] = ch2; + //updateHash(i * N_SEATS); + } + } else if (degrees == 4) { + for (r = 1; r <= N_RINGS; r++) { + for (s = 0; s < N_SEATS / 2; s++) { + ch1 = board_[r * N_SEATS + s]; + board_[r * N_SEATS + s] = board_[r * N_SEATS + s + 4]; + //updateHash(i * N_SEATS + j); + board_[r * N_SEATS + s + 4] = ch1; + //updateHash(i * N_SEATS + j + 4); + } + } + } else { + return; + } + + uint64_t llp[3] = { 0 }; + + if (move_ < 0) { + r = (-move_) / N_SEATS; + s = (-move_) % N_SEATS; + s = (s + N_SEATS - degrees) % N_SEATS; + move_ = -(r * N_SEATS + s); + } else { + llp[0] = static_cast(move_ >> 8); + llp[1] = move_ & 0x00ff; + r = static_cast(llp[0]) / N_SEATS; + s = static_cast(llp[0]) % N_SEATS; + s = (s + N_SEATS - degrees) % N_SEATS; + llp[0] = static_cast(r * N_SEATS + s); + r = static_cast(llp[1]) / N_SEATS; + s = static_cast(llp[1]) % N_SEATS; + s = (s + N_SEATS - degrees) % N_SEATS; + llp[1] = static_cast(r * N_SEATS + s); + move_ = static_cast(((llp[0] << 8) | llp[1])); + } + + if (currentPos != 0) { + r = currentPos / N_SEATS; + s = currentPos % N_SEATS; + s = (s + N_SEATS - degrees) % N_SEATS; + currentPos = r * N_SEATS + s; + } + + if (currentRule.allowRemovePiecesRepeatedly) { + for (auto &mill : millList) { + llp[0] = (mill & 0x000000ff00000000) >> 32; + llp[1] = (mill & 0x0000000000ff0000) >> 16; + llp[2] = (mill & 0x00000000000000ff); + + for (i = 0; i < 3; i++) { + r = static_cast(llp[i]) / N_SEATS; + s = static_cast(llp[i]) % N_SEATS; + s = (s + N_SEATS - degrees) % N_SEATS; + llp[i] = static_cast(r * N_SEATS + s); + } + + mill &= 0xffffff00ff00ff00; + mill |= (llp[0] << 32) | (llp[1] << 16) | llp[2]; + } + } + + // 命令行解析 + if (cmdChange) { + int r1, s1, r2, s2; + int args = 0; + int mm = 0, ss = 0; + + args = sscanf(cmdline, "(%1u,%1u)->(%1u,%1u) %2u:%2u", &r1, &s1, &r2, &s2, &mm, &ss); + if (args >= 4) { + s1 = (s1 - 1 + N_SEATS - degrees) % N_SEATS; + s2 = (s2 - 1 + N_SEATS - degrees) % N_SEATS; + cmdline[3] = '1' + static_cast(s1); + cmdline[10] = '1' + static_cast(s2); + } else { + args = sscanf(cmdline, "-(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); + + if (args >= 2) { + s1 = (s1 - 1 + N_SEATS - degrees) % N_SEATS; + cmdline[4] = '1' + static_cast(s1); + } else { + args = sscanf(cmdline, "(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); + + if (args >= 2) { + s1 = (s1 - 1 + N_SEATS - degrees) % N_SEATS; + cmdline[3] = '1' + static_cast(s1); + } + } + } + + for (auto &iter : cmdlist) { + args = sscanf(iter.c_str(), "(%1u,%1u)->(%1u,%1u) %2u:%2u", &r1, &s1, &r2, &s2, &mm, &ss); + + if (args >= 4) { + s1 = (s1 - 1 + N_SEATS - degrees) % N_SEATS; + s2 = (s2 - 1 + N_SEATS - degrees) % N_SEATS; + iter[3] = '1' + static_cast(s1); + iter[10] = '1' + static_cast(s2); + } else { + args = sscanf(iter.c_str(), "-(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); + + if (args >= 2) { + s1 = (s1 - 1 + N_SEATS - degrees) % N_SEATS; + iter[4] = '1' + static_cast(s1); + } else { + args = sscanf(iter.c_str(), "(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); + if (args >= 2) { + s1 = (s1 - 1 + N_SEATS - degrees) % N_SEATS; + iter[3] = '1' + static_cast(s1); + } + } + } + } + } +} diff --git a/src/game/board.h b/src/game/board.h new file mode 100644 index 00000000..62a5fb02 --- /dev/null +++ b/src/game/board.h @@ -0,0 +1,145 @@ +/***************************************************************************** + * Copyright (C) 2018-2019 MillGame authors + * + * Authors: liuweilhy + * Calcitem + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef BOARD_H +#define BOARD_H + +#include + +#include "config.h" +#include "rule.h" +#include "types.h" + +using namespace std; + +class Board +{ +public: + Board(); + ~Board(); + + Board & operator=(const Board &); + + // 静态成员常量 + // 3圈,禁止修改! + static const int N_RINGS = 3; + + // 8位,禁止修改! + static const int N_SEATS = 8; + + // 横直斜3个方向,禁止修改! + static const int N_DIRECTIONS = 3; + + // 棋盘点的个数:40 + static const int N_POINTS = (Board::N_RINGS + 2) * Board::N_SEATS; + + // 遍历棋盘点所用的起始位置,即 [8, 32) + static const int POS_BEGIN = N_SEATS; + static const int POS_END = ((N_RINGS + 1) * N_SEATS); + + // 空棋盘点位,用于判断一个棋子位置是否在棋盘上 + static const int onBoard[N_POINTS]; + + // 成三表,表示棋盘上各个位置有成三关系的对应位置表 + // 这个表跟规则有关,一旦规则改变需要重新修改 + static int millTable[N_POINTS][N_DIRECTIONS][N_RINGS - 1]; + + // 生成成三表 + void createMillTable(const Rule ¤tRule); + + // 局面左右镜像 + void mirror(list &cmdlist, char *cmdline, int32_t move_, struct Rule ¤tRule, int currentPos, bool cmdChange = true); + + // 局面内外翻转 + void turn(list &cmdlist, char *cmdline, int32_t move_, const Rule ¤tRule, int currentPos, bool cmdChange = true); + + // 局面逆时针旋转 + void rotate(int degrees, list &cmdlist, char *cmdline, int32_t move_, const Rule ¤tRule, int currentPos, bool cmdChange = true); + + // 判断棋盘pos处的棋子处于几个“三连”中 + int isInMills(int pos, bool test = false); + + // 判断玩家的所有棋子是否都处于“三连”状态 + bool isAllInMills(char ch); + bool isAllInMills(enum Player); + + // 判断玩家的棋子周围有几个空位 + int getSurroundedEmptyPosCount(enum Player turn, const Rule ¤tRule, int nPiecesOnBoard_1, int nPiecesOnBoard_2, int pos, bool includeFobidden); + + // 判断玩家的棋子是否被围 + bool isSurrounded(enum Player turn, const Rule ¤tRule, int nPiecesOnBoard_1, int nPiecesOnBoard_2, int pos); + + // 判断玩家的棋子是否全部被围 + bool isAllSurrounded(enum Player turn, const Rule ¤tRule, int nPiecesOnBoard_1, int nPiecesOnBoard_2, char ch); + + bool isAllSurrounded(enum Player turn, const Rule ¤tRule, int nPiecesOnBoard_1, int nPiecesOnBoard_2, enum Player ply); + + // 三连加入列表 + int addMills(const Rule ¤tRule, int pos); + + // 获取位置点棋子的归属人 + enum Player getWhosPiece(int r, int s); + + bool getPieceRS(const Player &player, const int &number, int &r, int &s, struct Rule ¤tRule); + + // 获取当前棋子 + bool getCurrentPiece(Player &player, int &number, int currentPos); + + // 将棋盘下标形式转化为第r圈,第s位,r和s下标都从1开始 + void pos2rs(int pos, int &r, int &s); + + // 将第c圈,第p位转化为棋盘下标形式,r和s下标都从1开始 + int rs2Pos(int r, int s); + +//private: + + // 棋局,抽象为一个 5*8 的数组,上下两行留空 + /* + 0x00 代表无棋子 + 0x0F 代表禁点 + 0x11~0x1C 代表先手第 1~12 子 + 0x21~0x2C 代表后手第 1~12 子 + 判断棋子是先手的用 (board[i] & 0x10) + 判断棋子是后手的用 (board[i] & 0x20) + */ + int board_[N_POINTS]{}; + + /* + 本打算用如下的结构体来表示“三连” + struct Mill { + char piece1; // “三连”中最小的棋子 + char pos1; // 最小棋子的位置 + char piece2; // 次小的棋子 + char pos2; // 次小棋子的位置 + char piece3; // 最大的棋子 + char pos3; // 最大棋子的位置 + }; + + 但为了提高执行效率改用一个64位整数了,规则如下 + 0x 00 00 00 00 00 00 00 00 + unused unused piece1 pos1 piece2 pos2 piece3 pos3 + */ + + // 三连列表 + vector millList; +}; + +#endif diff --git a/src/game/millgame.cpp b/src/game/millgame.cpp index 1f85d4a9..f71772b4 100644 --- a/src/game/millgame.cpp +++ b/src/game/millgame.cpp @@ -24,113 +24,10 @@ #include "search.h" #include "movegen.h" -// 对静态常量数组的定义要放在类外,不要放在头文件 -// 预定义的4套规则 -const struct MillGame::Rule MillGame::RULES[N_RULES] = { -{ - "成三棋", // 成三棋 - // 规则说明 - "1. 双方各9颗子,开局依次摆子;\n" - "2. 凡出现三子相连,就提掉对手一子;\n" - "3. 不能提对手的“三连”子,除非无子可提;\n" - "4. 同时出现两个“三连”只能提一子;\n" - "5. 摆完后依次走子,每次只能往相邻位置走一步;\n" - "6. 把对手棋子提到少于3颗时胜利;\n" - "7. 走棋阶段不能行动(被“闷”)算负。", - 9, // 双方各9子 - 3, // 赛点子数为3 - false, // 没有斜线 - false, // 没有禁点,摆棋阶段被提子的点可以再摆子 - false, // 先摆棋者先行棋 - true, // 可以重复成三 - false, // 多个“三连”只能提一子 - false, // 不能提对手的“三连”子,除非无子可提; - true, // 摆棋满子(闷棋,只有12子棋才出现)算先手负 - true, // 走棋阶段不能行动(被“闷”)算负 - false, // 剩三子时不可以飞棋 - 0, // 不计步数 - 0 // 不计时 -}, -{ - "打三棋(12连棋)", // 打三棋 - // 规则说明 - "1. 双方各12颗子,棋盘有斜线;\n" - "2. 摆棋阶段被提子的位置不能再摆子,直到走棋阶段;\n" - "3. 摆棋阶段,摆满棋盘算先手负;\n" - "4. 走棋阶段,后摆棋的一方先走;\n" - "5. 同时出现两个“三连”只能提一子;\n" - "6. 其它规则与成三棋基本相同。", - 12, // 双方各12子 - 3, // 赛点子数为3 - true, // 有斜线 - true, // 有禁点,摆棋阶段被提子的点不能再摆子 - true, // 后摆棋者先行棋 - true, // 可以重复成三 - false, // 多个“三连”只能提一子 - true, // 可以提对手的“三连”子 - true, // 摆棋满子(闷棋,只有12子棋才出现)算先手负 - true, // 走棋阶段不能行动(被“闷”)算负 - false, // 剩三子时不可以飞棋 - 50, // 不计步数 - 0 // 不计时 -}, -{ - "九连棋", // 九连棋 - // 规则说明 - "1. 规则与成三棋基本相同,只是它的棋子有序号,\n" - "2. 相同序号、位置的“三连”不能重复提子;\n" - "3. 走棋阶段不能行动(被“闷”),则由对手继续走棋;\n" - "4. 一步出现几个“三连”就可以提几个子。", - 9, // 双方各9子 - 3, // 赛点子数为3 - false, // 没有斜线 - false, // 没有禁点,摆棋阶段被提子的点可以再摆子 - false, // 先摆棋者先行棋 - false, // 不可以重复成三 - true, // 出现几个“三连”就可以提几个子 - false, // 不能提对手的“三连”子,除非无子可提; - true, // 摆棋满子(闷棋,只有12子棋才出现)算先手负 - false, // 走棋阶段不能行动(被“闷”),则由对手继续走棋 - false, // 剩三子时不可以飞棋 - 0, // 不计步数 - 0 // 不计时 -}, -{ - "莫里斯九子棋", // 莫里斯九子棋 - // 规则说明 - "规则与成三棋基本相同,只是在走子阶段,当一方仅剩3子时,他可以飞子到任意空位。", - 9, // 双方各9子 - 3, // 赛点子数为3 - false, // 没有斜线 - false, // 没有禁点,摆棋阶段被提子的点可以再摆子 - false, // 先摆棋者先行棋 - true, // 可以重复成三 - false, // 多个“三连”只能提一子 - false, // 不能提对手的“三连”子,除非无子可提; - true, // 摆棋满子(闷棋,只有12子棋才出现)算先手负 - true, // 走棋阶段不能行动(被“闷”)算负 - true, // 剩三子时可以飞棋 - 0, // 不计步数 - 0 // 不计时 -} -}; - -// 名义上是个数组,实际上相当于一个判断是否在棋盘上的函数 -const int MillGame::onBoard[N_POINTS] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -// 成三表 -int MillGame::millTable[N_POINTS][N_DIRECTIONS][N_RINGS - 1] = {{{0}}}; - MillGame::MillGame() { // 单独提出 board 等数据,免得每次都写 context.board; - board_ = context.board; + board_ = context.board.board_; #if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) // 创建哈希数据 @@ -156,7 +53,7 @@ MillGame::MillGame(const MillGame &chess) *this = chess; } -MillGame &MillGame::operator = (const MillGame &chess) +MillGame &MillGame::operator= (const MillGame &chess) { if (this == &chess) return *this; @@ -167,7 +64,7 @@ MillGame &MillGame::operator = (const MillGame &chess) moveStep = chess.moveStep; randomMove_ = chess.randomMove_; giveUpIfMostLose_ = chess.giveUpIfMostLose_; - board_ = context.board; + board_ = context.board.board_; currentPos = chess.currentPos; winner = chess.winner; startTime = chess.startTime; @@ -184,15 +81,15 @@ MillGame &MillGame::operator = (const MillGame &chess) int MillGame::playerToId(enum Player player) { - if (player == MillGame::PLAYER1) + if (player == PLAYER1) return 1; - else if (player == MillGame::PLAYER2) + else if (player == PLAYER2) return 2; return 0; } -MillGame::Player MillGame::getOpponent(MillGame::Player player) +Player MillGame::getOpponent(Player player) { switch (player) { @@ -204,208 +101,7 @@ MillGame::Player MillGame::getOpponent(MillGame::Player player) break; } - return NOBODY; -} - -void MillGame::createMillTable() -{ -#ifdef CONST_MILL_TABLE - const int millTable_noObliqueLine[MillGame::N_POINTS][MillGame::N_DIRECTIONS][2] = { - /* 0 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 1 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 2 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 3 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 4 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 5 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 6 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 7 */ {{0, 0}, {0, 0}, {0, 0}}, - - /* 8 */ {{16, 24}, {9, 15}, {0, 0}}, - /* 9 */ {{0, 0}, {15, 8}, {10, 11}}, - /* 10 */ {{18, 26}, {11, 9}, {0, 0}}, - /* 11 */ {{0, 0}, {9, 10}, {12, 13}}, - /* 12 */ {{20, 28}, {13, 11}, {0, 0}}, - /* 13 */ {{0, 0}, {11, 12}, {14, 15}}, - /* 14 */ {{22, 30}, {15, 13}, {0, 0}}, - /* 15 */ {{0, 0}, {13, 14}, {8, 9}}, - - /* 16 */ {{8, 24}, {17, 23}, {0, 0}}, - /* 17 */ {{0, 0}, {23, 16}, {18, 19}}, - /* 18 */ {{10, 26}, {19, 17}, {0, 0}}, - /* 19 */ {{0, 0}, {17, 18}, {20, 21}}, - /* 20 */ {{12, 28}, {21, 19}, {0, 0}}, - /* 21 */ {{0, 0}, {19, 20}, {22, 23}}, - /* 22 */ {{14, 30}, {23, 21}, {0, 0}}, - /* 23 */ {{0, 0}, {21, 22}, {16, 17}}, - - /* 24 */ {{8, 16}, {25, 31}, {0, 0}}, - /* 25 */ {{0, 0}, {31, 24}, {26, 27}}, - /* 26 */ {{10, 18}, {27, 25}, {0, 0}}, - /* 27 */ {{0, 0}, {25, 26}, {28, 29}}, - /* 28 */ {{12, 20}, {29, 27}, {0, 0}}, - /* 29 */ {{0, 0}, {27, 28}, {30, 31}}, - /* 30 */ {{14, 22}, {31, 29}, {0, 0}}, - /* 31 */ {{0, 0}, {29, 30}, {24, 25}}, - - /* 32 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 33 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 34 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 35 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 36 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 37 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 38 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 39 */ {{0, 0}, {0, 0}, {0, 0}} - }; - - const int millTable_hasObliqueLines[MillGame::N_POINTS][MillGame::N_DIRECTIONS][2] = { - /* 0 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 1 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 2 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 3 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 4 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 5 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 6 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 7 */ {{0, 0}, {0, 0}, {0, 0}}, - - /* 8 */ {{16, 24}, {9, 15}, {0, 0}}, - /* 9 */ {{17, 25}, {15, 8}, {10, 11}}, - /* 10 */ {{18, 26}, {11, 9}, {0, 0}}, - /* 11 */ {{19, 27}, {9, 10}, {12, 13}}, - /* 12 */ {{20, 28}, {13, 11}, {0, 0}}, - /* 13 */ {{21, 29}, {11, 12}, {14, 15}}, - /* 14 */ {{22, 30}, {15, 13}, {0, 0}}, - /* 15 */ {{23, 31}, {13, 14}, {8, 9}}, - - /* 16 */ {{8, 24}, {17, 23}, {0, 0}}, - /* 17 */ {{9, 25}, {23, 16}, {18, 19}}, - /* 18 */ {{10, 26}, {19, 17}, {0, 0}}, - /* 19 */ {{11, 27}, {17, 18}, {20, 21}}, - /* 20 */ {{12, 28}, {21, 19}, {0, 0}}, - /* 21 */ {{13, 29}, {19, 20}, {22, 23}}, - /* 22 */ {{14, 30}, {23, 21}, {0, 0}}, - /* 23 */ {{15, 31}, {21, 22}, {16, 17}}, - - /* 24 */ {{8, 16}, {25, 31}, {0, 0}}, - /* 25 */ {{9, 17}, {31, 24}, {26, 27}}, - /* 26 */ {{10, 18}, {27, 25}, {0, 0}}, - /* 27 */ {{11, 19}, {25, 26}, {28, 29}}, - /* 28 */ {{12, 20}, {29, 27}, {0, 0}}, - /* 29 */ {{13, 21}, {27, 28}, {30, 31}}, - /* 30 */ {{14, 22}, {31, 29}, {0, 0}}, - /* 31 */ {{15, 23}, {29, 30}, {24, 25}}, - - /* 32 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 33 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 34 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 35 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 36 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 37 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 38 */ {{0, 0}, {0, 0}, {0, 0}}, - /* 39 */ {{0, 0}, {0, 0}, {0, 0}} - }; - - if (currentRule.hasObliqueLines) { - memcpy(millTable, millTable_hasObliqueLines, sizeof(millTable)); - } else { - memcpy(millTable, millTable_noObliqueLine, sizeof(millTable)); - } -#else /* CONST_MILL_TABLE */ - for (int i = 0; i < N_SEATS; i++) { - // 内外方向的“成三” - // 如果是0、2、4、6位(偶数位)或是有斜线 - if (!(i & 1) || this->currentRule.hasObliqueLines) { - millTable[1 * N_SEATS + i][0][0] = 2 * N_SEATS + i; - millTable[1 * N_SEATS + i][0][1] = 3 * N_SEATS + i; - - millTable[2 * N_SEATS + i][0][0] = 1 * N_SEATS + i; - millTable[2 * N_SEATS + i][0][1] = 3 * N_SEATS + i; - - millTable[3 * N_SEATS + i][0][0] = 1 * N_SEATS + i; - millTable[3 * N_SEATS + i][0][1] = 2 * N_SEATS + i; - } - // 对于无斜线情况下的1、3、5、7位(奇数位) - else { - // 置空该组“成三” - millTable[1 * N_SEATS + i][0][0] = 0; - millTable[1 * N_SEATS + i][0][1] = 0; - - millTable[2 * N_SEATS + i][0][0] = 0; - millTable[2 * N_SEATS + i][0][1] = 0; - - millTable[3 * N_SEATS + i][0][0] = 0; - millTable[3 * N_SEATS + i][0][1] = 0; - } - - // 当前圈上的“成三” - // 如果是0、2、4、6位 - if (!(i & 1)) { - millTable[1 * N_SEATS + i][1][0] = 1 * N_SEATS + (i + 1) % N_SEATS; - millTable[1 * N_SEATS + i][1][1] = 1 * N_SEATS + (i + N_SEATS - 1) % N_SEATS; - - millTable[2 * N_SEATS + i][1][0] = 2 * N_SEATS + (i + 1) % N_SEATS; - millTable[2 * N_SEATS + i][1][1] = 2 * N_SEATS + (i + N_SEATS - 1) % N_SEATS; - - millTable[3 * N_SEATS + i][1][0] = 3 * N_SEATS + (i + 1) % N_SEATS; - millTable[3 * N_SEATS + i][1][1] = 3 * N_SEATS + (i + N_SEATS - 1) % N_SEATS; - // 置空另一组“成三” - millTable[1 * N_SEATS + i][2][0] = 0; - millTable[1 * N_SEATS + i][2][1] = 0; - - millTable[2 * N_SEATS + i][2][0] = 0; - millTable[2 * N_SEATS + i][2][1] = 0; - - millTable[3 * N_SEATS + i][2][0] = 0; - millTable[3 * N_SEATS + i][2][1] = 0; - } - // 对于1、3、5、7位(奇数位) - else { - // 当前圈上逆时针的“成三” - millTable[1 * N_SEATS + i][1][0] = 1 * N_SEATS + (i + N_SEATS - 2) % N_SEATS; - millTable[1 * N_SEATS + i][1][1] = 1 * N_SEATS + (i + N_SEATS - 1) % N_SEATS; - - millTable[2 * N_SEATS + i][1][0] = 2 * N_SEATS + (i + N_SEATS - 2) % N_SEATS; - millTable[2 * N_SEATS + i][1][1] = 2 * N_SEATS + (i + N_SEATS - 1) % N_SEATS; - - millTable[3 * N_SEATS + i][1][0] = 3 * N_SEATS + (i + N_SEATS - 2) % N_SEATS; - millTable[3 * N_SEATS + i][1][1] = 3 * N_SEATS + (i + N_SEATS - 1) % N_SEATS; - - // 当前圈上顺时针的“成三” - millTable[1 * N_SEATS + i][2][0] = 1 * N_SEATS + (i + 1) % N_SEATS; - millTable[1 * N_SEATS + i][2][1] = 1 * N_SEATS + (i + 2) % N_SEATS; - - millTable[2 * N_SEATS + i][2][0] = 2 * N_SEATS + (i + 1) % N_SEATS; - millTable[2 * N_SEATS + i][2][1] = 2 * N_SEATS + (i + 2) % N_SEATS; - - millTable[3 * N_SEATS + i][2][0] = 3 * N_SEATS + (i + 1) % N_SEATS; - millTable[3 * N_SEATS + i][2][1] = 3 * N_SEATS + (i + 2) % N_SEATS; - } - } -#endif /* CONST_MILL_TABLE */ - -#if 0 - for (int i = 0; i < N_POINTS; i++) { - printf("/* %d */ {", i); - for (int j = 0; j < N_DIRECTIONS; j++) { - printf("{"); - for (int k = 0; k < 2; k++) { - if (k == 0) { - printf("%d, ", millTable[i][j][k]); - } else { - printf("%d", millTable[i][j][k]); - } - - } - if (j == 2) - printf("}"); - else - printf("}, "); - } - printf("},\n"); - } - - printf("======== millTable End =========\n"); - -#endif + return PLAYER_NOBODY; } // 设置配置 @@ -476,12 +172,12 @@ bool MillGame::setContext(const struct Rule *rule, step_t maxStepsLedToDraw, int // 当前棋局(3×8) if (board == nullptr) { - memset(context.board, 0, sizeof(context.board)); + memset(context.board.board_, 0, sizeof(context.board.board_)); #if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) context.hash = 0; #endif } else { - memcpy(context.board, board, sizeof(context.board)); + memcpy(context.board.board_, board, sizeof(context.board.board_)); } // 计算盘面子数 @@ -496,14 +192,14 @@ bool MillGame::setContext(const struct Rule *rule, step_t maxStepsLedToDraw, int */ context.nPiecesOnBoard_1 = context.nPiecesOnBoard_2 = 0; - for (int r = 1; r < N_RINGS + 2; r++) { - for (int s = 0; s < N_SEATS; s++) { - int pos = r * N_SEATS + s; - if (context.board[pos] & 0x10) { + for (int r = 1; r < Board::N_RINGS + 2; r++) { + for (int s = 0; s < Board::N_SEATS; s++) { + int pos = r * Board::N_SEATS + s; + if (context.board.board_[pos] & 0x10) { context.nPiecesOnBoard_1++; - } else if (context.board[pos] & 0x20) { + } else if (context.board.board_[pos] & 0x20) { context.nPiecesOnBoard_2++; - } else if (context.board[pos] & 0x0F) { + } else if (context.board.board_[pos] & 0x0F) { // 不计算盘面子数 } } @@ -534,16 +230,18 @@ bool MillGame::setContext(const struct Rule *rule, step_t maxStepsLedToDraw, int } // 清空成三记录 - context.millList.clear(); + if (!context.board.millList.empty()) { + context.board.millList.clear(); + } // 胜负标识 - winner = NOBODY; + winner = PLAYER_NOBODY; // 生成着法表 MoveList::createMoveTable(*this); // 生成成三表 - createMillTable(); + context.board.createMillTable(currentRule); // 不选中棋子 currentPos = 0; @@ -605,7 +303,7 @@ bool MillGame::reset() context.action = ACTION_PLACE; // 胜负标识 - winner = NOBODY; + winner = PLAYER_NOBODY; // 当前棋局(3×8) memset(board_, 0, sizeof(context.board)); @@ -620,7 +318,9 @@ bool MillGame::reset() context.nPiecesNeedRemove = 0; // 清空成三记录 - context.millList.clear(); + if (!context.board.millList.empty()) { + context.board.millList.clear(); + } // 不选中棋子 currentPos = 0; @@ -680,71 +380,6 @@ bool MillGame::start() } } -// Unused -bool MillGame::getPieceRS(const Player &player, const int &number, int &r, int &s) -{ - int piece; - - if (player == PLAYER1) { - piece = 0x10; - } else if (player == PLAYER2) { - piece = 0x20; - } else { - return false; - } - - if (number > 0 && number <= currentRule.nTotalPiecesEachSide) - piece &= number; - else - return false; - - for (int i = POS_BEGIN; i < POS_END; i++) { - if (board_[i] == piece) { - pos2rs(i, r, s); - return true; - } - } - - return false; -} - -// 获取当前棋子 -bool MillGame::getCurrentPiece(Player &player, int &number) -{ - if (!onBoard[currentPos]) - return false; - - int p = board_[currentPos]; - - if (p & 0x10) { - player = PLAYER1; - number = p - 0x10; - } else if (p & 0x20) { - player = PLAYER2; - number = p - 0x20; - } else { - return false; - } - - return true; -} - -void MillGame::pos2rs(const int pos, int &r, int &s) -{ - //r = pos / N_SEATS; - //s = pos % N_SEATS + 1; - r = pos >> 3; - s = (pos & 0x07) + 1; -} - -int MillGame::rs2Pos(int r, int s) -{ - if (r < 1 || r > N_RINGS || s < 1 || s > N_SEATS) - return 0; - - return r * N_SEATS + s - 1; -} - bool MillGame::place(int pos, int time_p, int8_t rs) { // 如果局面为“结局”,返回false @@ -760,13 +395,13 @@ bool MillGame::place(int pos, int time_p, int8_t rs) return false; // 如果落子位置在棋盘外、已有子点或禁点,返回false - if (!onBoard[pos] || board_[pos]) + if (!context.board.onBoard[pos] || board_[pos]) return false; // 格式转换 int r = 0; int s = 0; - pos2rs(pos, r, s); + context.board.pos2rs(pos, r, s); // 时间的临时变量 int player_ms = -1; @@ -806,7 +441,7 @@ bool MillGame::place(int pos, int time_p, int8_t rs) currentPos = pos; - n = addMills(currentPos); + n = context.board.addMills(currentRule, currentPos); // 开局阶段未成三 if (n == 0) { @@ -880,7 +515,7 @@ bool MillGame::place(int pos, int time_p, int8_t rs) move_ = (currentPos << 8) + pos; if (rs) { player_ms = update(time_p); - sprintf(cmdline, "(%1u,%1u)->(%1u,%1u) %02u:%02u", currentPos / N_SEATS, currentPos % N_SEATS + 1, + sprintf(cmdline, "(%1u,%1u)->(%1u,%1u) %02u:%02u", currentPos / Board::N_SEATS, currentPos % Board::N_SEATS + 1, r, s, player_ms / 60, player_ms % 60); cmdlist.emplace_back(string(cmdline)); currentStep++; @@ -900,7 +535,7 @@ bool MillGame::place(int pos, int time_p, int8_t rs) #endif currentPos = pos; - n = addMills(currentPos); + n = context.board.addMills(currentRule, currentPos); // 中局阶段未成三 if (n == 0) { @@ -935,7 +570,7 @@ out: bool MillGame::_place(int r, int s, int time_p) { // 转换为 pos - int pos = rs2Pos(r, s); + int pos = context.board.rs2Pos(r, s); return place(pos, time_p, true); } @@ -943,7 +578,7 @@ bool MillGame::_place(int r, int s, int time_p) bool MillGame::_capture(int r, int s, int time_p) { // 转换为 pos - int pos = rs2Pos(r, s); + int pos = context.board.rs2Pos(r, s); return capture(pos, time_p, 1); } @@ -965,7 +600,7 @@ bool MillGame::capture(int pos, int time_p, int8_t cp) // 格式转换 int r = 0; int s = 0; - pos2rs(pos, r, s); + context.board.pos2rs(pos, r, s); // 时间的临时变量 int player_ms = -1; @@ -979,7 +614,7 @@ bool MillGame::capture(int pos, int time_p, int8_t cp) // 如果当前子是否处于“三连”之中,且对方还未全部处于“三连”之中 if (!currentRule.allowRemoveMill && - isInMills(pos) && !isAllInMills(opponent)) { + context.board.isInMills(pos) && !context.board.isAllInMills(opponent)) { return false; } @@ -1112,7 +747,7 @@ bool MillGame::choose(int pos) // 判断选子是否可选 if (board_[pos] & t) { // 判断pos处的棋子是否被“闷” - if (isSurrounded(pos)) { + if (context.board.isSurrounded(context.turn, currentRule, context.nPiecesOnBoard_1, context.nPiecesOnBoard_2, pos)) { return false; } @@ -1130,7 +765,7 @@ bool MillGame::choose(int pos) bool MillGame::choose(int r, int s) { - return choose(rs2Pos(r, s)); + return choose(context.board.rs2Pos(r, s)); } bool MillGame::giveup(Player loser) @@ -1176,7 +811,7 @@ bool MillGame::command(const char *cmd) return false; } - return setContext(&MillGame::RULES[r - 1], s, t); + return setContext(&RULES[r - 1], s, t); } // 选子移动 @@ -1234,7 +869,7 @@ bool MillGame::command(const char *cmd) if (!strcmp(cmd, "draw")) { context.stage = GAME_OVER; - winner = DRAW; + winner = PLAYER_DRAW; score_draw++; tips = "三次重复局面判和。"; sprintf(cmdline, "Threefold Repetition. Draw!"); @@ -1337,7 +972,7 @@ bool MillGame::win(bool forceDraw) // 如果有步数限定 if (currentRule.maxStepsLedToDraw > 0 && moveStep > currentRule.maxStepsLedToDraw) { - winner = DRAW; + winner = PLAYER_DRAW; context.stage = GAME_OVER; sprintf(cmdline, "Steps over. In draw!"); cmdlist.emplace_back(string(cmdline)); @@ -1366,14 +1001,14 @@ bool MillGame::win(bool forceDraw) } // 如果摆满了,根据规则判断胜负 - if (context.nPiecesOnBoard_1 + context.nPiecesOnBoard_2 >= N_SEATS * N_RINGS) { + if (context.nPiecesOnBoard_1 + context.nPiecesOnBoard_2 >= Board::N_SEATS * Board::N_RINGS) { context.stage = GAME_OVER; if (currentRule.isStartingPlayerLoseWhenBoardFull) { winner = PLAYER2; sprintf(cmdline, "Player2 win!"); } else { - winner = DRAW; + winner = PLAYER_DRAW; sprintf(cmdline, "Full. In draw!"); } @@ -1382,7 +1017,7 @@ bool MillGame::win(bool forceDraw) } // 如果中局被“闷” - if (context.stage == GAME_MOVING && context.action == ACTION_CHOOSE && isAllSurrounded(context.turn)) { + if (context.stage == GAME_MOVING && context.action == ACTION_CHOOSE && context.board.isAllSurrounded(context.turn, currentRule, context.nPiecesOnBoard_1, context.nPiecesOnBoard_2, context.turn)) { // 规则要求被“闷”判负,则对手获胜 context.stage = GAME_OVER; @@ -1414,7 +1049,7 @@ bool MillGame::win(bool forceDraw) if (forceDraw) { tips = "重复三次局面和棋!"; - winner = DRAW; + winner = PLAYER_DRAW; context.stage = GAME_OVER; sprintf(cmdline, "Threefold Repetition. Draw!"); cmdlist.emplace_back(string(cmdline)); @@ -1425,160 +1060,17 @@ bool MillGame::win(bool forceDraw) return false; } -int MillGame::isInMills(int pos, bool test) -{ - int n = 0; - int pos1, pos2; - int m = test? INT32_MAX : board_[pos] & '\x30'; - - for (int i = 0; i < 3; i++) { - pos1 = millTable[pos][i][0]; - pos2 = millTable[pos][i][1]; - if (m & board_[pos1] & board_[pos2]) - n++; - } - - return n; -} - -int MillGame::addMills(int pos) -{ - // 成三用一个64位整数了,规则如下 - // 0x 00 00 00 00 00 00 00 00 - // unused unused piece1 pos1 piece2 pos2 piece3 pos3 - // piece1、piece2、piece3按照序号从小到大顺序排放 - uint64_t mill = 0; - int n = 0; - int p[3], min, temp; - char m = board_[pos] & '\x30'; - - for (int i = 0; i < 3; i++) { - p[0] = pos; - p[1] = millTable[pos][i][0]; - p[2] = millTable[pos][i][1]; - - // 如果没有成三 - if (!(m & board_[p[1]] & board_[p[2]])) { - continue; - } - - // 如果成三 - - // 排序 - for (int j = 0; j < 2; j++) { - min = j; - - for (int k = j + 1; k < 3; k++) { - if (p[min] > p[k]) - min = k; - } - - if (min == j) { - continue; - } - - temp = p[min]; - p[min] = p[j]; - p[j] = temp; - } - - // 成三 - mill = (static_cast(board_[p[0]]) << 40) - + (static_cast(p[0]) << 32) - + (static_cast(board_[p[1]]) << 24) - + (static_cast(p[1]) << 16) - + (static_cast(board_[p[2]]) << 8) - + static_cast(p[2]); - - // 如果允许相同三连反复去子 - if (currentRule.allowRemovePiecesRepeatedly) { - n++; - continue; - } - - // 如果不允许相同三连反复去子 - - // 迭代器 - list::iterator iter; - - // 遍历 - for (iter = context.millList.begin(); iter != context.millList.end(); iter++) { - if (mill == *iter) - break; - } - - // 如果没找到历史项 - if (iter == context.millList.end()) { - n++; - context.millList.push_back(mill); - } - } - - return n; -} - -bool MillGame::isAllInMills(char ch) -{ - for (int i = POS_BEGIN; i < POS_END; i++) { - if (board_[i] & ch) { - if (!isInMills(i)) { - return false; - } - } - } - - return true; -} - -bool MillGame::isAllInMills(enum Player player) -{ - char ch = 0x00; - - if (player == PLAYER1) - ch = 0x10; - else if (player == PLAYER2) - ch = 0x20; - else - return true; - - return isAllInMills(ch); -} - -// 判断玩家的棋子周围有几个空位 -int MillGame::getSurroundedEmptyPosCount(int pos, bool includeFobidden) -{ - int count = 0; - - if ((context.turn == PLAYER1 && - (context.nPiecesOnBoard_1 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces)) || - (context.turn == PLAYER2 && - (context.nPiecesOnBoard_2 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces))) { - int d, movePos; - for (d = 0; d < N_MOVE_DIRECTIONS; d++) { - movePos = MoveList::moveTable[pos][d]; - if (movePos) { - if (board_[movePos] == 0x00 || - (includeFobidden && board_[movePos] == 0x0F)) { - count++; - } - } - } - } - - return count; -} - // 计算玩家1和玩家2的棋子活动能力之差 -int MillGame::getMobilityDiff(bool includeFobidden) +int MillGame::getMobilityDiff(enum Player turn, const Rule &rule, int nPiecesOnBoard_1, int nPiecesOnBoard_2, bool includeFobidden) { - int *board = context.board; + int *board = context.board.board_; int mobility1 = 0; int mobility2 = 0; int diff = 0; int n = 0; - for (int i = POS_BEGIN; i < POS_END; i++) { - n = getSurroundedEmptyPosCount(i, includeFobidden); + for (int i = Board::POS_BEGIN; i < Board::POS_END; i++) { + n = context.board.getSurroundedEmptyPosCount(turn, rule, nPiecesOnBoard_1, nPiecesOnBoard_2, i, includeFobidden); if (board[i] & 0x10) { mobility1 += n; @@ -1592,79 +1084,13 @@ int MillGame::getMobilityDiff(bool includeFobidden) return diff; } -// 判断玩家的棋子是否被围 -bool MillGame::isSurrounded(int pos) -{ - // 判断pos处的棋子是否被“闷” - if ((context.turn == PLAYER1 && - (context.nPiecesOnBoard_1 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces)) || - (context.turn == PLAYER2 && - (context.nPiecesOnBoard_2 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces))) { - int i, movePos; - for (i = 0; i < 4; i++) { - movePos = MoveList::moveTable[pos][i]; - if (movePos && !board_[movePos]) - break; - } - // 被围住 - if (i == 4) - return true; - } - // 没被围住 - return false; -} - -bool MillGame::isAllSurrounded(char ch) -{ - // 如果摆满 - if (context.nPiecesOnBoard_1 + context.nPiecesOnBoard_2 >= N_SEATS * N_RINGS) - return true; - - // 判断是否可以飞子 - if ((context.turn == PLAYER1 && - (context.nPiecesOnBoard_1 <= currentRule.nPiecesAtLeast && currentRule.allowFlyWhenRemainThreePieces)) || - (context.turn == PLAYER2 && - (context.nPiecesOnBoard_2 <= currentRule.nPiecesAtLeast && currentRule.allowFlyWhenRemainThreePieces))) { - return false; - } - - // 查询整个棋盘 - int movePos; - for (int i = 1; i < N_SEATS * (N_RINGS + 1); i++) { - if (!(ch & board_[i])) { - continue; - } - - for (int d = 0; d < N_MOVE_DIRECTIONS; d++) { - movePos = MoveList::moveTable[i][d]; - if (movePos && !board_[movePos]) - return false; - } - } - - return true; -} - -// 判断玩家的棋子是否全部被围 -bool MillGame::isAllSurrounded(enum Player ply) -{ - char t = '\x30'; - - if (ply == PLAYER1) - t &= '\x10'; - else if (ply == PLAYER2) - t &= '\x20'; - - return isAllSurrounded(t); -} - void MillGame::cleanForbiddenPoints() { int pos = 0; - for (int r = 1; r <= N_RINGS; r++) { - for (int s = 0; s < N_SEATS; s++) { - pos = r * N_SEATS + s; + for (int r = 1; r <= Board::N_RINGS; r++) { + for (int s = 0; s < Board::N_SEATS; s++) { + pos = r * Board::N_SEATS + s; if (board_[pos] == '\x0f') { #if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) @@ -1676,7 +1102,7 @@ void MillGame::cleanForbiddenPoints() } } -enum MillGame::Player MillGame::changeTurn() +enum Player MillGame::changeTurn() { // 设置轮到谁走 context.turn = (context.turn == PLAYER1) ? PLAYER2 : PLAYER1; @@ -1687,12 +1113,12 @@ enum MillGame::Player MillGame::changeTurn() void MillGame::setTips() { switch (context.stage) { - case MillGame::GAME_NOTSTARTED: + case GAME_NOTSTARTED: tips = "轮到玩家1落子,剩余" + std::to_string(context.nPiecesInHand_1) + "子" + " 比分 " + to_string(score_1) + ":" + to_string(score_2) + ", 和棋 " + to_string(score_draw); break; - case MillGame::GAME_PLACING: + case GAME_PLACING: if (context.action == ACTION_PLACE) { if (context.turn == PLAYER1) { tips = "轮到玩家1落子,剩余" + std::to_string(context.nPiecesInHand_1) + "子"; @@ -1708,7 +1134,7 @@ void MillGame::setTips() } break; - case MillGame::GAME_MOVING: + case GAME_MOVING: if (context.action == ACTION_PLACE || context.action == ACTION_CHOOSE) { if (context.turn == PLAYER1) { tips = "轮到玩家1选子移动"; @@ -1724,8 +1150,8 @@ void MillGame::setTips() } break; - case MillGame::GAME_OVER: - if (winner == DRAW) { + case GAME_OVER: + if (winner == PLAYER_DRAW) { score_draw++; tips = "双方平局!比分 " + to_string(score_1) + ":" + to_string(score_2) + ", 和棋 " + to_string(score_draw); } @@ -1750,19 +1176,6 @@ void MillGame::setTips() } } -enum MillGame::Player MillGame::getWhosPiece(int r, int s) -{ - int pos = rs2Pos(r, s); - - if (board_[pos] & '\x10') - return PLAYER1; - - if (board_[pos] & '\x20') - return PLAYER2; - - return NOBODY; -} - void MillGame::getElapsedTime(time_t &p1_ms, time_t &p2_ms) { update(); @@ -1771,443 +1184,6 @@ void MillGame::getElapsedTime(time_t &p1_ms, time_t &p2_ms) p2_ms = elapsedSeconds_2; } -void MillGame::mirror(bool cmdChange /*= true*/) -{ - int ch; - int r, s; - int i; - - for (r = 1; r <= N_RINGS; r++) { - for (s = 1; s < N_SEATS / 2; s++) { - ch = board_[r * N_SEATS + s]; - board_[r * N_SEATS + s] = board_[(r + 1) * N_SEATS - s]; - //updateHash(i * N_SEATS + j); - board_[(r + 1) * N_SEATS - s] = ch; - //updateHash((i + 1) * N_SEATS - j); - } - } - - uint64_t llp[3] = {0}; - - if (move_ < 0) { - r = (-move_) / N_SEATS; - s = (-move_) % N_SEATS; - s = (N_SEATS - s) % N_SEATS; - move_ = -(r * N_SEATS + s); - } else { - llp[0] = static_cast(move_ >> 8); - llp[1] = move_ & 0x00ff; - - for (i = 0; i < 2; i++) { - r = static_cast(llp[i]) / N_SEATS; - s = static_cast(llp[i]) % N_SEATS; - s = (N_SEATS - s) % N_SEATS; - llp[i] = (static_cast(r) * N_SEATS + s); - } - - move_ = static_cast(((llp[0] << 8) | llp[1])); - } - - if (currentPos != 0) { - r = currentPos / N_SEATS; - s = currentPos % N_SEATS; - s = (N_SEATS - s) % N_SEATS; - currentPos = r * N_SEATS + s; - } - - if (currentRule.allowRemovePiecesRepeatedly) { - for (auto &mill : context.millList) { - llp[0] = (mill & 0x000000ff00000000) >> 32; - llp[1] = (mill & 0x0000000000ff0000) >> 16; - llp[2] = (mill & 0x00000000000000ff); - - for (i = 0; i < 3; i++) { - r = static_cast(llp[i]) / N_SEATS; - s = static_cast(llp[i]) % N_SEATS; - s = (N_SEATS - s) % N_SEATS; - llp[i] = static_cast(r * N_SEATS + s); - } - - mill &= 0xffffff00ff00ff00; - mill |= (llp[0] << 32) | (llp[1] << 16) | llp[2]; - } - } - - // 命令行解析 - if (cmdChange) { - int r1, s1, r2, s2; - int args = 0; - int mm = 0, ss = 0; - - args = sscanf(cmdline, "(%1u,%1u)->(%1u,%1u) %2u:%2u", &r1, &s1, &r2, &s2, &mm, &ss); - if (args >= 4) { - s1 = (N_SEATS - s1 + 1) % N_SEATS; - s2 = (N_SEATS - s2 + 1) % N_SEATS; - cmdline[3] = '1' + static_cast(s1); - cmdline[10] = '1' + static_cast(s2); - } else { - args = sscanf(cmdline, "-(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); - if (args >= 2) { - s1 = (N_SEATS - s1 + 1) % N_SEATS; - cmdline[4] = '1' + static_cast(s1); - } else { - args = sscanf(cmdline, "(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); - if (args >= 2) { - s1 = (N_SEATS - s1 + 1) % N_SEATS; - cmdline[3] = '1' + static_cast(s1); - } - } - } - - for (auto & iter : cmdlist) { - args = sscanf(iter.c_str(), "(%1u,%1u)->(%1u,%1u) %2u:%2u", &r1, &s1, &r2, &s2, &mm, &ss); - if (args >= 4) { - s1 = (N_SEATS - s1 + 1) % N_SEATS; - s2 = (N_SEATS - s2 + 1) % N_SEATS; - iter[3] = '1' + static_cast(s1); - iter[10] = '1' + static_cast(s2); - } else { - args = sscanf(iter.c_str(), "-(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); - if (args >= 2) { - s1 = (N_SEATS - s1 + 1) % N_SEATS; - iter[4] = '1' + static_cast(s1); - } else { - args = sscanf(iter.c_str(), "(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); - if (args >= 2) { - s1 = (N_SEATS - s1 + 1) % N_SEATS; - iter[3] = '1' + static_cast(s1); - } - } - } - } - } -} - -void MillGame::turn(bool cmdChange /*= true*/) -{ - int ch; - int r, s; - int i; - - for (s = 0; s < N_SEATS; s++) { - ch = board_[N_SEATS + s]; - board_[N_SEATS + s] = board_[N_SEATS * N_RINGS + s]; - //updateHash(N_SEATS + s); - board_[N_SEATS * N_RINGS + s] = ch; - //updateHash(N_SEATS * N_RINGS + s); - } - - uint64_t llp[3] = {0}; - - if (move_ < 0) { - r = (-move_) / N_SEATS; - s = (-move_) % N_SEATS; - - if (r == 1) - r = N_RINGS; - else if (r == N_RINGS) - r = 1; - - move_ = -(r * N_SEATS + s); - } else { - llp[0] = static_cast(move_ >> 8); - llp[1] = move_ & 0x00ff; - - for (i = 0; i < 2; i++) { - r = static_cast(llp[i]) / N_SEATS; - s = static_cast(llp[i]) % N_SEATS; - - if (r == 1) - r = N_RINGS; - else if (r == N_RINGS) - r = 1; - - llp[i] = static_cast(r * N_SEATS + s); - } - - move_ = static_cast(((llp[0] << 8) | llp[1])); - } - - if (currentPos != 0) { - r = currentPos / N_SEATS; - s = currentPos % N_SEATS; - - if (r == 1) - r = N_RINGS; - else if (r == N_RINGS) - r = 1; - - currentPos = r * N_SEATS + s; - } - - if (currentRule.allowRemovePiecesRepeatedly) { - for (auto &mill : context.millList) { - llp[0] = (mill & 0x000000ff00000000) >> 32; - llp[1] = (mill & 0x0000000000ff0000) >> 16; - llp[2] = (mill & 0x00000000000000ff); - - for (i = 0; i < 3; i++) { - r = static_cast(llp[i]) / N_SEATS; - s = static_cast(llp[i]) % N_SEATS; - - if (r == 1) - r = N_RINGS; - else if (r == N_RINGS) - r = 1; - - llp[i] = static_cast(r * N_SEATS + s); - } - - mill &= 0xffffff00ff00ff00; - mill |= (llp[0] << 32) | (llp[1] << 16) | llp[2]; - } - } - - // 命令行解析 - if (cmdChange) { - int r1, s1, r2, s2; - int args = 0; - int mm = 0, ss = 0; - - args = sscanf(cmdline, "(%1u,%1u)->(%1u,%1u) %2u:%2u", - &r1, &s1, &r2, &s2, &mm, &ss); - - if (args >= 4) { - if (r1 == 1) - r1 = N_RINGS; - else if (r1 == N_RINGS) - r1 = 1; - - if (r2 == 1) - r2 = N_RINGS; - else if (r2 == N_RINGS) - r2 = 1; - - cmdline[1] = '0' + static_cast(r1); - cmdline[8] = '0' + static_cast(r2); - } else { - args = sscanf(cmdline, "-(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); - if (args >= 2) { - if (r1 == 1) - r1 = N_RINGS; - else if (r1 == N_RINGS) - r1 = 1; - cmdline[2] = '0' + static_cast(r1); - } else { - args = sscanf(cmdline, "(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); - if (args >= 2) { - if (r1 == 1) - r1 = N_RINGS; - else if (r1 == N_RINGS) - r1 = 1; - cmdline[1] = '0' + static_cast(r1); - } - } - } - - for (auto & iter : cmdlist) { - args = sscanf(iter.c_str(), - "(%1u,%1u)->(%1u,%1u) %2u:%2u", - &r1, &s1, &r2, &s2, &mm, &ss); - - if (args >= 4) { - if (r1 == 1) - r1 = N_RINGS; - else if (r1 == N_RINGS) - r1 = 1; - - if (r2 == 1) - r2 = N_RINGS; - else if (r2 == N_RINGS) - r2 = 1; - - iter[1] = '0' + static_cast(r1); - iter[8] = '0' + static_cast(r2); - } else { - args = sscanf(iter.c_str(), "-(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); - if (args >= 2) { - if (r1 == 1) - r1 = N_RINGS; - else if (r1 == N_RINGS) - r1 = 1; - - iter[2] = '0' + static_cast(r1); - } else { - args = sscanf(iter.c_str(), "(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); - if (args >= 2) { - if (r1 == 1) - r1 = N_RINGS; - else if (r1 == N_RINGS) - r1 = 1; - - iter[1] = '0' + static_cast(r1); - } - } - } - } - } -} - -void MillGame::rotate(int degrees, bool cmdChange /*= true*/) -{ - // 将degrees转化为0~359之间的数 - degrees = degrees % 360; - - if (degrees < 0) - degrees += 360; - - if (degrees == 0 || degrees % 90) - return; - - degrees /= 45; - - int ch1, ch2; - int r, s; - int i; - - if (degrees == 2) { - for (r = 1; r <= N_RINGS; r++) { - ch1 = board_[r * N_SEATS]; - ch2 = board_[r * N_SEATS + 1]; - - for (s = 0; s < N_SEATS - 2; s++) { - board_[r * N_SEATS + s] = board_[r * N_SEATS + s + 2]; - } - - board_[r * N_SEATS + 6] = ch1; - //updateHash(i * N_SEATS + 6); - board_[r * N_SEATS + 7] = ch2; - //updateHash(i * N_SEATS + 7); - } - } else if (degrees == 6) { - for (r = 1; r <= N_RINGS; r++) { - ch1 = board_[r * N_SEATS + 7]; - ch2 = board_[r * N_SEATS + 6]; - - for (s = N_SEATS - 1; s >= 2; s--) { - board_[r * N_SEATS + s] = board_[r * N_SEATS + s - 2]; - //updateHash(i * N_SEATS + j); - } - - board_[r * N_SEATS + 1] = ch1; - //updateHash(i * N_SEATS + 1); - board_[r * N_SEATS] = ch2; - //updateHash(i * N_SEATS); - } - } else if (degrees == 4) { - for (r = 1; r <= N_RINGS; r++) { - for (s = 0; s < N_SEATS / 2; s++) { - ch1 = board_[r * N_SEATS + s]; - board_[r * N_SEATS + s] = board_[r * N_SEATS + s + 4]; - //updateHash(i * N_SEATS + j); - board_[r * N_SEATS + s + 4] = ch1; - //updateHash(i * N_SEATS + j + 4); - } - } - } else { - return; - } - - uint64_t llp[3] = { 0 }; - - if (move_ < 0) { - r = (-move_) / N_SEATS; - s = (-move_) % N_SEATS; - s = (s + N_SEATS - degrees) % N_SEATS; - move_ = -(r * N_SEATS + s); - } else { - llp[0] = static_cast(move_ >> 8); - llp[1] = move_ & 0x00ff; - r = static_cast(llp[0]) / N_SEATS; - s = static_cast(llp[0]) % N_SEATS; - s = (s + N_SEATS - degrees) % N_SEATS; - llp[0] = static_cast(r * N_SEATS + s); - r = static_cast(llp[1]) / N_SEATS; - s = static_cast(llp[1]) % N_SEATS; - s = (s + N_SEATS - degrees) % N_SEATS; - llp[1] = static_cast(r * N_SEATS + s); - move_ = static_cast(((llp[0] << 8) | llp[1])); - } - - if (currentPos != 0) { - r = currentPos / N_SEATS; - s = currentPos % N_SEATS; - s = (s + N_SEATS - degrees) % N_SEATS; - currentPos = r * N_SEATS + s; - } - - if (currentRule.allowRemovePiecesRepeatedly) { - for (auto &mill : context.millList){ - llp[0] = (mill & 0x000000ff00000000) >> 32; - llp[1] = (mill & 0x0000000000ff0000) >> 16; - llp[2] = (mill & 0x00000000000000ff); - - for (i = 0; i < 3; i++) { - r = static_cast(llp[i]) / N_SEATS; - s = static_cast(llp[i]) % N_SEATS; - s = (s + N_SEATS - degrees) % N_SEATS; - llp[i] = static_cast(r * N_SEATS + s); - } - - mill &= 0xffffff00ff00ff00; - mill |= (llp[0] << 32) | (llp[1] << 16) | llp[2]; - } - } - - // 命令行解析 - if (cmdChange) { - int r1, s1, r2, s2; - int args = 0; - int mm = 0, ss = 0; - - args = sscanf(cmdline, "(%1u,%1u)->(%1u,%1u) %2u:%2u", &r1, &s1, &r2, &s2, &mm, &ss); - if (args >= 4) { - s1 = (s1 - 1 + N_SEATS - degrees) % N_SEATS; - s2 = (s2 - 1 + N_SEATS - degrees) % N_SEATS; - cmdline[3] = '1' + static_cast(s1); - cmdline[10] = '1' + static_cast(s2); - } else { - args = sscanf(cmdline, "-(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); - - if (args >= 2) { - s1 = (s1 - 1 + N_SEATS - degrees) % N_SEATS; - cmdline[4] = '1' + static_cast(s1); - } else { - args = sscanf(cmdline, "(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); - - if (args >= 2) { - s1 = (s1 - 1 + N_SEATS - degrees) % N_SEATS; - cmdline[3] = '1' + static_cast(s1); - } - } - } - - for (auto &iter : cmdlist) { - args = sscanf(iter.c_str(), "(%1u,%1u)->(%1u,%1u) %2u:%2u", &r1, &s1, &r2, &s2, &mm, &ss); - - if (args >= 4) { - s1 = (s1 - 1 + N_SEATS - degrees) % N_SEATS; - s2 = (s2 - 1 + N_SEATS - degrees) % N_SEATS; - iter[3] = '1' + static_cast(s1); - iter[10] = '1' + static_cast(s2); - } else { - args = sscanf(iter.c_str(), "-(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); - - if (args >= 2) { - s1 = (s1 - 1 + N_SEATS - degrees) % N_SEATS; - iter[4] = '1' + static_cast(s1); - } else { - args = sscanf(iter.c_str(), "(%1u,%1u) %2u:%2u", &r1, &s1, &mm, &ss); - if (args >= 2) { - s1 = (s1 - 1 + N_SEATS - degrees) % N_SEATS; - iter[3] = '1' + static_cast(s1); - } - } - } - } - } -} - #if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) #if 0 @@ -2238,7 +1214,7 @@ void MillGame::constructHash() context.hash = 0; #include "zobrist.h" - memcpy(context.zobrist, zobrist0, sizeof(hash_t) * MillGame::N_POINTS * MillGame::POINT_TYPE_COUNT); + memcpy(context.zobrist, zobrist0, sizeof(hash_t) * Board::N_POINTS * POINT_TYPE_COUNT); #if 0 // 预留末8位后续填充局面特征标志 @@ -2253,7 +1229,7 @@ void MillGame::constructHash() #endif } -MillGame::hash_t MillGame::getHash() +hash_t MillGame::getHash() { // TODO: 每次获取哈希值时更新 hash 值低8位,放在此处调用不优雅 updateHashMisc(); @@ -2261,7 +1237,7 @@ MillGame::hash_t MillGame::getHash() return context.hash; } -MillGame::hash_t MillGame::updateHash(int pos) +hash_t MillGame::updateHash(int pos) { // PieceType is board_[pos] @@ -2274,12 +1250,12 @@ MillGame::hash_t MillGame::updateHash(int pos) return context.hash; } -MillGame::hash_t MillGame::revertHash(int pos) +hash_t MillGame::revertHash(int pos) { return updateHash(pos); } -MillGame::hash_t MillGame::updateHashMisc() +hash_t MillGame::updateHashMisc() { // 清除标记位 context.hash &= static_cast(~0xFF); @@ -2294,10 +1270,9 @@ MillGame::hash_t MillGame::updateHashMisc() context.hash |= 1U << 1; } - context.hash |= static_cast(context.nPiecesNeedRemove) << 2; - context.hash |= static_cast(context.nPiecesInHand_1) << 4; // TODO: 或许换 game.stage 也可以? + context.hash |= static_cast(context.nPiecesNeedRemove) << 2; + context.hash |= static_cast(context.nPiecesInHand_1) << 4; // TODO: 或许换 game.stage 也可以? return context.hash; } #endif /* HASH_MAP_ENABLE etc. */ - diff --git a/src/game/millgame.h b/src/game/millgame.h index 8a49e92c..f4ef6a42 100644 --- a/src/game/millgame.h +++ b/src/game/millgame.h @@ -27,9 +27,54 @@ #include #include "config.h" +#include "types.h" +#include "rule.h" +#include "board.h" -using std::string; -using std::list; +using namespace std; + + +// 棋局结构体,算法相关,包含当前棋盘数据 +// 单独分离出来供AI判断局面用,生成置换表时使用 +class ChessContext +{ +public: + Board board; + +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) + // 局面的哈希值 + hash_t hash{}; + + // Zobrist 数组 + hash_t zobrist[Board::N_POINTS][POINT_TYPE_COUNT]{}; +#endif /* HASH_MAP_ENABLE */ + + // 局面阶段标识 + enum GameStage stage; + + // 轮流状态标识 + enum Player turn; + + // 动作状态标识 + enum Action action + { + }; + + // 玩家1剩余未放置子数 + int nPiecesInHand_1{}; + + // 玩家2剩余未放置子数 + int nPiecesInHand_2{}; + + // 玩家1盘面剩余子数 + int nPiecesOnBoard_1{}; + + // 玩家1盘面剩余子数 + int nPiecesOnBoard_2{}; + + // 尚待去除的子数 + int nPiecesNeedRemove{}; +}; // 棋类(在数据模型内,玩家只分先后手,不分黑白) // 注意:MillGame类不是线程安全的! @@ -40,118 +85,11 @@ class MillGame friend class MillGameAi_ab; public: - // 静态成员常量 - // 3圈,禁止修改! - static const int N_RINGS = 3; - - // 8位,禁止修改! - static const int N_SEATS = 8; - - // 横直斜3个方向,禁止修改! - static const int N_DIRECTIONS = 3; - - // 棋盘点的个数:40 - static const int N_POINTS = (MillGame::N_RINGS + 2) * MillGame::N_SEATS; - - // 移动方向,包括顺时针、逆时针、向内、向外4个方向 - enum MoveDirection - { - MOVE_DIRECTION_CLOCKWISE = 0, // 顺时针 - MOVE_DIRECTION_ANTICLOCKWISE = 1, // 逆时针 - MOVE_DIRECTION_INWARD = 2, // 向内 - MOVE_DIRECTION_OUTWARD = 3, // 向外 - MOVE_DIRECTION_FLY = 4, // 飞子 - N_MOVE_DIRECTIONS = 4 // 移动方向数 - }; - - // 遍历棋盘点所用的起始位置,即 [8, 32) - static const int POS_BEGIN = N_SEATS; - static const int POS_END = ((N_RINGS + 1) * N_SEATS); - - // 预定义的规则数目 - static const int N_RULES = 4; - - // 定义类型 - using move_t = int32_t; - using step_t = uint16_t; - -#ifdef HASH_MAP_CUTDOWN - using hash_t = uint32_t; -#else - using hash_t = uint64_t; -#endif /* HASH_MAP_CUTDOWN */ - - // 位置迭代器 - // typedef typename std::vector::iterator posIterator; - // typedef typename std::vector::const_iterator constPosIterator; - // 赢盘数 int score_1 {}; int score_2 {}; int score_draw {}; - // 嵌套的规则结构体 - struct Rule - { - // 规则名称 - const char *name; - - // 规则介绍 - const char *description; - - // 任一方子数,各9子或各12子 - int nTotalPiecesEachSide; - - // 赛点子数,少于则判负 - int nPiecesAtLeast; - - // 是否有斜线 - bool hasObliqueLines; - - // 是否有禁点(摆棋阶段被提子的点不能再摆子) - bool hasForbiddenPoint; - - // 是否后摆棋者先行棋 - bool isDefenderMoveFirst; - - // 相同顺序和位置的重复“三连”是否可反复提子 - bool allowRemovePiecesRepeatedly; - - // 多个“三连”能否多提子 - bool allowRemoveMultiPieces; - - // 能否提“三连”的子 - bool allowRemoveMill; - - // 摆棋满子(闷棋,只有12子棋才出现),是否算先手负,false为和棋 - bool isStartingPlayerLoseWhenBoardFull; - - // 走棋阶段不能行动(被“闷”)是否算负,false则轮空(由对手走棋) - bool isLoseWhenNoWay; - - // 剩三子时是否可以飞棋 - bool allowFlyWhenRemainThreePieces; - - // 最大步数,超出判和 - step_t maxStepsLedToDraw; - - // 包干最长时间(秒),超出判负,为0则不计时 - int maxTimeLedToLose; - }; - - // 预定义的规则 - static const struct Rule RULES[N_RULES]; - - // 局面阶段标识 - enum GameStage : uint16_t - { - GAME_NONE = 0x0000, - GAME_NOTSTARTED = 0x0001, // 未开局 - GAME_PLACING = 0x0002, // 开局(摆棋) - GAME_MOVING = 0x0004, // 中局(走棋) - GAME_OVER = 0x0008 // 结局 - }; - uint64_t rand64() { return static_cast(rand()) ^ (static_cast(rand()) << 15) ^ @@ -165,130 +103,11 @@ public: return rand64() << 8; } - // 玩家标识, 轮流状态, 胜负标识 - enum Player : uint8_t - { - PLAYER1 = 0x10, // 玩家1 - PLAYER2 = 0x20, // 玩家2 - DRAW = 0x40, // 双方和棋 - NOBODY = 0x80 // 胜负未分 - }; - static int playerToId(enum Player player); static Player getOpponent(enum Player player); - // 动作状态标识 - enum Action : uint16_t - { - ACTION_NONE = 0x0000, - ACTION_CHOOSE = 0x0100, // 选子 - ACTION_PLACE = 0x0200, // 落子 - ACTION_CAPTURE = 0x0400 // 提子 - }; - - // 棋盘点上棋子的类型 - enum PointType : uint16_t - { - POINT_TYPE_EMPTY = 0, // 没有棋子 - POINT_TYPE_PLAYER1 = 1, // 先手的子 - POINT_TYPE_PLAYER2 = 2, // 后手的子 - POINT_TYPE_FORBIDDEN = 3, // 禁点 - POINT_TYPE_COUNT = 4 - }; - - // 棋局结构体,算法相关,包含当前棋盘数据 - // 单独分离出来供AI判断局面用,生成置换表时使用 - struct ChessContext - { - // 棋局,抽象为一个(5×8)的数组,上下两行留空 - /* - 0x00 代表无棋子 - 0x0F 代表禁点 - 0x11~0x1C 代表先手第 1~12 子 - 0x21~0x2C 代表后手第 1~12 子 - 判断棋子是先手的用 (board[i] & 0x10) - 判断棋子是后手的用 (board[i] & 0x20) - */ - int board[N_POINTS] {}; - -#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) - // 局面的哈希值 - hash_t hash {}; - - // 标记处于走子阶段的哈希 - hash_t gameMovingHash {}; - - // 吃子动作的哈希 - hash_t actionCaptureHash {}; - - // 标记轮到玩家2行棋的哈希 - hash_t player2sTurnHash {}; - - // Zobrist 数组 - hash_t zobrist[N_POINTS][POINT_TYPE_COUNT] {}; -#endif /* HASH_MAP_ENABLE */ - - // 局面阶段标识 - enum MillGame::GameStage stage; - - // 轮流状态标识 - enum MillGame::Player turn; - - // 动作状态标识 - enum MillGame::Action action {}; - - // 玩家1剩余未放置子数 - int nPiecesInHand_1 {}; - - // 玩家2剩余未放置子数 - int nPiecesInHand_2 {}; - - // 玩家1盘面剩余子数 - int nPiecesOnBoard_1 {}; - - // 玩家1盘面剩余子数 - int nPiecesOnBoard_2 {}; - - // 尚待去除的子数 - int nPiecesNeedRemove {}; - -#if 0 - 本打算用如下的结构体来表示“三连” - struct Mill { - char piece1; // “三连”中最小的棋子 - char pos1; // 最小棋子的位置 - char piece2; // 次小的棋子 - char pos2; // 次小棋子的位置 - char piece3; // 最大的棋子 - char pos3; // 最大棋子的位置 - }; - 但为了提高执行效率改用一个64位整数了,规则如下 - 0x 00 00 00 00 00 00 00 00 - unused unused piece1 pos1 piece2 pos2 piece3 pos3 -#endif - - // “三连列表” - list millList; - }; - private: - // 空棋盘点位,用于判断一个棋子位置是否在棋盘上 - static const int onBoard[N_POINTS]; - - // 着法表,每个位置有最多4种走法:顺时针、逆时针、向内、向外 - // 这个表跟规则有关,一旦规则改变需要重新修改 - static int moveTable[N_POINTS][N_MOVE_DIRECTIONS]; - - // 成三表,表示棋盘上各个位置有成三关系的对应位置表 - // 这个表跟规则有关,一旦规则改变需要重新修改 - static int millTable[N_POINTS][N_DIRECTIONS][N_RINGS - 1]; - - // 生成着法表 - void createMoveTable(); - - // 生成成三表 - void createMillTable(); // 创建哈希值 void constructHash(); @@ -331,15 +150,9 @@ public: // 获取棋盘数据 const int *getBoard() const { - return context.board; + return context.board.board_; } - // 获取棋子位置(r, s) - bool getPieceRS(const Player &player, const int &number, int &r, int &s); - - // 获取当前棋子 - bool getCurrentPiece(Player &player, int &number); - // 获取当前棋子位置点 int getCurrentPos() const { @@ -409,9 +222,6 @@ public: return tips; } - // 获取位置点棋子的归属人 - enum Player getWhosPiece(int r, int s); - // 获取当前着法 const char *getCmdLine() const { @@ -467,7 +277,7 @@ public: } // 计算玩家1和玩家2的棋子活动能力之差 - int getMobilityDiff(bool includeFobidden); + int getMobilityDiff(enum Player turn, const Rule &rule, int nPiecesOnBoard_1, int nPiecesOnBoard_2, bool includeFobidden); // 游戏重置 bool reset(); @@ -490,42 +300,6 @@ public: // 命令行解析函数 bool command(const char *cmd); - // 局面左右镜像 - void mirror(bool cmdChange = true); - - // 局面内外翻转 - void turn(bool cmdChange = true); - - // 局面逆时针旋转 - void rotate(int degrees, bool cmdChange = true); - - // 判断棋盘pos处的棋子处于几个“三连”中 - int isInMills(int pos, bool test = false); - - // 判断玩家的所有棋子是否都处于“三连”状态 - bool isAllInMills(char ch); - bool isAllInMills(enum Player); - - // 判断玩家的棋子周围有几个空位 - int getSurroundedEmptyPosCount(int pos, bool includeFobidden); - - // 判断玩家的棋子是否被围 - bool isSurrounded(int pos); - - // 判断玩家的棋子是否全部被围 - bool isAllSurrounded(char ch); - - bool isAllSurrounded(enum Player); - - // 三连加入列表 - int addMills(int pos); - - // 将棋盘下标形式转化为第r圈,第s位,r和s下标都从1开始 - void pos2rs(int pos, int &r, int &s); - - // 将第c圈,第p位转化为棋盘下标形式,r和s下标都从1开始 - int rs2Pos(int r, int s); - // 更新时间和状态,用内联函数以提高效率 inline int update(int time_p = -1); @@ -537,7 +311,7 @@ public: void cleanForbiddenPoints(); // 改变轮流 - enum MillGame::Player changeTurn(); + enum Player changeTurn(); // 设置提示 void setTips(); @@ -558,7 +332,7 @@ public: public: /* TODO: move to private */ // 棋局上下文 - struct ChessContext context; + ChessContext context; // 当前使用的规则 struct Rule currentRule @@ -568,13 +342,41 @@ public: /* TODO: move to private */ // 棋局上下文中的棋盘数据,单独提出来 int *board_; + // 棋谱 + list cmdlist; + + // 着法命令行用于棋谱的显示和解析, 当前着法的命令行指令,即一招棋谱 + char cmdline[64]{}; + + /* + 当前着法,AI会用到,如下表示 + 0x 00 00 + pos1 pos2 + 开局落子:0x00??,??为棋盘上的位置 + 移子:0x__??,__为移动前的位置,??为移动后的位置 + 去子:0xFF??,??取位置补码,即为负数 + + 31 ----- 24 ----- 25 + | \ | / | + | 23 -- 16 -- 17 | + | | \ | / | | + | | 15 08 09 | | + 30-22-14 10-18-26 + | | 13 12 11 | | + | | / | \ | | + | 21 -- 20 -- 19 | + | / | \ | + 29 ----- 28 ----- 27 + */ + int32_t move_{}; + + // 选中的棋子在board中的位置 + int currentPos{}; + private: // 棋局哈希值 // uint64_t hash; - // 选中的棋子在board中的位置 - int currentPos {}; - // 胜负标识 enum Player winner; @@ -602,35 +404,6 @@ private: // 玩家2用时(秒) time_t elapsedSeconds_2 {}; - /* 当前着法,AI会用到,如下表示 - 0x 00 00 - pos1 pos2 - 开局落子:0x00??,??为棋盘上的位置 - 移子:0x__??,__为移动前的位置,??为移动后的位置 - 去子:0xFF??,??取位置补码,即为负数 - */ - /* - 31 ----- 24 ----- 25 - | \ | / | - | 23 -- 16 -- 17 | - | | \ | / | | - | | 15 08 09 | | - 30-22-14 10-18-26 - | | 13 12 11 | | - | | / | \ | | - | 21 -- 20 -- 19 | - | / | \ | - 29 ----- 28 ----- 27 - */ - int32_t move_ {}; - - // 着法命令行用于棋谱的显示和解析 - // 当前着法的命令行指令,即一招棋谱 - char cmdline[64] {}; - - // 棋谱 - list cmdlist; - // 当前棋局的字符提示 string tips; }; diff --git a/src/game/rule.cpp b/src/game/rule.cpp new file mode 100644 index 00000000..24efde07 --- /dev/null +++ b/src/game/rule.cpp @@ -0,0 +1,114 @@ +/***************************************************************************** + * Copyright (C) 2018-2019 MillGame authors + * + * Authors: liuweilhy + * Calcitem + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include "rule.h" +#include "types.h" + +// 对静态常量数组的定义要放在类外,不要放在头文件 +// 预定义的4套规则 +const struct Rule RULES[N_RULES] = { + { + "成三棋", // 成三棋 + // 规则说明 + "1. 双方各9颗子,开局依次摆子;\n" + "2. 凡出现三子相连,就提掉对手一子;\n" + "3. 不能提对手的“三连”子,除非无子可提;\n" + "4. 同时出现两个“三连”只能提一子;\n" + "5. 摆完后依次走子,每次只能往相邻位置走一步;\n" + "6. 把对手棋子提到少于3颗时胜利;\n" + "7. 走棋阶段不能行动(被“闷”)算负。", + 9, // 双方各9子 + 3, // 赛点子数为3 + false, // 没有斜线 + false, // 没有禁点,摆棋阶段被提子的点可以再摆子 + false, // 先摆棋者先行棋 + true, // 可以重复成三 + false, // 多个“三连”只能提一子 + false, // 不能提对手的“三连”子,除非无子可提; + true, // 摆棋满子(闷棋,只有12子棋才出现)算先手负 + true, // 走棋阶段不能行动(被“闷”)算负 + false, // 剩三子时不可以飞棋 + 0, // 不计步数 + 0 // 不计时 + }, + { + "打三棋(12连棋)", // 打三棋 + // 规则说明 + "1. 双方各12颗子,棋盘有斜线;\n" + "2. 摆棋阶段被提子的位置不能再摆子,直到走棋阶段;\n" + "3. 摆棋阶段,摆满棋盘算先手负;\n" + "4. 走棋阶段,后摆棋的一方先走;\n" + "5. 同时出现两个“三连”只能提一子;\n" + "6. 其它规则与成三棋基本相同。", + 12, // 双方各12子 + 3, // 赛点子数为3 + true, // 有斜线 + true, // 有禁点,摆棋阶段被提子的点不能再摆子 + true, // 后摆棋者先行棋 + true, // 可以重复成三 + false, // 多个“三连”只能提一子 + true, // 可以提对手的“三连”子 + true, // 摆棋满子(闷棋,只有12子棋才出现)算先手负 + true, // 走棋阶段不能行动(被“闷”)算负 + false, // 剩三子时不可以飞棋 + 50, // 不计步数 + 0 // 不计时 + }, + { + "九连棋", // 九连棋 + // 规则说明 + "1. 规则与成三棋基本相同,只是它的棋子有序号,\n" + "2. 相同序号、位置的“三连”不能重复提子;\n" + "3. 走棋阶段不能行动(被“闷”),则由对手继续走棋;\n" + "4. 一步出现几个“三连”就可以提几个子。", + 9, // 双方各9子 + 3, // 赛点子数为3 + false, // 没有斜线 + false, // 没有禁点,摆棋阶段被提子的点可以再摆子 + false, // 先摆棋者先行棋 + false, // 不可以重复成三 + true, // 出现几个“三连”就可以提几个子 + false, // 不能提对手的“三连”子,除非无子可提; + true, // 摆棋满子(闷棋,只有12子棋才出现)算先手负 + false, // 走棋阶段不能行动(被“闷”),则由对手继续走棋 + false, // 剩三子时不可以飞棋 + 0, // 不计步数 + 0 // 不计时 + }, + { + "莫里斯九子棋", // 莫里斯九子棋 + // 规则说明 + "规则与成三棋基本相同,只是在走子阶段,当一方仅剩3子时,他可以飞子到任意空位。", + 9, // 双方各9子 + 3, // 赛点子数为3 + false, // 没有斜线 + false, // 没有禁点,摆棋阶段被提子的点可以再摆子 + false, // 先摆棋者先行棋 + true, // 可以重复成三 + false, // 多个“三连”只能提一子 + false, // 不能提对手的“三连”子,除非无子可提; + true, // 摆棋满子(闷棋,只有12子棋才出现)算先手负 + true, // 走棋阶段不能行动(被“闷”)算负 + true, // 剩三子时可以飞棋 + 0, // 不计步数 + 0 // 不计时 + } +}; diff --git a/src/game/rule.h b/src/game/rule.h new file mode 100644 index 00000000..c3fc1a41 --- /dev/null +++ b/src/game/rule.h @@ -0,0 +1,82 @@ +/***************************************************************************** + * Copyright (C) 2018-2019 MillGame authors + * + * Authors: liuweilhy + * Calcitem + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef RULE_H +#define RULE_H + +#include "types.h" + +struct Rule +{ + // 规则名称 + const char *name; + + // 规则介绍 + const char *description; + + // 任一方子数,各9子或各12子 + int nTotalPiecesEachSide; + + // 赛点子数,少于则判负 + int nPiecesAtLeast; + + // 是否有斜线 + bool hasObliqueLines; + + // 是否有禁点(摆棋阶段被提子的点不能再摆子) + bool hasForbiddenPoint; + + // 是否后摆棋者先行棋 + bool isDefenderMoveFirst; + + // 相同顺序和位置的重复“三连”是否可反复提子 + bool allowRemovePiecesRepeatedly; + + // 多个“三连”能否多提子 + bool allowRemoveMultiPieces; + + // 能否提“三连”的子 + bool allowRemoveMill; + + // 摆棋满子(闷棋,只有12子棋才出现),是否算先手负,false为和棋 + bool isStartingPlayerLoseWhenBoardFull; + + // 走棋阶段不能行动(被“闷”)是否算负,false则轮空(由对手走棋) + bool isLoseWhenNoWay; + + // 剩三子时是否可以飞棋 + bool allowFlyWhenRemainThreePieces; + + // 最大步数,超出判和 + step_t maxStepsLedToDraw; + + // 包干最长时间(秒),超出判负,为0则不计时 + int maxTimeLedToLose; +}; + +// 预定义的规则数目 +static const int N_RULES = 4; + +// 预定义的规则 +extern const struct Rule RULES[N_RULES]; + +#endif /* RULE_H */ + diff --git a/src/game/types.h b/src/game/types.h new file mode 100644 index 00000000..c13a01f9 --- /dev/null +++ b/src/game/types.h @@ -0,0 +1,87 @@ +/***************************************************************************** + * Copyright (C) 2018-2019 MillGame authors + * + * Authors: liuweilhy + * Calcitem + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef TYPES_H +#define TYPES_H + +#include "config.h" + +using move_t = int32_t; +using step_t = uint16_t; +using value_t = int16_t; +using depth_t = uint8_t; + +#ifdef HASH_MAP_CUTDOWN +using hash_t = uint32_t; +#else +using hash_t = uint64_t; +#endif /* HASH_MAP_CUTDOWN */ + +// 移动方向,包括顺时针、逆时针、向内、向外4个方向 +enum MoveDirection +{ + MOVE_DIRECTION_CLOCKWISE = 0, // 顺时针 + MOVE_DIRECTION_ANTICLOCKWISE = 1, // 逆时针 + MOVE_DIRECTION_INWARD = 2, // 向内 + MOVE_DIRECTION_OUTWARD = 3, // 向外 + MOVE_DIRECTION_FLY = 4, // 飞子 + N_MOVE_DIRECTIONS = 4 // 移动方向数 +}; + +// 棋盘点上棋子的类型 +enum PointType : uint16_t +{ + POINT_TYPE_EMPTY = 0, // 没有棋子 + POINT_TYPE_PLAYER1 = 1, // 先手的子 + POINT_TYPE_PLAYER2 = 2, // 后手的子 + POINT_TYPE_FORBIDDEN = 3, // 禁点 + POINT_TYPE_COUNT = 4 +}; + +// 玩家标识, 轮流状态, 胜负标识 +enum Player : uint8_t +{ + PLAYER1 = 0x10, // 玩家1 + PLAYER2 = 0x20, // 玩家2 + PLAYER_DRAW = 0x40, // 双方和棋 + PLAYER_NOBODY = 0x80 // 胜负未分 +}; + +// 局面阶段标识 +enum GameStage : uint16_t +{ + GAME_NONE = 0x0000, + GAME_NOTSTARTED = 0x0001, // 未开局 + GAME_PLACING = 0x0002, // 开局(摆棋) + GAME_MOVING = 0x0004, // 中局(走棋) + GAME_OVER = 0x0008 // 结局 +}; + +// 动作状态标识 +enum Action : uint16_t +{ + ACTION_NONE = 0x0000, + ACTION_CHOOSE = 0x0100, // 选子 + ACTION_PLACE = 0x0200, // 落子 + ACTION_CAPTURE = 0x0400 // 提子 +}; + +#endif /* TYPES_H */ diff --git a/src/ui/qt/boarditem.cpp b/src/ui/qt/boarditem.cpp index a8875ebe..4620f326 100644 --- a/src/ui/qt/boarditem.cpp +++ b/src/ui/qt/boarditem.cpp @@ -136,7 +136,7 @@ void BoardItem::paint(QPainter *painter, } } -#ifdef DRAW_SEAT_NUMBER +#ifdef PLAYER_DRAW_SEAT_NUMBER // 画 Seat 编号 QPen fontPen(QBrush(Qt::white), LINE_WEIGHT, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin); painter->setPen(fontPen); @@ -151,7 +151,7 @@ void BoardItem::paint(QPainter *painter, QString strSeat(cSeat); painter->drawText(position[(N_RINGS - 1) * N_SEATS + i], strSeat); } -#endif // DRAW_SEAT_NUMBER +#endif // PLAYER_DRAW_SEAT_NUMBER } QPointF BoardItem::nearestPosition(QPointF const pos) diff --git a/src/ui/qt/gamecontroller.cpp b/src/ui/qt/gamecontroller.cpp index 183eabd6..6fdb7c40 100644 --- a/src/ui/qt/gamecontroller.cpp +++ b/src/ui/qt/gamecontroller.cpp @@ -104,11 +104,11 @@ const QMap GameController::getActions() // 之所以不用信号和槽的模式,是因为发信号的时候槽还来不及关联 QMap actions; - for (int i = 0; i < MillGame::N_RULES; i++) { + for (int i = 0; i < N_RULES; i++) { // QMap的key存放int索引值,value存放规则名称和规则提示 QStringList strlist; - strlist.append(tr(MillGame::RULES[i].name)); - strlist.append(tr(MillGame::RULES[i].description)); + strlist.append(tr(RULES[i].name)); + strlist.append(tr(RULES[i].description)); actions.insert(i, strlist); } @@ -137,8 +137,8 @@ void GameController::gameReset() timeID = 0; // 棋未下完,则算对手得分 - if (chess_.getStage() == MillGame::GAME_MOVING && - chess_.whoWin() == MillGame::NOBODY) { + if (chess_.getStage() == GAME_MOVING && + chess_.whoWin() == PLAYER_NOBODY) { giveUp(); } @@ -262,10 +262,10 @@ void GameController::setInvert(bool arg) } } -void GameController::setRule(int ruleNo, MillGame::step_t stepLimited /*= -1*/, int timeLimited /*= -1*/) +void GameController::setRule(int ruleNo, step_t stepLimited /*= -1*/, int timeLimited /*= -1*/) { // 更新规则,原限时和限步不变 - if (ruleNo < 0 || ruleNo >= MillGame::N_RULES) + if (ruleNo < 0 || ruleNo >= N_RULES) return; this->ruleNo_ = ruleNo; @@ -275,7 +275,7 @@ void GameController::setRule(int ruleNo, MillGame::step_t stepLimited /*= -1*/, } // 设置模型规则,重置游戏 - chess_.setContext(&MillGame::RULES[ruleNo], stepsLimit, timeLimit); + chess_.setContext(&RULES[ruleNo], stepsLimit, timeLimit); chessTemp = chess_; // 重置游戏 @@ -314,7 +314,7 @@ void GameController::setEngine2(bool arg) } } -void GameController::setAiDepthTime(MillGameAi_ab::depth_t depth1, int time1, MillGameAi_ab::depth_t depth2, int time2) +void GameController::setAiDepthTime(depth_t depth1, int time1, depth_t depth2, int time2) { if (isAiPlayer1) { ai1.stop(); @@ -336,7 +336,7 @@ void GameController::setAiDepthTime(MillGameAi_ab::depth_t depth1, int time1, Mi } } -void GameController::getAiDepthTime(MillGameAi_ab::depth_t &depth1, int &time1, MillGameAi_ab::depth_t &depth2, int &time2) +void GameController::getAiDepthTime(depth_t &depth1, int &time1, depth_t &depth2, int &time2) { ai1.getDepthTime(depth1, time1); ai2.getDepthTime(depth2, time2); @@ -398,8 +398,8 @@ void GameController::flip() ai2.wait(); } - chess_.mirror(); - chess_.rotate(180); + chess_.context.board.mirror(chess_.cmdlist, chess_.cmdline, chess_.move_, chess_.currentRule, chess_.currentPos); + chess_.context.board.rotate(180, chess_.cmdlist, chess_.cmdline, chess_.move_, chess_.currentRule, chess_.currentPos); chessTemp = chess_; // 更新棋谱 @@ -438,7 +438,7 @@ void GameController::mirror() ai2.wait(); } - chess_.mirror(); + chess_.context.board.mirror(chess_.cmdlist, chess_.cmdline, chess_.move_, chess_.currentRule, chess_.currentPos); chessTemp = chess_; // 更新棋谱 @@ -480,7 +480,7 @@ void GameController::turnRight() ai2.wait(); } - chess_.rotate(-90); + chess_.context.board.rotate(-90, chess_.cmdlist, chess_.cmdline, chess_.move_, chess_.currentRule, chess_.currentPos); chessTemp = chess_; // 更新棋谱 @@ -520,7 +520,7 @@ void GameController::turnLeft() ai2.wait(); } - chess_.rotate(90); + chess_.context.board.rotate(90, chess_.cmdlist, chess_.cmdline, chess_.move_, chess_.currentRule, chess_.currentPos); chessTemp = chess_; // 更新棋谱 @@ -564,7 +564,7 @@ void GameController::timerEvent(QTimerEvent *event) emit time2Changed(qt2.toString("hh:mm:ss")); // 如果胜负已分 - if (chess_.whoWin() != MillGame::NOBODY) { + if (chess_.whoWin() != PLAYER_NOBODY) { // 停止计时 killTimer(timeID); @@ -609,8 +609,8 @@ void GameController::timerEvent(QTimerEvent *event) bool GameController::isAIsTurn() { - return ((chess_.whosTurn() == MillGame::PLAYER1 && isAiPlayer1) || - (chess_.whosTurn() == MillGame::PLAYER2 && isAiPlayer2)); + return ((chess_.whosTurn() == PLAYER1 && isAiPlayer1) || + (chess_.whosTurn() == PLAYER2 && isAiPlayer2)); } // 关键槽函数,根据QGraphicsScene的信号和状态来执行选子、落子或去子 @@ -647,7 +647,7 @@ bool GameController::actionPiece(QPointF pos) manualListModel.removeRows(currentRow + 1, manualListModel.rowCount() - currentRow - 1); // 如果再决出胜负后悔棋,则重新启动计时 - if (chess_.whoWin() == MillGame::NOBODY) { + if (chess_.whoWin() == PLAYER_NOBODY) { // 重新启动计时 timeID = startTimer(100); @@ -664,7 +664,7 @@ bool GameController::actionPiece(QPointF pos) } // 如果未开局则开局 - if (chess_.getStage() == MillGame::GAME_NOTSTARTED) + if (chess_.getStage() == GAME_NOTSTARTED) gameStart(); // 判断执行选子、落子或去子 @@ -673,9 +673,9 @@ bool GameController::actionPiece(QPointF pos) QGraphicsItem *item = scene.itemAt(pos, QTransform()); switch (chess_.getAction()) { - case MillGame::ACTION_PLACE: + case ACTION_PLACE: if (chess_._place(r, s)) { - if (chess_.getAction() == MillGame::ACTION_CAPTURE) { + if (chess_.getAction() == ACTION_CAPTURE) { // 播放成三音效 playSound(":/sound/resources/sound/capture.wav"); } else { @@ -689,7 +689,7 @@ bool GameController::actionPiece(QPointF pos) // 如果移子不成功,尝试重新选子,这里不break [[fallthrough]]; - case MillGame::ACTION_CHOOSE: + case ACTION_CHOOSE: piece = qgraphicsitem_cast(item); if (!piece) break; @@ -703,7 +703,7 @@ bool GameController::actionPiece(QPointF pos) } break; - case MillGame::ACTION_CAPTURE: + case ACTION_CAPTURE: if (chess_._capture(r, s)) { // 播放音效 playSound(":/sound/resources/sound/remove.wav"); @@ -739,7 +739,7 @@ bool GameController::actionPiece(QPointF pos) // 播放胜利或失败音效 #ifndef DONOT_PLAY_WIN_SOUND - if (chess_.whoWin() != MillGame::NOBODY && + if (chess_.whoWin() != PLAYER_NOBODY && (manualListModel.data(manualListModel.index(currentRow - 1))).toString().contains("Time over.")) playSound(":/sound/resources/sound/win.wav"); #endif @@ -747,8 +747,8 @@ bool GameController::actionPiece(QPointF pos) // AI设置 if (&chess_ == &(this->chess_)) { // 如果还未决出胜负 - if (chess_.whoWin() == MillGame::NOBODY) { - if (chess_.whosTurn() == MillGame::PLAYER1) { + if (chess_.whoWin() == PLAYER_NOBODY) { + if (chess_.whosTurn() == PLAYER1) { if (isAiPlayer1) { ai1.resume(); } @@ -782,11 +782,11 @@ bool GameController::giveUp() { bool result = false; - if (chess_.whosTurn() == MillGame::PLAYER1) { - result = chess_.giveup(MillGame::PLAYER1); + if (chess_.whosTurn() == PLAYER1) { + result = chess_.giveup(PLAYER1); } - else if (chess_.whosTurn() == MillGame::PLAYER2) { - result = chess_.giveup(MillGame::PLAYER2); + else if (chess_.whosTurn() == PLAYER2) { + result = chess_.giveup(PLAYER2); } if (result) { @@ -802,7 +802,7 @@ bool GameController::giveUp() manualListModel.insertRow(++currentRow); manualListModel.setData(manualListModel.index(currentRow), i.c_str()); } - if (chess_.whoWin() != MillGame::NOBODY) + if (chess_.whoWin() != PLAYER_NOBODY) playSound(":/sound/resources/sound/loss.wav"); } @@ -825,11 +825,11 @@ bool GameController::command(const QString &cmd, bool update /* = true */) QString sound; switch (chess_.getAction()) { - case MillGame::ACTION_CHOOSE: - case MillGame::ACTION_PLACE: + case ACTION_CHOOSE: + case ACTION_PLACE: sound = ":/sound/resources/sound/drog.wav"; break; - case MillGame::ACTION_CAPTURE: + case ACTION_CAPTURE: sound = ":/sound/resources/sound/remove.wav"; break; default: @@ -837,14 +837,14 @@ bool GameController::command(const QString &cmd, bool update /* = true */) } // 如果未开局则开局 - if (chess_.getStage() == MillGame::GAME_NOTSTARTED) { + if (chess_.getStage() == GAME_NOTSTARTED) { gameStart(); } if (!chess_.command(cmd.toStdString().c_str())) return false; - if (sound == ":/sound/resources/sound/drog.wav" && chess_.getAction() == MillGame::ACTION_CAPTURE) { + if (sound == ":/sound/resources/sound/drog.wav" && chess_.getAction() == ACTION_CAPTURE) { sound = ":/sound/resources/sound/capture.wav"; } @@ -882,7 +882,7 @@ bool GameController::command(const QString &cmd, bool update /* = true */) // 播放胜利或失败音效 #ifndef DONOT_PLAY_WIN_SOUND - if (chess_.whoWin() != MillGame::NOBODY && + if (chess_.whoWin() != PLAYER_NOBODY && (manualListModel.data(manualListModel.index(currentRow - 1))).toString().contains("Time over.")) { playSound(":/sound/resources/sound/win.wav"); } @@ -891,8 +891,8 @@ bool GameController::command(const QString &cmd, bool update /* = true */) // AI设置 if (&chess_ == &(this->chess_)) { // 如果还未决出胜负 - if (chess_.whoWin() == MillGame::NOBODY) { - if (chess_.whosTurn() == MillGame::PLAYER1) { + if (chess_.whoWin() == PLAYER_NOBODY) { + if (chess_.whosTurn() == PLAYER1) { if (isAiPlayer1) { ai1.resume(); } @@ -1004,9 +1004,9 @@ bool GameController::updateScence(MillGame &chess) int j; // 遍历棋盘,查找并放置棋盘上的棋子 - for (j = MillGame::POS_BEGIN; j < MillGame::POS_END; j++) { + for (j = Board::POS_BEGIN; j < Board::POS_END; j++) { if (board[j] == key) { - pos = scene.rs2pos(j / MillGame::N_SEATS, j % MillGame::N_SEATS + 1); + pos = scene.rs2pos(j / Board::N_SEATS, j % Board::N_SEATS + 1); if (piece->pos() != pos) { // 让移动的棋子位于顶层 @@ -1028,7 +1028,7 @@ bool GameController::updateScence(MillGame &chess) } // 如果没有找到,放置棋盘外的棋子 - if (j == (MillGame::N_SEATS) * (MillGame::N_RINGS + 1)) { + if (j == (Board::N_SEATS) * (Board::N_RINGS + 1)) { // 判断是被吃掉的子,还是未安放的子 if (key & 0x10) { pos = (key - 0x11 < nTotalPieces / 2 - chess.getPiecesInHandCount_1()) ? @@ -1043,7 +1043,7 @@ bool GameController::updateScence(MillGame &chess) deletedPiece = piece; #ifdef GAME_PLACING_SHOW_CAPTURED_PIECES - if (chess.getStage() == MillGame::GAME_MOVING) { + if (chess.getStage() == GAME_MOVING) { #endif QPropertyAnimation *animation = new QPropertyAnimation(piece, "pos"); animation->setDuration(durationTime); @@ -1061,10 +1061,10 @@ bool GameController::updateScence(MillGame &chess) } // 添加摆棋阶段禁子点 - if (chess.getRule()->hasForbiddenPoint && chess.getStage() == MillGame::GAME_PLACING) { - for (int j = MillGame::POS_BEGIN; j < MillGame::POS_END; j++) { + if (chess.getRule()->hasForbiddenPoint && chess.getStage() == GAME_PLACING) { + for (int j = Board::POS_BEGIN; j < Board::POS_END; j++) { if (board[j] == 0x0F) { - pos = scene.rs2pos(j / MillGame::N_SEATS, j % MillGame::N_SEATS + 1); + pos = scene.rs2pos(j / Board::N_SEATS, j % Board::N_SEATS + 1); if (nTotalPieces < pieceList.size()) { pieceList.at(nTotalPieces++)->setPos(pos); } else { @@ -1080,7 +1080,7 @@ bool GameController::updateScence(MillGame &chess) } // 走棋阶段清除禁子点 - if (chess.getRule()->hasForbiddenPoint && chess.getStage() != MillGame::GAME_PLACING) { + if (chess.getRule()->hasForbiddenPoint && chess.getStage() != GAME_PLACING) { while (nTotalPieces < pieceList.size()) { delete pieceList.at(nTotalPieces); pieceList.removeAt(nTotalPieces); diff --git a/src/ui/qt/gamecontroller.h b/src/ui/qt/gamecontroller.h index 06b055ea..fd3c6368 100644 --- a/src/ui/qt/gamecontroller.h +++ b/src/ui/qt/gamecontroller.h @@ -100,8 +100,8 @@ public: return &manualListModel; } - void setAiDepthTime(MillGameAi_ab::depth_t depth1, int time1, MillGameAi_ab::depth_t depth2, int time2); - void getAiDepthTime(MillGameAi_ab::depth_t &depth1, int &time1, MillGameAi_ab::depth_t &depth2, int &time2); + void setAiDepthTime(depth_t depth1, int time1, depth_t depth2, int time2); + void getAiDepthTime(depth_t &depth1, int &time1, depth_t &depth2, int &time2); signals: @@ -126,7 +126,7 @@ signals: public slots: // 设置规则 - void setRule(int ruleNo, MillGame::step_t stepLimited = UINT16_MAX, int timeLimited = -1); + void setRule(int ruleNo, step_t stepLimited = UINT16_MAX, int timeLimited = -1); // 游戏开始 void gameStart(); @@ -267,7 +267,7 @@ private: int timeLimit; // 规则限步数 - MillGame::step_t stepsLimit; + step_t stepsLimit; // 玩家1剩余时间(秒) time_t remainingTime1; diff --git a/src/ui/qt/gamewindow.cpp b/src/ui/qt/gamewindow.cpp index 5ab7a4df..522292ac 100644 --- a/src/ui/qt/gamewindow.cpp +++ b/src/ui/qt/gamewindow.cpp @@ -383,14 +383,14 @@ void MillGameWindow::ruleInfo() ui.labelRule->setText(tl + sl); // 规则提示 - ui.labelInfo->setToolTip(QString(MillGame::RULES[ruleNo].name) + "\n" + - MillGame::RULES[ruleNo].description); + ui.labelInfo->setToolTip(QString(RULES[ruleNo].name) + "\n" + + RULES[ruleNo].description); ui.labelRule->setToolTip(ui.labelInfo->toolTip()); #if 0 - QString tip_Rule = QString("%1\n%2").arg(tr(MillGame::RULES[ruleNo].name)) - .arg(tr(MillGame::RULES[ruleNo].info)); + QString tip_Rule = QString("%1\n%2").arg(tr(RULES[ruleNo].name)) + .arg(tr(RULES[ruleNo].info)); #endif } @@ -466,7 +466,7 @@ void MillGameWindow::on_actionLimited_T_triggered() int dTime = comboBox_time->currentData().toInt(); if (gStep != dStep || gTime != dTime) { // 重置游戏规则 - game->setRule(ruleNo, static_cast(dStep), dTime); + game->setRule(ruleNo, static_cast(dStep), dTime); } } @@ -922,7 +922,7 @@ void MillGameWindow::on_actionEngine_E_triggered() connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject())); // 目前数据 - MillGameAi_ab::depth_t depth1, depth2; + depth_t depth1, depth2; int time1, time2; game->getAiDepthTime(depth1, time1, depth2, time2); spinBox_depth1->setValue(depth1); @@ -932,11 +932,11 @@ void MillGameWindow::on_actionEngine_E_triggered() // 新设数据 if (dialog->exec() == QDialog::Accepted) { - MillGameAi_ab::depth_t depth1_new, depth2_new; + depth_t depth1_new, depth2_new; int time1_new, time2_new; - depth1_new = static_cast(spinBox_depth1->value()); - depth2_new = static_cast(spinBox_depth2->value()); + depth1_new = static_cast(spinBox_depth1->value()); + depth2_new = static_cast(spinBox_depth2->value()); time1_new = spinBox_time1->value(); time2_new = spinBox_time2->value();