diff --git a/src/ai/evaluate.cpp b/src/ai/evaluate.cpp index bbf2ff3b..3c133f70 100644 --- a/src/ai/evaluate.cpp +++ b/src/ai/evaluate.cpp @@ -20,7 +20,7 @@ #include "evaluate.h" #ifdef ALPHABETA_AI -value_t Evaluation::getValue(StateInfo *st, Position *position, Node *node) +value_t Evaluation::getValue(Position *position, Node *node) { // 初始评估值为0,对先手有利则增大,对后手有利则减小 value_t value = VALUE_ZERO; @@ -82,7 +82,7 @@ value_t Evaluation::getValue(StateInfo *st, Position *position, Node *node) #ifdef EVALUATE_MOBILITY // 按棋子活动能力计分 - value += st->getMobilityDiff(position->turn, position->nPiecesInHand[BLACK], position->nPiecesInHand[WHITE], false) * 10; + value += st->position->getMobilityDiff(position->turn, position->nPiecesInHand[BLACK], position->nPiecesInHand[WHITE], false) * 10; #endif /* EVALUATE_MOBILITY */ switch (position->action) { @@ -120,7 +120,7 @@ value_t Evaluation::getValue(StateInfo *st, Position *position, Node *node) // 走棋阶段被闷判断 else if (position->action == ACTION_CHOOSE && - st->position->board.isAllSurrounded(position->sideId, position->nPiecesOnBoard, position->sideToMove) && + position->board.isAllSurrounded(position->sideId, position->nPiecesOnBoard, position->sideToMove) && rule.isLoseWhenNoWay) { // 规则要求被“闷”判负,则对手获胜 value_t delta = position->sideToMove == PLAYER_BLACK ? -VALUE_WIN : VALUE_WIN; @@ -140,7 +140,7 @@ value_t Evaluation::getValue(StateInfo *st, Position *position, Node *node) break; } - if (st->position->sideToMove == PLAYER_WHITE) { + if (position->sideToMove == PLAYER_WHITE) { value = -value; } diff --git a/src/ai/evaluate.h b/src/ai/evaluate.h index d0669d7a..15481fb8 100644 --- a/src/ai/evaluate.h +++ b/src/ai/evaluate.h @@ -32,7 +32,7 @@ public: Evaluation &operator=(const Evaluation &) = delete; - static value_t getValue(StateInfo *st, Position *Position, Node *node); + static value_t getValue(Position *Position, Node *node); // 评估子力 #ifdef EVALUATE_ENABLE diff --git a/src/ai/mcts.cpp b/src/ai/mcts.cpp index 6e683f10..9050c27d 100644 --- a/src/ai/mcts.cpp +++ b/src/ai/mcts.cpp @@ -16,7 +16,7 @@ #ifdef MCTS_AI -void StateInfo::doRandomMove(Node *node, mt19937_64 *engine) +void Position::doRandomMove(Node *node, mt19937_64 *engine) { assert(hasMoves()); checkInvariant(); @@ -43,7 +43,7 @@ void StateInfo::doRandomMove(Node *node, mt19937_64 *engine) doMove(m); } -bool StateInfo::hasMoves() const +bool Position::hasMoves() const { checkInvariant(); @@ -55,7 +55,7 @@ bool StateInfo::hasMoves() const return true; } -double StateInfo::getResult(player_t currentSideToMove) const +double Position::getResult(player_t currentSideToMove) const { assert(!hasMoves()); checkInvariant(); @@ -73,17 +73,17 @@ double StateInfo::getResult(player_t currentSideToMove) const } } -void StateInfo::checkInvariant() const +void Position::checkInvariant() const { - assert(position->sideToMove == PLAYER_BLACK || position->sideToMove == PLAYER_WHITE); + assert(sideToMove == PLAYER_BLACK || sideToMove == PLAYER_WHITE); } //////////////////////////////////////////////////////////////////////////////////////// -Node::Node(StateInfo &state) : - sideToMove(state.position->sideToMove) +Node::Node(Position &position) : + sideToMove(position->sideToMove) { - state.generateMoves(moves); + position->generateMoves(moves); } Node::Node(StateInfo &state, const move_t &m, Node *p) : @@ -91,7 +91,7 @@ Node::Node(StateInfo &state, const move_t &m, Node *p) : parent(p), sideToMove(state.position->sideToMove) { - state.generateMoves(moves); + state.position->generateMoves(moves); } void deleteChild(Node *node) @@ -187,9 +187,9 @@ Node *Node::selectChild() const return nodeMax; } -Node *Node::addChild(const move_t &move, StateInfo &state) +Node *Node::addChild(const move_t &move, Position &position) { - auto node = new Node(state, move, this); // TODO: memmgr_alloc + auto node = new Node(position, move, this); // TODO: memmgr_alloc //children.push_back(node); children[childrenSize] = node; @@ -260,7 +260,7 @@ string Node::indentString(int indent) const ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// -Node *AIAlgorithm::computeTree(StateInfo state, +Node *AIAlgorithm::computeTree(Position position, const MCTSOptions options, mt19937_64::result_type initialSeed) { @@ -275,7 +275,7 @@ Node *AIAlgorithm::computeTree(StateInfo state, } // Will support more players later. - assert(state.position->sideToMove == PLAYER_BLACK || state.position->sideToMove == PLAYER_WHITE); + assert(position->sideToMove == PLAYER_BLACK || position->sideToMove == PLAYER_WHITE); Node *root = new Node(state); @@ -288,12 +288,12 @@ Node *AIAlgorithm::computeTree(StateInfo state, //auto node = root.get(); Node *node = root; - StateInfo st = state; + Position pos = position; // Select a path through the tree to a leaf node. while (!node->hasUntriedMoves() && node->hasChildren()) { node = node->selectChild(); - st.doMove(node->move); + pos.doMove(node->move); } // If we are not already at the final game, expand the @@ -301,18 +301,18 @@ Node *AIAlgorithm::computeTree(StateInfo state, if (node->hasUntriedMoves()) { auto move = node->getUntriedMove(&random_engine); st.doMove(move); - node = node->addChild(move, st); + node = node->addChild(move, pos); } // We now play randomly until the game ends. while (st.hasMoves()) { - st.doRandomMove(root, &random_engine); + pos.doRandomMove(root, &random_engine); } // We have now reached a final game. Backpropagate the result // up the tree to the root node. while (node != nullptr) { - node->update(st.getResult(node->sideToMove)); + node->update(pos.getResult(node->sideToMove)); node = node->parent; } @@ -334,17 +334,17 @@ Node *AIAlgorithm::computeTree(StateInfo state, return root; } -move_t AIAlgorithm::computeMove(StateInfo state, +move_t AIAlgorithm::computeMove(Position position, const MCTSOptions options) { // Will support more players later. - assert(state.position->sideToMove == PLAYER_BLACK || state.position->sideToMove == PLAYER_WHITE); + assert(position->sideToMove == PLAYER_BLACK || position->sideToMove == PLAYER_WHITE); // 分段随机打乱着法表 MoveList::shuffle(); - Stack moves; - state.generateMoves(moves); + Stack moves; + position->generateMoves(moves); assert(moves.size() > 0); if (moves.size() == 1) { return moves[0]; @@ -446,9 +446,9 @@ move_t AIAlgorithm::computeMove(StateInfo state, ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -ostream &operator << (ostream &out, StateInfo &state) +ostream &operator << (ostream &out, Position &pos) { - //state.print(out); + //state.position->print(out); return out; } diff --git a/src/ai/movegen.cpp b/src/ai/movegen.cpp index f1008abb..818b9776 100644 --- a/src/ai/movegen.cpp +++ b/src/ai/movegen.cpp @@ -27,7 +27,7 @@ #include "search.h" #include "position.h" -void StateInfo::generateChildren(const Stack &moves, +void Position::generateChildren(const Stack &moves, AIAlgorithm *ai, Node *node #ifdef TT_MOVE_ENABLE @@ -55,12 +55,12 @@ void StateInfo::generateChildren(const Stack &moves, } // 赋值 - node->sideToMove = position->sideToMove; + node->sideToMove = sideToMove; return; } -int StateInfo::generateMoves(Stack &moves) +int Position::generateMoves(Stack &moves) { square_t square; player_t opponent; @@ -68,24 +68,24 @@ int StateInfo::generateMoves(Stack &moves) moves.clear(); // 列出所有合法的下一招 - switch (position->action) { + switch (action) { // 对于选子和落子动作 case ACTION_CHOOSE: case ACTION_PLACE: // 对于摆子阶段 - if (position->phase & (PHASE_PLACING | PHASE_READY)) { + if (phase & (PHASE_PLACING | PHASE_READY)) { for (move_t i : MoveList::movePriorityTable) { square = static_cast(i); // 如果已经有子占据, 继续检索 - if (boardLocations[square]) { + if (board.locations[square]) { continue; } #ifdef MCTS_AI moves.push_back((move_t)square); #else // MCTS_AI - if (position->phase != PHASE_READY) { + if (phase != PHASE_READY) { moves.push_back((move_t)square); } else { // 若为先手,则抢占星位 @@ -103,7 +103,7 @@ int StateInfo::generateMoves(Stack &moves) } // 对于移子阶段 - if (position->phase & PHASE_MOVING) { + if (phase & PHASE_MOVING) { square_t newSquare, oldSquare; // 尽量走理论上较差的位置的棋子 @@ -114,13 +114,13 @@ int StateInfo::generateMoves(Stack &moves) continue; } - if (position->nPiecesOnBoard[position->sideId] > rule.nPiecesAtLeast || + if (nPiecesOnBoard[sideId] > rule.nPiecesAtLeast || !rule.allowFlyWhenRemainThreePieces) { // 对于棋盘上还有3个子以上,或不允许飞子的情况,要求必须在着法表中 for (int direction = DIRECTION_BEGIN; direction < DIRECTIONS_COUNT; direction++) { // 对于原有位置,遍历四个方向的着法,如果棋盘上为空位就加到结点列表中 newSquare = static_cast(MoveList::moveTable[oldSquare][direction]); - if (newSquare && !boardLocations[newSquare]) { + if (newSquare && !board.locations[newSquare]) { move_t m = move_t((oldSquare << 8) + newSquare); moves.push_back((move_t)m); } @@ -128,7 +128,7 @@ int StateInfo::generateMoves(Stack &moves) } else { // 对于棋盘上还有不到3个字,但允许飞子的情况,不要求在着法表中,是空位就行 for (newSquare = SQ_BEGIN; newSquare < SQ_END; newSquare = static_cast(newSquare + 1)) { - if (!boardLocations[newSquare]) { + if (!board.locations[newSquare]) { move_t m = move_t((oldSquare << 8) + newSquare); moves.push_back((move_t)m); } @@ -140,13 +140,13 @@ int StateInfo::generateMoves(Stack &moves) // 对于吃子动作 case ACTION_CAPTURE: - opponent = Player::getOpponent(position->sideToMove); + opponent = Player::getOpponent(sideToMove); - if (position->board.isAllInMills(opponent)) { + if (board.isAllInMills(opponent)) { // 全成三的情况 for (int i = Board::MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) { square = static_cast(MoveList::movePriorityTable[i]); - if (boardLocations[square] & opponent) { + if (board.locations[square] & opponent) { moves.push_back((move_t)-square); } } @@ -156,8 +156,8 @@ int StateInfo::generateMoves(Stack &moves) // 不是全成三的情况 for (int i = Board::MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) { square = static_cast(MoveList::movePriorityTable[i]); - if (boardLocations[square] & opponent) { - if (rule.allowRemoveMill || !position->board.inHowManyMills(square, PLAYER_NOBODY)) { + if (board.locations[square] & opponent) { + if (rule.allowRemoveMill || !board.inHowManyMills(square, PLAYER_NOBODY)) { moves.push_back((move_t)-square); } } @@ -172,7 +172,7 @@ int StateInfo::generateMoves(Stack &moves) return moves.size(); } -int StateInfo::generateNullMove(Stack &moves) +int Position::generateNullMove(Stack &moves) { moves.clear(); moves.push_back((move_t)SQ_0); @@ -436,3 +436,122 @@ void MoveList::shuffle() movePriorityTable[i + 16] = movePriorityTable3[i]; } } + + + +/// generate generates all the legal moves in the given position + +//template<> +ExtMove *generate(/* TODO: const */ Position &position, ExtMove *moveList) +{ + square_t square; + player_t opponent; + + //moves.clear(); + ExtMove *cur = moveList; + + // 列出所有合法的下一招 + switch (position.action) { + // 对于选子和落子动作 + case ACTION_CHOOSE: + case ACTION_PLACE: + // 对于摆子阶段 + if (position.phase & (PHASE_PLACING | PHASE_READY)) { + for (move_t i : MoveList::movePriorityTable) { + square = static_cast(i); + + // 如果已经有子占据, 继续检索 + if (position.board.locations[square]) { + continue; + } + +#ifdef MCTS_AI + moves.push_back((move_t)square); +#else // MCTS_AI + if (position.phase != PHASE_READY) { + *cur++ = ((move_t)square); + } else { + // 若为先手,则抢占星位 +#ifdef FIRST_MOVE_STAR_PREFERRED + if (Board::isStar(square)) { + moves.push_back((move_t)square); + } +#else + *cur++ = ((move_t)square); +#endif + } +#endif // MCTS_AI + } + break; + } + + // 对于移子阶段 + if (position.phase & PHASE_MOVING) { + square_t newSquare, oldSquare; + + // 尽量走理论上较差的位置的棋子 + for (int i = Board::MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) { + oldSquare = static_cast(MoveList::movePriorityTable[i]); + + if (!position.choose(oldSquare)) { + continue; + } + + if (position.nPiecesOnBoard[position.sideId] > rule.nPiecesAtLeast || + !rule.allowFlyWhenRemainThreePieces) { + // 对于棋盘上还有3个子以上,或不允许飞子的情况,要求必须在着法表中 + for (int direction = DIRECTION_BEGIN; direction < DIRECTIONS_COUNT; direction++) { + // 对于原有位置,遍历四个方向的着法,如果棋盘上为空位就加到结点列表中 + newSquare = static_cast(MoveList::moveTable[oldSquare][direction]); + if (newSquare && !position.board.locations[newSquare]) { + move_t m = move_t((oldSquare << 8) + newSquare); + *cur++ = ((move_t)m); + } + } + } else { + // 对于棋盘上还有不到3个字,但允许飞子的情况,不要求在着法表中,是空位就行 + for (newSquare = SQ_BEGIN; newSquare < SQ_END; newSquare = static_cast(newSquare + 1)) { + if (!position.board.locations[newSquare]) { + move_t m = move_t((oldSquare << 8) + newSquare); + *cur++ = ((move_t)m); + } + } + } + } + } + break; + + // 对于吃子动作 + case ACTION_CAPTURE: + opponent = Player::getOpponent(position.sideToMove); + + if (position.board.isAllInMills(opponent)) { + // 全成三的情况 + for (int i = Board::MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) { + square = static_cast(MoveList::movePriorityTable[i]); + if (position.board.locations[square] & opponent) { + *cur++ = ((move_t)-square); + } + } + break; + } + + // 不是全成三的情况 + for (int i = Board::MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) { + square = static_cast(MoveList::movePriorityTable[i]); + if (position.board.locations[square] & opponent) { + if (rule.allowRemoveMill || !position.board.inHowManyMills(square, PLAYER_NOBODY)) { + *cur++ = ((move_t)-square); + } + } + } + break; + + default: + assert(0); + break; + } + + return moveList; +} + diff --git a/src/ai/movegen.h b/src/ai/movegen.h index 5f1da72d..b1d872bb 100644 --- a/src/ai/movegen.h +++ b/src/ai/movegen.h @@ -24,13 +24,50 @@ #include "position.h" #include "search.h" +enum GenType +{ + CAPTURES, + LEGAL +}; + +struct ExtMove +{ + move_t move; + value_t value; + + operator move_t() const + { + return move; + } + + void operator = (move_t m) + { + move = m; + } + + // Inhibit unwanted implicit conversions to Move + // with an ambiguity that yields to a compile error. + operator float() const = delete; +}; + +inline bool operator < (const ExtMove &first, const ExtMove &second) +{ + return first.value < second.value; +} + +//template +ExtMove *generate(const Position &pos, ExtMove *moveList); + +/// The MoveList struct is a simple wrapper around generate(). It sometimes comes +/// in handy to use this class instead of the low level generate() function. +//template class MoveList { public: MoveList() = delete; MoveList &operator=(const MoveList &) = delete; - + // 生成着法表 static void create(); @@ -41,11 +78,39 @@ public: inline static move_t moveTable[SQ_EXPANDED_COUNT][DIRECTIONS_COUNT] = { {MOVE_NONE} }; // 着法顺序表, 后续会被打乱 - inline static array movePriorityTable { + inline static array movePriorityTable{ (move_t)8, (move_t)9, (move_t)10, (move_t)11, (move_t)12, (move_t)13, (move_t)14, (move_t)15, (move_t)16, (move_t)17, (move_t)18, (move_t)19, (move_t)20, (move_t)21, (move_t)22, (move_t)23, (move_t)24, (move_t)25, (move_t)26, (move_t)27, (move_t)28, (move_t)29, (move_t)30, (move_t)31, }; + + //explicit MoveList(const Position &pos) : last(generate(pos, moveList)) + explicit MoveList(const Position &pos) : last(generate(pos, moveList)) + { + } + + const ExtMove *begin() const + { + return moveList; + } + + const ExtMove *end() const + { + return last; + } + + size_t size() const + { + return last - moveList; + } + + bool contains(move_t move) const + { + return std::find(begin(), end(), move) != end(); + } + +private: + ExtMove moveList[MAX_MOVES], *last; }; #endif /* MOVEGEN_H */ diff --git a/src/ai/search.cpp b/src/ai/search.cpp index 90073a1b..99d0f928 100644 --- a/src/ai/search.cpp +++ b/src/ai/search.cpp @@ -123,15 +123,15 @@ depth_t AIAlgorithm::changeDepth(depth_t origDepth) if (st->position->phase & PHASE_PLACING) { if (rule.nTotalPiecesEachSide == 12) { - d = placingDepthTable_12[rule.nTotalPiecesEachSide * 2 - st->getPiecesInHandCount(BLACK) - st->getPiecesInHandCount(WHITE)]; + d = placingDepthTable_12[rule.nTotalPiecesEachSide * 2 - st->position->getPiecesInHandCount(BLACK) - st->position->getPiecesInHandCount(WHITE)]; } else { - d = placingDepthTable_9[rule.nTotalPiecesEachSide * 2 - st->getPiecesInHandCount(BLACK) - st->getPiecesInHandCount(WHITE)]; + d = placingDepthTable_9[rule.nTotalPiecesEachSide * 2 - st->position->getPiecesInHandCount(BLACK) - st->position->getPiecesInHandCount(WHITE)]; } } if (st->position->phase & PHASE_MOVING) { - int pb = st->getPiecesOnBoardCount(BLACK); - int pw = st->getPiecesOnBoardCount(WHITE); + int pb = st->position->getPiecesOnBoardCount(BLACK); + int pw = st->position->getPiecesOnBoardCount(WHITE); int pieces = pb + pw; int diff = pb - pw; @@ -185,7 +185,7 @@ void AIAlgorithm::buildRoot() Node *Node::addChild( const move_t &m, AIAlgorithm *ai, - StateInfo *st + Position *position #ifdef TT_MOVE_ENABLE , const move_t &ttMove #endif // TT_MOVE_ENABLE @@ -289,7 +289,7 @@ Node *Node::addChild( } // 若为走子之前的统计故走棋阶段可能会从 @-0-@ 走成 0-@-@, 并未成三,所以需要传值 sqsrc 进行判断 - int nMills = st->position->board.inHowManyMills(sq, st->position->sideToMove, sqsrc); + int nMills = position->board.inHowManyMills(sq, position->sideToMove, sqsrc); int nopponentMills = 0; #ifdef SORT_MOVE_WITH_HUMAN_KNOWLEDGES @@ -300,17 +300,17 @@ Node *Node::addChild( #ifdef ALPHABETA_AI newNode->rating += static_cast(RATING_ONE_MILL * nMills); #endif - } else if (st->getPhase() == PHASE_PLACING) { + } else if (position->getPhase() == PHASE_PLACING) { // 在摆棋阶段, 检测落子点是否能阻止对方成三 - nopponentMills = st->position->board.inHowManyMills(sq, st->position->opponent); + nopponentMills = position->board.inHowManyMills(sq, position->opponent); #ifdef ALPHABETA_AI newNode->rating += static_cast(RATING_BLOCK_ONE_MILL * nopponentMills); #endif } #if 1 - else if (st->getPhase() == PHASE_MOVING) { + else if (position->getPhase() == PHASE_MOVING) { // 在走棋阶段, 检测落子点是否能阻止对方成三 - nopponentMills = st->position->board.inHowManyMills(sq, st->position->opponent); + nopponentMills = position->board.inHowManyMills(sq, position->opponent); if (nopponentMills) { int nPlayerPiece = 0; @@ -318,7 +318,7 @@ Node *Node::addChild( int nForbidden = 0; int nEmpty = 0; - st->position->board.getSurroundedPieceCount(sq, st->position->sideId, + position->board.getSurroundedPieceCount(sq, position->sideId, nPlayerPiece, nOpponentPiece, nForbidden, nEmpty); #ifdef ALPHABETA_AI @@ -337,7 +337,7 @@ Node *Node::addChild( // 对于12子棋, 白方第2着走星点的重要性和成三一样重要 (TODO) #ifdef ALPHABETA_AI if (rule.nTotalPiecesEachSide == 12 && - st->getPiecesOnBoardCount(2) < 2 && // patch: 仅当白方第2着时 + position->getPiecesOnBoardCount(2) < 2 && // patch: 仅当白方第2着时 Board::isStar(static_cast(m))) { newNode->rating += RATING_STAR_SQUARE; } @@ -348,7 +348,7 @@ Node *Node::addChild( int nForbidden = 0; int nEmpty = 0; - st->position->board.getSurroundedPieceCount(sq, st->position->sideId, + position->board.getSurroundedPieceCount(sq, position->sideId, nPlayerPiece, nOpponentPiece, nForbidden, nEmpty); #ifdef ALPHABETA_AI @@ -367,7 +367,7 @@ Node *Node::addChild( } // 吃子点处于对方的三连中 - nopponentMills = st->position->board.inHowManyMills(sq, st->position->opponent); + nopponentMills = position->board.inHowManyMills(sq, position->opponent); if (nopponentMills) { if (nOpponentPiece >= 2) { // 旁边对方的子较多, 则倾向不吃 @@ -604,8 +604,8 @@ int AIAlgorithm::search(depth_t depth) #ifdef THREEFOLD_REPETITION static int nRepetition = 0; - if (state->getPhase() == PHASE_MOVING) { - hash_t hash = state->getHash(); + if (state->position->getPhase() == PHASE_MOVING) { + hash_t hash = state->position->getHash(); if (std::find(moveHistory.begin(), moveHistory.end(), hash) != moveHistory.end()) { nRepetition++; @@ -618,7 +618,7 @@ int AIAlgorithm::search(depth_t depth) } } - if (state->getPhase() == PHASE_PLACING) { + if (state->position->getPhase() == PHASE_PLACING) { moveHistory.clear(); } #endif // THREEFOLD_REPETITION @@ -715,7 +715,7 @@ int AIAlgorithm::search(depth_t depth) if (gameOptions.getIDSEnabled()) { #ifdef IDS_WINDOW - value_t window = state->getPhase() == PHASE_PLACING ? VALUE_PLACING_WINDOW : VALUE_MOVING_WINDOW; + value_t window = state->position.getPhase() == PHASE_PLACING ? VALUE_PLACING_WINDOW : VALUE_MOVING_WINDOW; alpha = value - window; beta = value + window; #else @@ -786,7 +786,7 @@ value_t AIAlgorithm::search(depth_t depth, value_t alpha, value_t beta, Node *no #if defined (TRANSPOSITION_TABLE_ENABLE) || defined(ENDGAME_LEARNING) // 获取哈希值 - hash_t hash = st->getHash(); + hash_t hash = st->position->getHash(); #endif #ifdef ENDGAME_LEARNING @@ -889,7 +889,7 @@ value_t AIAlgorithm::search(depth_t depth, value_t alpha, value_t beta, Node *no depth <= 0 || unlikely(requiredQuit)) { // 局面评估 - node->value = Evaluation::getValue(st, position, node); + node->value = Evaluation::getValue(position, node); evaluatedNodeCount++; // 为争取速胜,value 值 +- 深度 @@ -947,9 +947,9 @@ value_t AIAlgorithm::search(depth_t depth, value_t alpha, value_t beta, Node *no // 生成子节点树,即生成每个合理的着法 if (node->childrenSize == 0) { - int moveCount = st->generateMoves(moves); + int moveCount = st->position->generateMoves(moves); - st->generateChildren(moves, this, node + st->position->generateChildren(moves, this, node #ifdef TT_MOVE_ENABLE , ttMove #endif // TT_MOVE_ENABLE @@ -971,7 +971,7 @@ value_t AIAlgorithm::search(depth_t depth, value_t alpha, value_t beta, Node *no #ifdef TRANSPOSITION_TABLE_ENABLE #ifdef PREFETCH_SUPPORT for (int i = 0; i < nchild; i++) { - TT::prefetchHash(st->getNextMainHash(node->children[i]->move)); + TT::prefetchHash(st->position->getNextMainHash(node->children[i]->move)); } #ifdef PREFETCH_DEBUG @@ -1104,7 +1104,7 @@ void AIAlgorithm::stashPosition() void AIAlgorithm::doMove(move_t move) { // 执行着法 - st->doMove(move); + st->position->doMove(move); } void AIAlgorithm::undoMove() @@ -1118,13 +1118,13 @@ void AIAlgorithm::undoMove() void AIAlgorithm::doNullMove() { // 执行空着 - st->doNullMove(); + st->position->doNullMove(); } void AIAlgorithm::undoNullMove() { // 执行空着 - st->undoNullMove(); + st->position->undoNullMove(); } #ifdef ALPHABETA_AI diff --git a/src/ai/search.h b/src/ai/search.h index 89b4e558..5d162063 100644 --- a/src/ai/search.h +++ b/src/ai/search.h @@ -57,10 +57,10 @@ class Position; using namespace std; using namespace CTSL; -// 注意:StateInfo类不是线程安全的! -// 所以不能在ai类中修改StateInfo类的静态成员变量,切记! -// 另外,AI类是StateInfo类的友元类,可以访问其私有变量 -// 尽量不要使用StateInfo的操作函数,因为有参数安全性检测和不必要的赋值,影响效率 +// 注意:Position 类不是线程安全的! +// 所以不能在ai类中修改 Position 类的静态成员变量,切记! +// 另外,AI类是 Position 类的友元类,可以访问其私有变量 +// 尽量不要使用 Position 的操作函数,因为有参数安全性检测和不必要的赋值,影响效率 class Node { @@ -69,8 +69,8 @@ public: ~Node(); #ifdef MCTS_AI - Node(StateInfo &state); - Node(StateInfo &state, const move_t &move, Node *parent); + Node(Position &position); + Node(Position &position, const move_t &move, Node *parent); #endif // MCTS_AI bool hasChildren() const; @@ -78,7 +78,7 @@ public: Node *addChild( const move_t &move, AIAlgorithm *ai, - StateInfo *st + Position *position #ifdef TT_MOVE_ENABLE , const move_t &ttMove #endif // TT_MOVE_ENABLE @@ -90,14 +90,14 @@ public: move_t getUntriedMove(RandomEngine *engine) const; Node *bestChildren() const; Node *selectChild() const; - Node *addChild(const move_t &move, StateInfo &state); + Node *addChild(const move_t &move, Position &position); void update(double result); string toString(); string treeToString(int max_depth = 1000000, int indent = 0) const; string indentString(int indent) const; #endif // MCTS_AI - static const int NODE_CHILDREN_SIZE = MOVE_COUNT; + static const int NODE_CHILDREN_SIZE = MAX_MOVES; Node *children[NODE_CHILDREN_SIZE]; Node *parent { nullptr }; @@ -212,10 +212,10 @@ public: #ifdef MCTS_AI // TODO: 分离到 MCTS 算法类 - Node *computeTree(StateInfo state, + Node *computeTree(Position position, const MCTSOptions options, mt19937_64::result_type initialSeed); - move_t AIAlgorithm::computeMove(StateInfo state, + move_t AIAlgorithm::computeMove(Position position, const MCTSOptions options); #endif @@ -308,7 +308,7 @@ private: Stack positionStack; #endif /* USE_STD_STACK */ - Stack moves; + Stack moves; // 标识,用于跳出剪枝算法,立即返回 bool requiredQuit {false}; diff --git a/src/base/aithread.cpp b/src/base/aithread.cpp index 17bd7009..4afbe1d1 100644 --- a/src/base/aithread.cpp +++ b/src/base/aithread.cpp @@ -98,7 +98,7 @@ void AiThread::emitCommand() } #ifdef MCTS_AI -move_t computeMove(StateInfo state, +move_t computeMove(Position position, const MCTSOptions options); #endif // MCTS_AI diff --git a/src/game/position.cpp b/src/game/position.cpp index 8c716d25..75509f7e 100644 --- a/src/game/position.cpp +++ b/src/game/position.cpp @@ -34,10 +34,10 @@ StateInfo::StateInfo() position = new Position(); //memset(position, 0, sizeof(Position)); +} - // 单独提出 boardLocations 等数据,免得每次都写 boardLocations; - boardLocations = position->board.locations; - +Position::Position() +{ // 创建哈希数据 constructHash(); @@ -57,7 +57,10 @@ StateInfo::~StateInfo() delete position; position = nullptr; } +} +Position::~Position() +{ cmdlist.clear(); } @@ -73,6 +76,11 @@ StateInfo::StateInfo(const StateInfo &state) *this = state; } +Position::Position(const Position &pos) +{ + *this = pos; +} + StateInfo::StateInfo(StateInfo &state) { if (position != nullptr) { @@ -85,22 +93,32 @@ StateInfo::StateInfo(StateInfo &state) *this = state; } +Position::Position(Position &pos) +{ + *this = pos; +} + StateInfo &StateInfo::operator= (const StateInfo &state) { memcpy(position, state.position, sizeof(Position)); - currentStep = state.currentStep; - moveStep = state.moveStep; - boardLocations = position->board.locations; - currentSquare = state.currentSquare; - winner = state.winner; - startTime = state.startTime; - currentTime = state.currentTime; - elapsedSeconds[BLACK] = state.elapsedSeconds[BLACK]; - elapsedSeconds[WHITE] = state.elapsedSeconds[WHITE]; - move = state.move; - memcpy(cmdline, state.cmdline, sizeof(cmdline)); - cmdlist = state.cmdlist; - tips = state.tips; + return *this; +} + +Position &Position::operator= (const Position &pos) +{ + currentStep = pos.currentStep; + moveStep = pos.moveStep; + memcpy(board.locations, pos.board.locations, sizeof(board.locations)); + currentSquare = pos.currentSquare; + winner = pos.winner; + startTime = pos.startTime; + currentTime = pos.currentTime; + elapsedSeconds[BLACK] = pos.elapsedSeconds[BLACK]; + elapsedSeconds[WHITE] = pos.elapsedSeconds[WHITE]; + move = pos.move; + memcpy(cmdline, pos.cmdline, sizeof(cmdline)); + cmdlist = pos.cmdlist; + tips = pos.tips; return *this; } @@ -108,37 +126,43 @@ StateInfo &StateInfo::operator= (const StateInfo &state) StateInfo &StateInfo::operator= (StateInfo &state) { memcpy(position, state.position, sizeof(Position)); - currentStep = state.currentStep; - moveStep = state.moveStep; - boardLocations = position->board.locations; - currentSquare = state.currentSquare; - winner = state.winner; - startTime = state.startTime; - currentTime = state.currentTime; - elapsedSeconds[BLACK] = state.elapsedSeconds[BLACK]; - elapsedSeconds[WHITE] = state.elapsedSeconds[WHITE]; - move = state.move; - memcpy(cmdline, state.cmdline, sizeof(cmdline)); - cmdlist = state.cmdlist; - tips = state.tips; + return *this; +} + +Position &Position::operator= (Position &pos) +{ + currentStep = pos.currentStep; + moveStep = pos.moveStep; + memcpy(board.locations, pos.board.locations, sizeof(board.locations)); + currentSquare = pos.currentSquare; + winner = pos.winner; + startTime = pos.startTime; + currentTime = pos.currentTime; + elapsedSeconds[BLACK] = pos.elapsedSeconds[BLACK]; + elapsedSeconds[WHITE] = pos.elapsedSeconds[WHITE]; + move = pos.move; + memcpy(cmdline, pos.cmdline, sizeof(cmdline)); + cmdlist = pos.cmdlist; + tips = pos.tips; return *this; } -int StateInfo::countPiecesOnBoard() + +int Position::countPiecesOnBoard() { - position->nPiecesOnBoard[BLACK] = position->nPiecesOnBoard[WHITE] = 0; + nPiecesOnBoard[BLACK] = nPiecesOnBoard[WHITE] = 0; for (int r = 1; r < Board::N_RINGS + 2; r++) { for (int s = 0; s < Board::N_SEATS; s++) { square_t square = static_cast(r * Board::N_SEATS + s); - if (boardLocations[square] & PIECE_BLACK) { - position->nPiecesOnBoard[BLACK]++; - } else if (boardLocations[square] & PIECE_WHITE) { - position->nPiecesOnBoard[WHITE]++; + if (board.locations[square] & PIECE_BLACK) { + nPiecesOnBoard[BLACK]++; + } else if (board.locations[square] & PIECE_WHITE) { + nPiecesOnBoard[WHITE]++; } #if 0 - else if (boardLocations[square] & PIECE_FORBIDDEN) { + else if (board.locations[square] & PIECE_FORBIDDEN) { // 不计算盘面子数 } #endif @@ -146,24 +170,24 @@ int StateInfo::countPiecesOnBoard() } // 设置玩家盘面剩余子数和未放置子数 - if (position->nPiecesOnBoard[BLACK] > rule.nTotalPiecesEachSide || - position->nPiecesOnBoard[WHITE] > rule.nTotalPiecesEachSide) { + if (nPiecesOnBoard[BLACK] > rule.nTotalPiecesEachSide || + nPiecesOnBoard[WHITE] > rule.nTotalPiecesEachSide) { return -1; } - return position->nPiecesOnBoard[BLACK] + position->nPiecesOnBoard[WHITE]; + return nPiecesOnBoard[BLACK] + nPiecesOnBoard[WHITE]; } -int StateInfo::countPiecesInHand() +int Position::countPiecesInHand() { - position->nPiecesInHand[BLACK] = rule.nTotalPiecesEachSide - position->nPiecesOnBoard[BLACK]; - position->nPiecesInHand[WHITE] = rule.nTotalPiecesEachSide - position->nPiecesOnBoard[WHITE]; + nPiecesInHand[BLACK] = rule.nTotalPiecesEachSide - nPiecesOnBoard[BLACK]; + nPiecesInHand[WHITE] = rule.nTotalPiecesEachSide - nPiecesOnBoard[WHITE]; - return position->nPiecesInHand[BLACK] + position->nPiecesInHand[WHITE]; + return nPiecesInHand[BLACK] + nPiecesInHand[WHITE]; } // 设置棋局状态和棋盘数据,用于初始化 -bool StateInfo::setPosition(const struct Rule *newRule, +bool Position::setPosition(const struct Rule *newRule, step_t initialStep, phase_t phase, player_t side, action_t action, const char *locations, @@ -179,20 +203,20 @@ bool StateInfo::setPosition(const struct Rule *newRule, this->moveStep = initialStep; // 局面阶段标识 - position->phase = phase; + phase = phase; // 轮流状态标识 setSideToMove(side); // 动作状态标识 - position->action = action; + action = action; // 当前棋局(3×8) if (locations == nullptr) { - memset(boardLocations, 0, sizeof(position->board.locations)); - position->hash = 0; + memset(board.locations, 0, sizeof(board.locations)); + hash = 0; } else { - memcpy(boardLocations, locations, sizeof(position->board.locations)); + memcpy(board.locations, locations, sizeof(board.locations)); } if (countPiecesOnBoard() == -1) { @@ -204,14 +228,14 @@ bool StateInfo::setPosition(const struct Rule *newRule, // 设置去子状态时的剩余尚待去除子数 if (action == ACTION_CAPTURE) { if (0 <= nPiecesNeedRemove && nPiecesNeedRemove < 3) { - position->nPiecesNeedRemove = nPiecesNeedRemove; + nPiecesNeedRemove = nPiecesNeedRemove; } } else { - position->nPiecesNeedRemove = 0; + nPiecesNeedRemove = 0; } // 清空成三记录 - position->board.millListSize = 0; + board.millListSize = 0; // 胜负标识 winner = PLAYER_NOBODY; @@ -220,7 +244,7 @@ bool StateInfo::setPosition(const struct Rule *newRule, MoveList::create(); // 生成成三表 - position->board.createMillTable(); + board.createMillTable(); // 不选中棋子 currentSquare = SQ_0; @@ -249,9 +273,9 @@ bool StateInfo::setPosition(const struct Rule *newRule, return false; } -bool StateInfo::reset() +bool Position::reset() { - if (position->phase == PHASE_READY && + if (phase == PHASE_READY && elapsedSeconds[BLACK] == elapsedSeconds[WHITE] == 0) { return true; } @@ -261,31 +285,31 @@ bool StateInfo::reset() moveStep = 0; // 局面阶段标识 - position->phase = PHASE_READY; + phase = PHASE_READY; // 设置轮流状态 setSideToMove(PLAYER_BLACK); // 动作状态标识 - position->action = ACTION_PLACE; + action = ACTION_PLACE; // 胜负标识 winner = PLAYER_NOBODY; // 当前棋局(3×8) - memset(boardLocations, 0, sizeof(position->board)); + memset(board.locations, 0, sizeof(board.locations)); // 盘面子数归零 - position->nPiecesOnBoard[BLACK] = position->nPiecesOnBoard[WHITE] = 0; + nPiecesOnBoard[BLACK] = nPiecesOnBoard[WHITE] = 0; // 设置玩家盘面剩余子数和未放置子数 - position->nPiecesInHand[BLACK] = position->nPiecesInHand[WHITE] = rule.nTotalPiecesEachSide; + nPiecesInHand[BLACK] = nPiecesInHand[WHITE] = rule.nTotalPiecesEachSide; // 设置去子状态时的剩余尚待去除子数 - position->nPiecesNeedRemove = 0; + nPiecesNeedRemove = 0; // 清空成三记录 - position->board.millListSize = 0; + board.millListSize = 0; // 不选中棋子 currentSquare = SQ_0; @@ -294,7 +318,7 @@ bool StateInfo::reset() elapsedSeconds[BLACK] = elapsedSeconds[WHITE] = 0; // 哈希归零 - position->hash = 0; + hash = 0; // 提示 setTips(); @@ -326,9 +350,9 @@ bool StateInfo::reset() return false; } -bool StateInfo::start() +bool Position::start() { - switch (position->phase) { + switch (phase) { // 如果游戏已经开始,则返回false case PHASE_PLACING: case PHASE_MOVING: @@ -342,29 +366,29 @@ bool StateInfo::start() // 启动计时器 startTime = time(NULL); // 进入开局状态 - position->phase = PHASE_PLACING; + phase = PHASE_PLACING; return true; default: return false; } } -bool StateInfo::place(square_t square, int8_t updateCmdlist) +bool Position::place(square_t square, int8_t updateCmdlist) { // 如果局面为“结局”,返回false - if (position->phase == PHASE_GAMEOVER) + if (phase == PHASE_GAMEOVER) return false; // 如果局面为“未开局”,则开局 - if (position->phase == PHASE_READY) + if (phase == PHASE_READY) start(); // 如非“落子”状态,返回false - if (position->action != ACTION_PLACE) + if (action != ACTION_PLACE) return false; // 如果落子位置在棋盘外、已有子点或禁点,返回false - if (!position->board.onBoard[square] || boardLocations[square]) + if (!board.onBoard[square] || board.locations[square]) return false; // 格式转换 @@ -379,13 +403,13 @@ bool StateInfo::place(square_t square, int8_t updateCmdlist) int piece = '\x00'; int n = 0; - if (position->phase == PHASE_PLACING) { - int playerId = Player::toId(position->sideToMove); - piece = (0x01 | position->sideToMove) + rule.nTotalPiecesEachSide - position->nPiecesInHand[playerId]; - position->nPiecesInHand[playerId]--; - position->nPiecesOnBoard[playerId]++; + if (phase == PHASE_PLACING) { + int playerId = Player::toId(sideToMove); + piece = (0x01 | sideToMove) + rule.nTotalPiecesEachSide - nPiecesInHand[playerId]; + nPiecesInHand[playerId]--; + nPiecesOnBoard[playerId]++; - boardLocations[square] = piece; + board.locations[square] = piece; updateHash(square); @@ -401,24 +425,24 @@ bool StateInfo::place(square_t square, int8_t updateCmdlist) currentSquare = square; - n = position->board.addMills(currentSquare); + n = board.addMills(currentSquare); // 开局阶段未成三 if (n == 0) { - assert(position->nPiecesInHand[BLACK] >= 0 && position->nPiecesInHand[WHITE] >= 0); + assert(nPiecesInHand[BLACK] >= 0 && nPiecesInHand[WHITE] >= 0); // 如果双方都无未放置的棋子 - if (position->nPiecesInHand[BLACK] == 0 && position->nPiecesInHand[WHITE] == 0) { + if (nPiecesInHand[BLACK] == 0 && nPiecesInHand[WHITE] == 0) { // 决胜负 if (checkGameOverCondition()) { goto out; } // 进入中局阶段 - position->phase = PHASE_MOVING; + phase = PHASE_MOVING; // 进入选子状态 - position->action = ACTION_CHOOSE; + action = ACTION_CHOOSE; // 清除禁点 cleanForbiddenLocations(); @@ -444,10 +468,10 @@ bool StateInfo::place(square_t square, int8_t updateCmdlist) // 如果成三 else { // 设置去子数目 - position->nPiecesNeedRemove = rule.allowRemoveMultiPieces ? n : 1; + nPiecesNeedRemove = rule.allowRemoveMultiPieces ? n : 1; // 进入去子状态 - position->action = ACTION_CAPTURE; + action = ACTION_CAPTURE; } goto out; @@ -461,7 +485,7 @@ bool StateInfo::place(square_t square, int8_t updateCmdlist) // 对于中局落子 (ontext.phase == GAME_MOVING) // 如果落子不合法 - if (position->nPiecesOnBoard[position->sideId] > rule.nPiecesAtLeast || + if (nPiecesOnBoard[sideId] > rule.nPiecesAtLeast || !rule.allowFlyWhenRemainThreePieces) { int i; for (i = 0; i < 4; i++) { @@ -487,20 +511,20 @@ bool StateInfo::place(square_t square, int8_t updateCmdlist) moveStep++; } - boardLocations[square] = boardLocations[currentSquare]; + board.locations[square] = board.locations[currentSquare]; updateHash(square); revertHash(currentSquare); - boardLocations[currentSquare] = '\x00'; + board.locations[currentSquare] = '\x00'; currentSquare = square; - n = position->board.addMills(currentSquare); + n = board.addMills(currentSquare); // 中局阶段未成三 if (n == 0) { // 进入选子状态 - position->action = ACTION_CHOOSE; + action = ACTION_CHOOSE; // 设置轮到谁走 changeSideToMove(); @@ -513,10 +537,10 @@ bool StateInfo::place(square_t square, int8_t updateCmdlist) // 中局阶段成三 else { // 设置去子数目 - position->nPiecesNeedRemove = rule.allowRemoveMultiPieces ? n : 1; + nPiecesNeedRemove = rule.allowRemoveMultiPieces ? n : 1; // 进入去子状态 - position->action = ACTION_CAPTURE; + action = ACTION_CAPTURE; } out: @@ -527,7 +551,7 @@ out: return true; } -bool StateInfo::_place(int r, int s) +bool Position::_place(int r, int s) { // 转换为 square square_t square = Board::polarToSquare(r, s); @@ -535,7 +559,7 @@ bool StateInfo::_place(int r, int s) return place(square, true); } -bool StateInfo::_capture(int r, int s) +bool Position::_capture(int r, int s) { // 转换为 square square_t square = Board::polarToSquare(r, s); @@ -543,18 +567,18 @@ bool StateInfo::_capture(int r, int s) return capture(square, 1); } -bool StateInfo::capture(square_t square, int8_t updateCmdlist) +bool Position::capture(square_t square, int8_t updateCmdlist) { // 如果局面为"未开局"或“结局”,返回false - if (position->phase & PHASE_NOTPLAYING) + if (phase & PHASE_NOTPLAYING) return false; // 如非“去子”状态,返回false - if (position->action != ACTION_CAPTURE) + if (action != ACTION_CAPTURE) return false; // 如果去子完成,返回false - if (position->nPiecesNeedRemove <= 0) + if (nPiecesNeedRemove <= 0) return false; // 格式转换 @@ -565,30 +589,30 @@ bool StateInfo::capture(square_t square, int8_t updateCmdlist) // 时间的临时变量 int seconds = -1; - player_t opponent = Player::getOpponent(position->sideToMove); + player_t opponent = Player::getOpponent(sideToMove); // 判断去子是不是对手棋 - if (!(opponent & boardLocations[square])) + if (!(opponent & board.locations[square])) return false; // 如果当前子是否处于“三连”之中,且对方还未全部处于“三连”之中 if (!rule.allowRemoveMill && - position->board.inHowManyMills(square, PLAYER_NOBODY) && - !position->board.isAllInMills(opponent)) { + board.inHowManyMills(square, PLAYER_NOBODY) && + !board.isAllInMills(opponent)) { return false; } // 去子(设置禁点) - if (rule.hasForbiddenLocations && position->phase == PHASE_PLACING) { + if (rule.hasForbiddenLocations && phase == PHASE_PLACING) { revertHash(square); - boardLocations[square] = '\x0f'; + board.locations[square] = '\x0f'; updateHash(square); } else { // 去子 revertHash(square); - boardLocations[square] = '\x00'; + board.locations[square] = '\x00'; } - position->nPiecesOnBoard[position->opponentId]--; + nPiecesOnBoard[opponentId]--; move = static_cast(-square); @@ -601,7 +625,7 @@ bool StateInfo::capture(square_t square, int8_t updateCmdlist) } currentSquare = SQ_0; - position->nPiecesNeedRemove--; + nPiecesNeedRemove--; //updateHash(square); // TODO: 多余? 若去掉.则评估点的数量和哈希命中数量上升, 未剪枝? // 去子完成 @@ -612,7 +636,7 @@ bool StateInfo::capture(square_t square, int8_t updateCmdlist) } // 还有其余的子要去吗 - if (position->nPiecesNeedRemove > 0) { + if (nPiecesNeedRemove > 0) { // 继续去子 return true; } @@ -620,15 +644,15 @@ bool StateInfo::capture(square_t square, int8_t updateCmdlist) // 所有去子都完成了 // 开局阶段 - if (position->phase == PHASE_PLACING) { + if (phase == PHASE_PLACING) { // 如果双方都无未放置的棋子 - if (position->nPiecesInHand[BLACK] == 0 && position->nPiecesInHand[WHITE] == 0) { + if (nPiecesInHand[BLACK] == 0 && nPiecesInHand[WHITE] == 0) { // 进入中局阶段 - position->phase = PHASE_MOVING; + phase = PHASE_MOVING; // 进入选子状态 - position->action = ACTION_CHOOSE; + action = ACTION_CHOOSE; // 清除禁点 cleanForbiddenLocations(); @@ -648,7 +672,7 @@ bool StateInfo::capture(square_t square, int8_t updateCmdlist) // 如果双方还有子 else { // 进入落子状态 - position->action = ACTION_PLACE; + action = ACTION_PLACE; // 设置轮到谁走 changeSideToMove(); @@ -662,7 +686,7 @@ bool StateInfo::capture(square_t square, int8_t updateCmdlist) // 中局阶段 else { // 进入选子状态 - position->action = ACTION_CHOOSE; + action = ACTION_CHOOSE; // 设置轮到谁走 changeSideToMove(); @@ -681,23 +705,23 @@ out: return true; } -bool StateInfo::choose(square_t square) +bool Position::choose(square_t square) { // 如果局面不是"中局”,返回false - if (position->phase != PHASE_MOVING) + if (phase != PHASE_MOVING) return false; // 如非“选子”或“落子”状态,返回false - if (position->action != ACTION_CHOOSE && position->action != ACTION_PLACE) + if (action != ACTION_CHOOSE && action != ACTION_PLACE) return false; // 判断选子是否可选 - if (boardLocations[square] & position->sideToMove) { + if (board.locations[square] & sideToMove) { // 选子 currentSquare = square; // 选子完成,进入落子状态 - position->action = ACTION_PLACE; + action = ACTION_PLACE; return true; } @@ -705,19 +729,19 @@ bool StateInfo::choose(square_t square) return false; } -bool StateInfo::choose(int r, int s) +bool Position::choose(int r, int s) { return choose(Board::polarToSquare(r, s)); } -bool StateInfo::giveup(player_t loser) +bool Position::giveup(player_t loser) { - if (position->phase & PHASE_NOTPLAYING || - position->phase == PHASE_NONE) { + if (phase & PHASE_NOTPLAYING || + phase == PHASE_NONE) { return false; } - position->phase = PHASE_GAMEOVER; + phase = PHASE_GAMEOVER; int loserId = Player::toId(loser); char loserCh = Player::idToCh(loserId); @@ -734,7 +758,7 @@ bool StateInfo::giveup(player_t loser) } // 打算用个C++的命令行解析库的,简单的没必要,但中文编码有极小概率出问题 -bool StateInfo::command(const char *cmd) +bool Position::command(const char *cmd) { int r; unsigned t; @@ -801,7 +825,7 @@ bool StateInfo::command(const char *cmd) } if (!strcmp(cmd, "draw")) { - position->phase = PHASE_GAMEOVER; + phase = PHASE_GAMEOVER; winner = PLAYER_DRAW; score_draw++; tips = "三次重复局面判和。"; @@ -814,7 +838,7 @@ bool StateInfo::command(const char *cmd) return false; } -bool StateInfo::doMove(move_t m) +bool Position::doMove(move_t m) { if (m < 0) { return capture(static_cast(-m)); @@ -831,21 +855,21 @@ bool StateInfo::doMove(move_t m) return false; } -player_t StateInfo::getWinner() const +player_t Position::getWinner() const { return winner; } -int StateInfo::update() +int Position::update() { int ret = -1; int timePoint = -1; - time_t *seconds = &elapsedSeconds[position->sideId]; - time_t opponentSeconds = elapsedSeconds[position->opponentId]; + time_t *seconds = &elapsedSeconds[sideId]; + time_t opponentSeconds = elapsedSeconds[opponentId]; // 根据局面调整计时器 - if (!(position->phase & PHASE_PLAYING)) { + if (!(phase & PHASE_PLAYING)) { return -1; } @@ -868,15 +892,15 @@ int StateInfo::update() } // 是否分出胜负 -bool StateInfo::checkGameOverCondition() +bool Position::checkGameOverCondition() { - if (position->phase & PHASE_NOTPLAYING) { + if (phase & PHASE_NOTPLAYING) { return true; } // 如果有时间限定 if (rule.maxTimeLedToLose > 0) { - position->phase = PHASE_GAMEOVER; + phase = PHASE_GAMEOVER; // 这里不能update更新时间,否则会形成循环嵌套 for (int i = 1; i <= 2; i++) @@ -898,7 +922,7 @@ bool StateInfo::checkGameOverCondition() if (rule.maxStepsLedToDraw > 0 && moveStep > rule.maxStepsLedToDraw) { winner = PLAYER_DRAW; - position->phase = PHASE_GAMEOVER; + phase = PHASE_GAMEOVER; sprintf(cmdline, "Steps over. In draw!"); cmdlist.emplace_back(string(cmdline)); return true; @@ -907,10 +931,10 @@ bool StateInfo::checkGameOverCondition() // 如果玩家子数小于赛点,则对方获胜 for (int i = 1; i <= 2; i++) { - if (position->nPiecesOnBoard[i] + position->nPiecesInHand[i] < rule.nPiecesAtLeast) { + if (nPiecesOnBoard[i] + nPiecesInHand[i] < rule.nPiecesAtLeast) { int o = Player::getOpponentById(i); winner = Player::idToPlayer(o); - position->phase = PHASE_GAMEOVER; + phase = PHASE_GAMEOVER; sprintf(cmdline, "Player%d win!", o); cmdlist.emplace_back(string(cmdline)); @@ -920,10 +944,10 @@ bool StateInfo::checkGameOverCondition() #ifdef MCTS_AI #if 0 - int diff = position->nPiecesOnBoard[BLACK] - position->nPiecesOnBoard[WHITE]; + int diff = nPiecesOnBoard[BLACK] - nPiecesOnBoard[WHITE]; if (diff > 4) { winner = PLAYER_BLACK; - position->phase = PHASE_GAMEOVER; + phase = PHASE_GAMEOVER; sprintf(cmdline, "Player1 win!"); cmdlist.emplace_back(string(cmdline)); @@ -932,7 +956,7 @@ bool StateInfo::checkGameOverCondition() if (diff < -4) { winner = PLAYER_WHITE; - position->phase = PHASE_GAMEOVER; + phase = PHASE_GAMEOVER; sprintf(cmdline, "Player2 win!"); cmdlist.emplace_back(string(cmdline)); @@ -942,8 +966,8 @@ bool StateInfo::checkGameOverCondition() #endif // 如果摆满了,根据规则判断胜负 - if (position->nPiecesOnBoard[BLACK] + position->nPiecesOnBoard[WHITE] >= Board::N_SEATS * Board::N_RINGS) { - position->phase = PHASE_GAMEOVER; + if (nPiecesOnBoard[BLACK] + nPiecesOnBoard[WHITE] >= Board::N_SEATS * Board::N_RINGS) { + phase = PHASE_GAMEOVER; if (rule.isStartingPlayerLoseWhenBoardFull) { winner = PLAYER_WHITE; @@ -959,15 +983,15 @@ bool StateInfo::checkGameOverCondition() } // 如果中局被“闷” - if (position->phase == PHASE_MOVING && position->action == ACTION_CHOOSE && position->board.isAllSurrounded(position->sideId, position->nPiecesOnBoard, position->sideToMove)) { + if (phase == PHASE_MOVING && action == ACTION_CHOOSE && board.isAllSurrounded(sideId, nPiecesOnBoard, sideToMove)) { // 规则要求被“闷”判负,则对手获胜 // TODO: 应该转移到下面的分支中 - position->phase = PHASE_GAMEOVER; + phase = PHASE_GAMEOVER; if (rule.isLoseWhenNoWay) { - tips = "玩家" + Player::chToStr(position->chSide) + "无子可走被闷"; - winner = Player::getOpponent(position->sideToMove); + tips = "玩家" + Player::chToStr(chSide) + "无子可走被闷"; + winner = Player::getOpponent(sideToMove); int winnerId = Player::toId(winner); - sprintf(cmdline, "Player%d no way to go. Player%d win!", position->sideId, winnerId); + sprintf(cmdline, "Player%d no way to go. Player%d win!", sideId, winnerId); cmdlist.emplace_back(string(cmdline)); return true; @@ -983,17 +1007,17 @@ bool StateInfo::checkGameOverCondition() } // 计算玩家1和玩家2的棋子活动能力之差 -int StateInfo::getMobilityDiff(player_t turn, int nPiecesOnBoard[], bool includeFobidden) +int Position::getMobilityDiff(player_t turn, int nPiecesOnBoard[], bool includeFobidden) { // TODO: 处理规则无禁点的情况 - location_t *locations = boardLocations; + location_t *locations = board.locations; int mobilityBlack = 0; int mobilityWhite = 0; int diff = 0; int n = 0; for (square_t i = SQ_BEGIN; i < SQ_END; i = static_cast(i + 1)) { - n = position->board.getSurroundedEmptyLocationCount(turn, nPiecesOnBoard, i, includeFobidden); + n = board.getSurroundedEmptyLocationCount(turn, nPiecesOnBoard, i, includeFobidden); if (locations[i] & PIECE_BLACK) { mobilityBlack += n; @@ -1007,7 +1031,7 @@ int StateInfo::getMobilityDiff(player_t turn, int nPiecesOnBoard[], bool include return diff; } -void StateInfo::cleanForbiddenLocations() +void Position::cleanForbiddenLocations() { if (!rule.hasForbiddenLocations) { return; @@ -1019,75 +1043,75 @@ void StateInfo::cleanForbiddenLocations() for (int s = 0; s < Board::N_SEATS; s++) { square = static_cast(r * Board::N_SEATS + s); - if (boardLocations[square] == '\x0f') { + if (board.locations[square] == '\x0f') { revertHash(square); - boardLocations[square] = '\x00'; + board.locations[square] = '\x00'; } } } } -void StateInfo::setSideToMove(player_t player) +void Position::setSideToMove(player_t player) { // 设置轮到谁走 - position->sideToMove = player; + sideToMove = player; - position->sideId = Player::toId(position->sideToMove); - position->chSide = Player::idToCh(position->sideId); + sideId = Player::toId(sideToMove); + chSide = Player::idToCh(sideId); - position->opponent = Player::getOpponent(player); + opponent = Player::getOpponent(player); - position->opponentId = Player::toId(position->opponent); - position->chOpponent = Player::idToCh(position->opponentId); + opponentId = Player::toId(opponent); + chOpponent = Player::idToCh(opponentId); } -player_t StateInfo::getSideToMove() +player_t Position::getSideToMove() { - return position->sideToMove; + return sideToMove; } -void StateInfo::changeSideToMove() +void Position::changeSideToMove() { - setSideToMove(Player::getOpponent(position->sideToMove)); + setSideToMove(Player::getOpponent(sideToMove)); } -bool StateInfo::doNullMove() +bool Position::doNullMove() { changeSideToMove(); return true; } -bool StateInfo::undoNullMove() +bool Position::undoNullMove() { changeSideToMove(); return true; } -void StateInfo::setTips() +void Position::setTips() { string winnerStr, t; int winnerId; - string turnStr = Player::chToStr(position->chSide); + string turnStr = Player::chToStr(chSide); - switch (position->phase) { + switch (phase) { case PHASE_READY: - tips = "轮到玩家1落子,剩余" + std::to_string(position->nPiecesInHand[BLACK]) + "子" + + tips = "轮到玩家1落子,剩余" + std::to_string(nPiecesInHand[BLACK]) + "子" + " 比分 " + to_string(score[BLACK]) + ":" + to_string(score[WHITE]) + ", 和棋 " + to_string(score_draw); break; case PHASE_PLACING: - if (position->action == ACTION_PLACE) { - tips = "轮到玩家" + turnStr + "落子,剩余" + std::to_string(position->nPiecesInHand[position->sideId]) + "子"; - } else if (position->action == ACTION_CAPTURE) { - tips = "成三!轮到玩家" + turnStr + "去子,需去" + std::to_string(position->nPiecesNeedRemove) + "子"; + if (action == ACTION_PLACE) { + tips = "轮到玩家" + turnStr + "落子,剩余" + std::to_string(nPiecesInHand[sideId]) + "子"; + } else if (action == ACTION_CAPTURE) { + tips = "成三!轮到玩家" + turnStr + "去子,需去" + std::to_string(nPiecesNeedRemove) + "子"; } break; case PHASE_MOVING: - if (position->action == ACTION_PLACE || position->action == ACTION_CHOOSE) { + if (action == ACTION_PLACE || action == ACTION_CHOOSE) { tips = "轮到玩家" + turnStr + "选子移动"; - } else if (position->action == ACTION_CAPTURE) { - tips = "成三!轮到玩家" + turnStr + "去子,需去" + std::to_string(position->nPiecesNeedRemove) + "子"; + } else if (action == ACTION_CAPTURE) { + tips = "成三!轮到玩家" + turnStr + "去子,需去" + std::to_string(nPiecesNeedRemove) + "子"; } break; @@ -1118,77 +1142,77 @@ void StateInfo::setTips() } } -time_t StateInfo::getElapsedTime(int playerId) +time_t Position::getElapsedTime(int playerId) { return elapsedSeconds[playerId]; } -void StateInfo::constructHash() +void Position::constructHash() { - position->hash = 0; + hash = 0; } -hash_t StateInfo::getHash() +hash_t Position::getHash() { // TODO: 每次获取哈希值时更新 hash 值剩余8位,放在此处调用不优雅 return updateHashMisc(); } -hash_t StateInfo::updateHash(square_t square) +hash_t Position::updateHash(square_t square) { - // PieceType is boardLocations[square] + // PieceType is board.locations[square] // 0b00 表示空白,0b01 = 1 表示先手棋子,0b10 = 2 表示后手棋子,0b11 = 3 表示禁点 - int pieceType = Player::toId(position->board.locationToPlayer(square)); + int pieceType = Player::toId(board.locationToPlayer(square)); // 清除或者放置棋子 - position->hash ^= zobrist[square][pieceType]; + hash ^= zobrist[square][pieceType]; - return position->hash; + return hash; } -hash_t StateInfo::revertHash(square_t square) +hash_t Position::revertHash(square_t square) { return updateHash(square); } -hash_t StateInfo::updateHashMisc() +hash_t Position::updateHashMisc() { const int HASH_MISC_BIT = 8; // 清除标记位 - position->hash = position->hash << HASH_MISC_BIT >> HASH_MISC_BIT; + hash = hash << HASH_MISC_BIT >> HASH_MISC_BIT; hash_t hi = 0; // 置位 - if (position->sideToMove == PLAYER_WHITE) { + if (sideToMove == PLAYER_WHITE) { hi |= 1U; } - if (position->action == ACTION_CAPTURE) { + if (action == ACTION_CAPTURE) { hi |= 1U << 1; } - hi |= static_cast(position->nPiecesNeedRemove) << 2; - hi |= static_cast(position->nPiecesInHand[BLACK]) << 4; // TODO: 或许换 position->phase 也可以? + hi |= static_cast(nPiecesNeedRemove) << 2; + hi |= static_cast(nPiecesInHand[BLACK]) << 4; // TODO: 或许换 phase 也可以? - position->hash = position->hash | (hi << (CHAR_BIT * sizeof(hash_t) - HASH_MISC_BIT)); + hash = hash | (hi << (CHAR_BIT * sizeof(hash_t) - HASH_MISC_BIT)); - return position->hash; + return hash; } -hash_t StateInfo::getNextMainHash(move_t m) +hash_t Position::getNextMainHash(move_t m) { - hash_t nextMainHash = position->hash /* << 8 >> 8 */; + hash_t nextMainHash = hash /* << 8 >> 8 */; square_t sq = SQ_0; if (m < 0) { sq = static_cast(-m); - int pieceType = Player::getOpponentById(Player::toId(position->sideToMove)); + int pieceType = Player::getOpponentById(Player::toId(sideToMove)); nextMainHash ^= zobrist[sq][pieceType]; - if (rule.hasForbiddenLocations && position->phase == PHASE_PLACING) { + if (rule.hasForbiddenLocations && phase == PHASE_PLACING) { nextMainHash ^= zobrist[sq][PIECETYPE_FORBIDDEN]; } @@ -1196,7 +1220,7 @@ hash_t StateInfo::getNextMainHash(move_t m) } sq = static_cast(m & 0x00ff); - int pieceType = Player::toId(position->sideToMove); + int pieceType = Player::toId(sideToMove); nextMainHash ^= zobrist[sq][pieceType]; if (m & 0x1f00) { diff --git a/src/game/position.h b/src/game/position.h index d1226715..82822096 100644 --- a/src/game/position.h +++ b/src/game/position.h @@ -44,6 +44,17 @@ class Node; class Position { public: + Position(); + virtual ~Position(); + + // 拷贝构造函数 + Position(Position &); + Position(const Position &); + + // 运算符重载 + Position &operator=(const Position &); + Position &operator=(Position &); + Board board; // 局面的哈希值 @@ -75,28 +86,8 @@ public: // 尚待去除的子数 int nPiecesNeedRemove {0}; -}; -// 棋类(在数据模型内,玩家只分先后手,不分黑白) -// 注意:StateInfo 类不是线程安全的! -// 所以不能跨线程修改 StateInfo 类的静态成员变量,切记! -class StateInfo -{ - // AI友元类 - friend class AIAlgorithm; - -public: - - StateInfo(); - virtual ~StateInfo(); - - // 拷贝构造函数 - StateInfo(StateInfo &); - StateInfo(const StateInfo &); - - // 运算符重载 - StateInfo &operator=(const StateInfo &); - StateInfo &operator=(StateInfo &); + ////////////////////////////////////// // 设置棋局状态和棋局,用于初始化 bool setPosition(const struct Rule *rule, @@ -109,7 +100,7 @@ public: // 获取棋盘数据 location_t *getBoardLocations() const { - return boardLocations; + return (location_t *)board.locations; } // 获取当前棋子位置点 @@ -133,13 +124,13 @@ public: // 获取局面阶段标识 enum phase_t getPhase() const { - return position->phase; + return phase; } // 获取动作状态标识 enum action_t getAction() const { - return position->action; + return action; } // 玩家1或玩家2的用时 @@ -178,19 +169,19 @@ public: // 玩家剩余未放置子数 int getPiecesInHandCount(int playerId) const { - return position->nPiecesInHand[playerId]; + return nPiecesInHand[playerId]; } // 玩家盘面剩余子数 int getPiecesOnBoardCount(int playerId) const { - return position->nPiecesOnBoard[playerId]; + return nPiecesOnBoard[playerId]; } // 尚待去除的子数 int getNum_NeedRemove() const { - return position->nPiecesNeedRemove; + return nPiecesNeedRemove; } // 计算玩家1和玩家2的棋子活动能力之差 @@ -239,17 +230,17 @@ public: void setTips(); // 着法生成 - void generateChildren(const Stack &moves, + void generateChildren(const Stack &moves, AIAlgorithm *ai, Node *node #ifdef TT_MOVE_ENABLE - , move_t ttMove + , move_t ttMove #endif // TT_MOVE_ENABLE ); // 着法生成 - int generateMoves(Stack &moves); - int generateNullMove(Stack &moves); + int generateMoves(Stack &moves); + int generateNullMove(Stack &moves); bool doNullMove(); bool undoNullMove(); @@ -272,7 +263,7 @@ public: #ifdef MCTS_AI // MCTS 相关 - Stack moves; + Stack moves; //template //void doRandomMove(RandomEngine *engine); @@ -290,17 +281,11 @@ public: int tm { -1 }; - // 棋局 - Position *position {nullptr}; - - // 棋局中的棋盘数据,单独提出来 - location_t *boardLocations {nullptr}; - // 棋谱 vector cmdlist; // 着法命令行用于棋谱的显示和解析, 当前着法的命令行指令,即一招棋谱 - char cmdline[64] {'\0'}; + char cmdline[64]{ '\0' }; /* 当前着法,AI会用到,如下表示 @@ -322,10 +307,10 @@ public: | / | \ | 29 ----- 28 ----- 27 */ - move_t move {MOVE_NONE}; + move_t move{ MOVE_NONE }; // 选中的棋子在board中的位置 - square_t currentSquare {}; + square_t currentSquare{}; private: @@ -342,16 +327,16 @@ private: player_t winner; // 当前步数 - step_t currentStep {}; + step_t currentStep{}; // 从走子阶段开始或上次吃子起的步数 - int moveStep {}; + int moveStep{}; - // 游戏起始时间 - time_t startTime {}; + // 游戏起始时间 + time_t startTime{}; // 当前游戏时间 - time_t currentTime {}; + time_t currentTime{}; // 玩家用时(秒) time_t elapsedSeconds[COLOR_COUNT]; @@ -360,4 +345,28 @@ private: string tips; }; +// 棋类(在数据模型内,玩家只分先后手,不分黑白) +// 注意:StateInfo 类不是线程安全的! +// 所以不能跨线程修改 StateInfo 类的静态成员变量,切记! +class StateInfo +{ + // AI友元类 + friend class AIAlgorithm; + +public: + + StateInfo(); + virtual ~StateInfo(); + + // 拷贝构造函数 + StateInfo(StateInfo &); + StateInfo(const StateInfo &); + + // 运算符重载 + StateInfo &operator=(const StateInfo &); + StateInfo &operator=(StateInfo &); + + Position *position { nullptr }; +}; + #endif /* POSITION_H */ diff --git a/src/game/types.h b/src/game/types.h index 32516244..9b106c61 100644 --- a/src/game/types.h +++ b/src/game/types.h @@ -39,7 +39,7 @@ enum move_t : int32_t //MOVE_NULL = 65 }; -static const int MOVE_COUNT = 40; +constexpr int MAX_MOVES = 40; enum color_t : uint8_t { diff --git a/src/ui/qt/gamecontroller.cpp b/src/ui/qt/gamecontroller.cpp index 19164ef4..df1ac0d9 100644 --- a/src/ui/qt/gamecontroller.cpp +++ b/src/ui/qt/gamecontroller.cpp @@ -148,7 +148,7 @@ extern deque openingBookDequeBak; void GameController::gameStart() { - state.start(); + state.position->start(); st = state; // 每隔100毫秒调用一次定时器处理函数 @@ -177,7 +177,7 @@ void GameController::gameReset() timeID = 0; // 重置游戏 - state.reset(); + state.position->reset(); st = state; // 停掉线程 @@ -247,7 +247,7 @@ void GameController::gameReset() // 更新棋谱 manualListModel.removeRows(0, manualListModel.rowCount()); manualListModel.insertRow(0); - manualListModel.setData(manualListModel.index(0), state.getCmdLine()); + manualListModel.setData(manualListModel.index(0), state.position->getCmdLine()); currentRow = 0; // 发出信号通知主窗口更新LCD显示 @@ -256,22 +256,22 @@ void GameController::gameReset() emit time2Changed(qtime.toString("hh:mm:ss")); // 发信号更新状态栏 - message = QString::fromStdString(state.getTips()); + message = QString::fromStdString(state.position->getTips()); emit statusBarChanged(message); // 更新比分 LCD 显示 - emit nGamesPlayedChanged(QString::number(state.nPlayed, 10)); - emit score1Changed(QString::number(state.score[BLACK], 10)); - emit score2Changed(QString::number(state.score[WHITE], 10)); - emit scoreDrawChanged(QString::number(state.score_draw, 10)); + emit nGamesPlayedChanged(QString::number(state.position->nPlayed, 10)); + emit score1Changed(QString::number(state.position->score[BLACK], 10)); + emit score2Changed(QString::number(state.position->score[WHITE], 10)); + emit scoreDrawChanged(QString::number(state.position->score_draw, 10)); // 更新胜率 LCD 显示 - state.nPlayed = state.score[BLACK] + state.score[WHITE] + state.score_draw; + state.position->nPlayed = state.position->score[BLACK] + state.position->score[WHITE] + state.position->score_draw; int winningRate_1 = 0, winningRate_2 = 0, winningRate_draw = 0; - if (state.nPlayed != 0) { - winningRate_1 = state.score[BLACK] * 10000 / state.nPlayed; - winningRate_2 = state.score[WHITE] * 10000 / state.nPlayed; - winningRate_draw = state.score_draw * 10000 / state.nPlayed; + if (state.position->nPlayed != 0) { + winningRate_1 = state.position->score[BLACK] * 10000 / state.position->nPlayed; + winningRate_2 = state.position->score[WHITE] * 10000 / state.position->nPlayed; + winningRate_draw = state.position->score_draw * 10000 / state.position->nPlayed; } emit winningRate1Changed(QString::number(winningRate_1, 10)); @@ -326,7 +326,7 @@ void GameController::setRule(int ruleNo, step_t stepLimited /*= -1*/, int timeLi } // 设置模型规则,重置游戏 - state.setPosition(&RULES[ruleNo]); + state.position->setPosition(&RULES[ruleNo]); st = state; // 重置游戏 @@ -558,13 +558,13 @@ void GameController::flip() #ifndef TRAINING_MODE stopAndWaitAiThreads(); - state.position->board.mirror(state.cmdlist, state.cmdline, state.move, state.currentSquare); - state.position->board.rotate(180, state.cmdlist, state.cmdline, state.move, state.currentSquare); + state.position->board.mirror(state.position->cmdlist, state.position->cmdline, state.position->move, state.position->currentSquare); + state.position->board.rotate(180, state.position->cmdlist, state.position->cmdline, state.position->move, state.position->currentSquare); st = state; // 更新棋谱 int row = 0; - for (const auto &str : *(state.getCmdList())) { + for (const auto &str : *(state.position->getCmdList())) { manualListModel.setData(manualListModel.index(row++), str.c_str()); } @@ -585,13 +585,13 @@ void GameController::mirror() #ifndef TRAINING_MODE stopAndWaitAiThreads(); - state.position->board.mirror(state.cmdlist, state.cmdline, state.move, state.currentSquare); + state.position->board.mirror(state.position->cmdlist, state.position->cmdline, state.position->move, state.position->currentSquare); st = state; // 更新棋谱 int row = 0; - for (const auto &str : *(state.getCmdList())) { + for (const auto &str : *(state.position->getCmdList())) { manualListModel.setData(manualListModel.index(row++), str.c_str()); } @@ -614,13 +614,13 @@ void GameController::turnRight() #ifndef TRAINING_MODE stopAndWaitAiThreads(); - state.position->board.rotate(-90, state.cmdlist, state.cmdline, state.move, state.currentSquare); + state.position->board.rotate(-90, state.position->cmdlist, state.position->cmdline, state.position->move, state.position->currentSquare); st = state; // 更新棋谱 int row = 0; - for (const auto &str : *(state.getCmdList())) { + for (const auto &str : *(state.position->getCmdList())) { manualListModel.setData(manualListModel.index(row++), str.c_str()); } @@ -641,12 +641,12 @@ void GameController::turnLeft() #ifndef TRAINING_MODE stopAndWaitAiThreads(); - state.position->board.rotate(90, state.cmdlist, state.cmdline, state.move, state.currentSquare); + state.position->board.rotate(90, state.position->cmdlist, state.position->cmdline, state.position->move, state.position->currentSquare); st = state; // 更新棋谱 int row = 0; - for (const auto &str : *(state.getCmdList())) { + for (const auto &str : *(state.position->getCmdList())) { manualListModel.setData(manualListModel.index(row++), str.c_str()); } @@ -664,9 +664,9 @@ void GameController::timerEvent(QTimerEvent *event) static QTime qt1, qt2; // 玩家的已用时间 - state.update(); - remainingTime[BLACK] = state.getElapsedTime(BLACK); - remainingTime[WHITE] = state.getElapsedTime(WHITE); + state.position->update(); + remainingTime[BLACK] = state.position->getElapsedTime(BLACK); + remainingTime[WHITE] = state.position->getElapsedTime(WHITE); // 如果规则要求计时,则time1和time2表示倒计时 if (timeLimit > 0) { @@ -682,7 +682,7 @@ void GameController::timerEvent(QTimerEvent *event) emit time2Changed(qt2.toString("hh:mm:ss")); // 如果胜负已分 - player_t winner = state.getWinner(); + player_t winner = state.position->getWinner(); if (winner != PLAYER_NOBODY) { // 停止计时 killTimer(timeID); @@ -692,7 +692,7 @@ void GameController::timerEvent(QTimerEvent *event) #ifndef TRAINING_MODE // 发信号更新状态栏 - message = QString::fromStdString(state.getTips()); + message = QString::fromStdString(state.position->getTips()); emit statusBarChanged(message); // 弹框 @@ -768,13 +768,13 @@ bool GameController::actionPiece(QPointF pos) manualListModel.removeRows(currentRow + 1, manualListModel.rowCount() - currentRow - 1); // 如果再决出胜负后悔棋,则重新启动计时 - if (state.getWinner() == PLAYER_NOBODY) { + if (state.position->getWinner() == PLAYER_NOBODY) { // 重新启动计时 timeID = startTimer(100); // 发信号更新状态栏 - message = QString::fromStdString(state.getTips()); + message = QString::fromStdString(state.position->getTips()); emit statusBarChanged(message); #ifndef MOBILE_APP_UI } @@ -785,7 +785,7 @@ bool GameController::actionPiece(QPointF pos) } // 如果未开局则开局 - if (state.getPhase() == PHASE_READY) + if (state.position->getPhase() == PHASE_READY) gameStart(); // 判断执行选子、落子或去子 @@ -793,15 +793,15 @@ bool GameController::actionPiece(QPointF pos) PieceItem *piece = nullptr; QGraphicsItem *item = scene.itemAt(pos, QTransform()); - switch (state.getAction()) { + switch (state.position->getAction()) { case ACTION_PLACE: - if (state._place(r, s)) { - if (state.getAction() == ACTION_CAPTURE) { + if (state.position->_place(r, s)) { + if (state.position->getAction() == ACTION_CAPTURE) { // 播放成三音效 - playSound(GAME_SOUND_MILL, state.getSideToMove()); + playSound(GAME_SOUND_MILL, state.position->getSideToMove()); } else { // 播放移动棋子音效 - playSound(GAME_SOUND_DROG, state.getSideToMove()); + playSound(GAME_SOUND_DROG, state.position->getSideToMove()); } result = true; break; @@ -814,24 +814,24 @@ bool GameController::actionPiece(QPointF pos) piece = qgraphicsitem_cast(item); if (!piece) break; - if (state.choose(r, s)) { + if (state.position->choose(r, s)) { // 播放选子音效 - playSound(GAME_SOUND_CHOOSE, state.getSideToMove()); + playSound(GAME_SOUND_CHOOSE, state.position->getSideToMove()); result = true; } else { // 播放禁止音效 - playSound(GAME_SOUND_FORBIDDEN, state.getSideToMove()); + playSound(GAME_SOUND_FORBIDDEN, state.position->getSideToMove()); } break; case ACTION_CAPTURE: - if (state._capture(r, s)) { + if (state.position->_capture(r, s)) { // 播放音效 - playSound(GAME_SOUND_CAPTURE, state.getSideToMove()); + playSound(GAME_SOUND_CAPTURE, state.position->getSideToMove()); result = true; } else { // 播放禁止音效 - playSound(GAME_SOUND_FORBIDDEN, state.getSideToMove()); + playSound(GAME_SOUND_FORBIDDEN, state.position->getSideToMove()); } break; @@ -842,7 +842,7 @@ bool GameController::actionPiece(QPointF pos) if (result) { // 发信号更新状态栏 - message = QString::fromStdString(state.getTips()); + message = QString::fromStdString(state.position->getTips()); emit statusBarChanged(message); // 将新增的棋谱行插入到ListModel @@ -850,7 +850,7 @@ bool GameController::actionPiece(QPointF pos) int k = 0; // 输出命令行 - for (const auto & i : *(state.getCmdList())) { + for (const auto & i : *(state.position->getCmdList())) { // 跳过已添加的,因标准list容器没有下标 if (k++ <= currentRow) continue; @@ -860,7 +860,7 @@ bool GameController::actionPiece(QPointF pos) // 播放胜利或失败音效 #ifndef DONOT_PLAY_WIN_SOUND - player_t winner = state.getWinner(); + player_t winner = state.position->getWinner(); if (winner != PLAYER_NOBODY && (manualListModel.data(manualListModel.index(currentRow - 1))).toString().contains("Time over.")) playSound(GAME_SOUND_WIN, winner); @@ -868,7 +868,7 @@ bool GameController::actionPiece(QPointF pos) // AI设置 // 如果还未决出胜负 - if (state.getWinner() == PLAYER_NOBODY) { + if (state.position->getWinner() == PLAYER_NOBODY) { resumeAiThreads(state.position->sideToMove); } // 如果已经决出胜负 @@ -887,7 +887,7 @@ bool GameController::actionPiece(QPointF pos) bool GameController::giveUp() { - bool result = state.giveup(state.position->sideToMove); + bool result = state.position->giveup(state.position->sideToMove); if (!result) { return false; @@ -900,7 +900,7 @@ bool GameController::giveUp() int k = 0; // 输出命令行 - for (const auto & i : *(state.getCmdList())) { + for (const auto & i : *(state.position->getCmdList())) { // 跳过已添加的,因标准list容器没有下标 if (k++ <= currentRow) continue; @@ -908,8 +908,8 @@ bool GameController::giveUp() manualListModel.setData(manualListModel.index(currentRow), i.c_str()); } - if (state.getWinner() != PLAYER_NOBODY) - playSound(GAME_SOUND_GIVE_UP, state.getSideToMove()); + if (state.position->getWinner() != PLAYER_NOBODY) + playSound(GAME_SOUND_GIVE_UP, state.position->getSideToMove()); #endif // TRAINING_MODE @@ -934,7 +934,7 @@ bool GameController::command(const QString &cmd, bool update /* = true */) // 声音 sound_t soundType = GAME_SOUND_NONE; - switch (state.getAction()) { + switch (state.position->getAction()) { case ACTION_CHOOSE: case ACTION_PLACE: soundType = GAME_SOUND_DROG; @@ -948,45 +948,45 @@ bool GameController::command(const QString &cmd, bool update /* = true */) #endif // 如果未开局则开局 - if (state.getPhase() == PHASE_READY) { + if (state.position->getPhase() == PHASE_READY) { gameStart(); } - if (!state.command(cmd.toStdString().c_str())) + if (!state.position->command(cmd.toStdString().c_str())) return false; #ifndef TRAINING_MODE - if (soundType == GAME_SOUND_DROG && state.getAction() == ACTION_CAPTURE) { + if (soundType == GAME_SOUND_DROG && state.position->getAction() == ACTION_CAPTURE) { soundType = GAME_SOUND_MILL; } if (update) { - playSound(soundType, state.getSideToMove()); + playSound(soundType, state.position->getSideToMove()); updateScence(state); } // 发信号更新状态栏 - message = QString::fromStdString(state.getTips()); + message = QString::fromStdString(state.position->getTips()); emit statusBarChanged(message); // 对于新开局 - if (state.getCmdList()->size() <= 1) { + if (state.position->getCmdList()->size() <= 1) { manualListModel.removeRows(0, manualListModel.rowCount()); manualListModel.insertRow(0); - manualListModel.setData(manualListModel.index(0), state.getCmdLine()); + manualListModel.setData(manualListModel.index(0), state.position->getCmdLine()); currentRow = 0; } // 对于当前局 else { currentRow = manualListModel.rowCount() - 1; // 跳过已添加行,迭代器不支持+运算符,只能一个个++ - auto i = (state.getCmdList()->begin()); - for (int r = 0; i != (state.getCmdList())->end(); i++) { + auto i = (state.position->getCmdList()->begin()); + for (int r = 0; i != (state.position->getCmdList())->end(); i++) { if (r++ > currentRow) break; } // 将新增的棋谱行插入到ListModel - while (i != state.getCmdList()->end()) { + while (i != state.position->getCmdList()->end()) { manualListModel.insertRow(++currentRow); manualListModel.setData(manualListModel.index(currentRow), (*i++).c_str()); } @@ -994,7 +994,7 @@ bool GameController::command(const QString &cmd, bool update /* = true */) // 播放胜利或失败音效 #ifndef DONOT_PLAY_WIN_SOUND - player_t winner = state.getWinner(); + player_t winner = state.position->getWinner(); if (winner != PLAYER_NOBODY && (manualListModel.data(manualListModel.index(currentRow - 1))).toString().contains("Time over.")) { playSound(GAME_SOUND_WIN, winner); @@ -1004,7 +1004,7 @@ bool GameController::command(const QString &cmd, bool update /* = true */) // AI设置 // 如果还未决出胜负 - if (state.getWinner() == PLAYER_NOBODY) { + if (state.position->getWinner() == PLAYER_NOBODY) { resumeAiThreads(state.position->sideToMove); } // 如果已经决出胜负 @@ -1077,7 +1077,7 @@ bool GameController::command(const QString &cmd, bool update /* = true */) #ifdef MESSAGEBOX_ENABLE // 弹框 - message = QString::fromStdString(state.getTips()); + message = QString::fromStdString(state.position->getTips()); QMessageBox::about(NULL, "游戏结果", message); #endif } @@ -1113,11 +1113,11 @@ bool GameController::phaseChange(int row, bool forceUpdate) for (int i = 0; i <= row; i++) { loggerDebug("%s\n", mlist.at(i).toStdString().c_str()); - st.command(mlist.at(i).toStdString().c_str()); + st.position->command(mlist.at(i).toStdString().c_str()); } // 下面这步关键,会让悔棋者承担时间损失 - st.setStartTime(static_cast(state.getStartTimeb())); + st.position->setStartTime(static_cast(state.position->getStartTimeb())); // 刷新棋局场景 updateScence(st); @@ -1138,7 +1138,7 @@ bool GameController::updateScence() bool GameController::updateScence(StateInfo &g) { #ifndef TRAINING_MODE - const location_t *board = g.getBoardLocations(); + const location_t *board = g.position->getBoardLocations(); QPointF pos; // game类中的棋子代码 @@ -1192,10 +1192,10 @@ bool GameController::updateScence(StateInfo &g) if (j == (Board::N_SEATS) * (Board::N_RINGS + 1)) { // 判断是被吃掉的子,还是未安放的子 if (key & PIECE_BLACK) { - pos = (key - 0x11 < nTotalPieces / 2 - g.getPiecesInHandCount(BLACK)) ? + pos = (key - 0x11 < nTotalPieces / 2 - g.position->getPiecesInHandCount(BLACK)) ? scene.pos_p2_g : scene.pos_p1; } else { - pos = (key - 0x21 < nTotalPieces / 2 - g.getPiecesInHandCount(WHITE)) ? + pos = (key - 0x21 < nTotalPieces / 2 - g.position->getPiecesInHandCount(WHITE)) ? scene.pos_p1_g : scene.pos_p2; } @@ -1204,7 +1204,7 @@ bool GameController::updateScence(StateInfo &g) deletedPiece = piece; #ifdef GAME_PLACING_SHOW_CAPTURED_PIECES - if (state.getPhase() == GAME_MOVING) { + if (state.position->getPhase() == GAME_MOVING) { #endif QPropertyAnimation *animation = new QPropertyAnimation(piece, "pos"); animation->setDuration(durationTime); @@ -1222,7 +1222,7 @@ bool GameController::updateScence(StateInfo &g) } // 添加摆棋阶段禁子点 - if (rule.hasForbiddenLocations && g.getPhase() == PHASE_PLACING) { + if (rule.hasForbiddenLocations && g.position->getPhase() == PHASE_PLACING) { for (int j = SQ_BEGIN; j < SQ_END; j++) { if (board[j] == PIECE_FORBIDDEN) { pos = scene.rs2pos(j / Board::N_SEATS, j % Board::N_SEATS + 1); @@ -1241,7 +1241,7 @@ bool GameController::updateScence(StateInfo &g) } // 走棋阶段清除禁子点 - if (rule.hasForbiddenLocations && g.getPhase() != PHASE_PLACING) { + if (rule.hasForbiddenLocations && g.position->getPhase() != PHASE_PLACING) { while (nTotalPieces < static_cast(pieceList.size())) { delete pieceList.at(pieceList.size() - 1); pieceList.pop_back(); @@ -1249,9 +1249,9 @@ bool GameController::updateScence(StateInfo &g) } // 选中当前棋子 - int ipos = g.getCurrentSquare(); + int ipos = g.position->getCurrentSquare(); if (ipos) { - key = board[g.getCurrentSquare()]; + key = board[g.position->getCurrentSquare()]; ipos = key & PIECE_BLACK ? (key - PIECE_B1) * 2 : (key - PIECE_W1) * 2 + 1; if (ipos >= 0 && ipos < nTotalPieces) { currentPiece = pieceList.at(static_cast(ipos)); @@ -1267,17 +1267,17 @@ bool GameController::updateScence(StateInfo &g) animationGroup->start(QAbstractAnimation::DeleteWhenStopped); // 更新比分 LCD 显示 - emit score1Changed(QString::number(g.score[BLACK], 10)); - emit score2Changed(QString::number(g.score[WHITE], 10)); - emit scoreDrawChanged(QString::number(g.score_draw, 10)); + emit score1Changed(QString::number(g.position->score[BLACK], 10)); + emit score2Changed(QString::number(g.position->score[WHITE], 10)); + emit scoreDrawChanged(QString::number(g.position->score_draw, 10)); // 更新胜率 LCD 显示 - state.nPlayed = state.score[BLACK] + state.score[WHITE] + state.score_draw; + state.position->nPlayed = state.position->score[BLACK] + state.position->score[WHITE] + state.position->score_draw; int winningRate_1 = 0, winningRate_2 = 0, winningRate_draw = 0; - if (state.nPlayed != 0) { - winningRate_1 = state.score[BLACK] * 10000 / state.nPlayed; - winningRate_2 = state.score[WHITE] * 10000 / state.nPlayed; - winningRate_draw = state.score_draw * 10000 / state.nPlayed; + if (state.position->nPlayed != 0) { + winningRate_1 = state.position->score[BLACK] * 10000 / state.position->nPlayed; + winningRate_2 = state.position->score[WHITE] * 10000 / state.position->nPlayed; + winningRate_draw = state.position->score_draw * 10000 / state.position->nPlayed; } emit winningRate1Changed(QString::number(winningRate_1, 10)); @@ -1303,7 +1303,7 @@ void GameController::showTestWindow() void GameController::humanGiveUp() { - if (state.getWinner() == PLAYER_NOBODY) { + if (state.position->getWinner() == PLAYER_NOBODY) { giveUp(); } } @@ -1354,16 +1354,16 @@ void GameController::saveScore() textStream << "" << endl; - state.nPlayed = state.score[BLACK] + state.score[WHITE] + state.score_draw; + state.position->nPlayed = state.position->score[BLACK] + state.position->score[WHITE] + state.position->score_draw; - if (state.nPlayed == 0) { + if (state.position->nPlayed == 0) { goto out; } - textStream << "Sum\t" + QString::number(state.nPlayed) << endl; - textStream << "Black\t" + QString::number(state.score[BLACK]) + "\t" + QString::number(state.score[BLACK] * 10000 / state.nPlayed) << endl; - textStream << "White\t" + QString::number(state.score[WHITE]) + "\t" + QString::number(state.score[WHITE] * 10000 / state.nPlayed) << endl; - textStream << "Draw\t" + QString::number(state.score_draw) + "\t" + QString::number(state.score_draw * 10000 / state.nPlayed) << endl; + textStream << "Sum\t" + QString::number(state.position->nPlayed) << endl; + textStream << "Black\t" + QString::number(state.position->score[BLACK]) + "\t" + QString::number(state.position->score[BLACK] * 10000 / state.position->nPlayed) << endl; + textStream << "White\t" + QString::number(state.position->score[WHITE]) + "\t" + QString::number(state.position->score[WHITE] * 10000 / state.position->nPlayed) << endl; + textStream << "Draw\t" + QString::number(state.position->score_draw) + "\t" + QString::number(state.position->score_draw * 10000 / state.position->nPlayed) << endl; out: file.flush(); diff --git a/src/ui/qt/gamewindow.cpp b/src/ui/qt/gamewindow.cpp index 41fec764..eb9b4021 100644 --- a/src/ui/qt/gamewindow.cpp +++ b/src/ui/qt/gamewindow.cpp @@ -608,7 +608,7 @@ void MillGameWindow::on_actionNew_N_triggered() QString strDate = QDateTime::currentDateTime().toString("yyyy-MM-dd"); QString whoWin; - switch (gameController->getState().getWinner()) { + switch (gameController->getState().position->getWinner()) { case PLAYER_BLACK: whoWin = "Black-Win"; break;