parent
4b4c754050
commit
073f4ffa01
|
@ -21,7 +21,7 @@
|
|||
|
||||
#include "evaluate.h"
|
||||
|
||||
value_t Evaluation::getValue(MillGame &gameTemp, GameContext *gameContext, MillGameAi_ab::Node *node)
|
||||
value_t Evaluation::getValue(Position &dummyPosition, PositionContext *positionContext, MillGameAi_ab::Node *node)
|
||||
{
|
||||
// 初始评估值为0,对先手有利则增大,对后手有利则减小
|
||||
value_t value = 0;
|
||||
|
@ -31,31 +31,31 @@ value_t Evaluation::getValue(MillGame &gameTemp, GameContext *gameContext, MillG
|
|||
int nPiecesNeedRemove = 0;
|
||||
|
||||
#ifdef DEBUG_AB_TREE
|
||||
node->stage = gameContext->stage;
|
||||
node->action = gameContext->action;
|
||||
node->stage = positionContext->stage;
|
||||
node->action = positionContext->action;
|
||||
node->evaluated = true;
|
||||
#endif
|
||||
|
||||
switch (gameContext->stage) {
|
||||
switch (positionContext->stage) {
|
||||
case GAME_NOTSTARTED:
|
||||
break;
|
||||
|
||||
case GAME_PLACING:
|
||||
// 按手中的棋子计分,不要break;
|
||||
nPiecesInHandDiff = gameContext->nPiecesInHand_1 - gameContext->nPiecesInHand_2;
|
||||
nPiecesInHandDiff = positionContext->nPiecesInHand_1 - positionContext->nPiecesInHand_2;
|
||||
value += nPiecesInHandDiff * 50;
|
||||
#ifdef DEBUG_AB_TREE
|
||||
node->nPiecesInHandDiff = nPiecesInHandDiff;
|
||||
#endif
|
||||
|
||||
// 按场上棋子计分
|
||||
nPiecesOnBoardDiff = gameContext->nPiecesOnBoard_1 - gameContext->nPiecesOnBoard_2;
|
||||
nPiecesOnBoardDiff = positionContext->nPiecesOnBoard_1 - positionContext->nPiecesOnBoard_2;
|
||||
value += nPiecesOnBoardDiff * 100;
|
||||
#ifdef DEBUG_AB_TREE
|
||||
node->nPiecesOnBoardDiff = nPiecesOnBoardDiff;
|
||||
#endif
|
||||
|
||||
switch (gameContext->action) {
|
||||
switch (positionContext->action) {
|
||||
// 选子和落子使用相同的评价方法
|
||||
case ACTION_CHOOSE:
|
||||
case ACTION_PLACE:
|
||||
|
@ -63,8 +63,8 @@ value_t Evaluation::getValue(MillGame &gameTemp, GameContext *gameContext, MillG
|
|||
|
||||
// 如果形成去子状态,每有一个可去的子,算100分
|
||||
case ACTION_CAPTURE:
|
||||
nPiecesNeedRemove = (gameContext->turn == PLAYER1) ?
|
||||
gameContext->nPiecesNeedRemove : -(gameContext->nPiecesNeedRemove);
|
||||
nPiecesNeedRemove = (positionContext->turn == PLAYER1) ?
|
||||
positionContext->nPiecesNeedRemove : -(positionContext->nPiecesNeedRemove);
|
||||
value += nPiecesNeedRemove * 100;
|
||||
#ifdef DEBUG_AB_TREE
|
||||
node->nPiecesNeedRemove = nPiecesNeedRemove;
|
||||
|
@ -78,14 +78,14 @@ value_t Evaluation::getValue(MillGame &gameTemp, GameContext *gameContext, MillG
|
|||
|
||||
case GAME_MOVING:
|
||||
// 按场上棋子计分
|
||||
value += gameContext->nPiecesOnBoard_1 * 100 - gameContext->nPiecesOnBoard_2 * 100;
|
||||
value += positionContext->nPiecesOnBoard_1 * 100 - positionContext->nPiecesOnBoard_2 * 100;
|
||||
|
||||
#ifdef EVALUATE_MOBILITY
|
||||
// 按棋子活动能力计分
|
||||
value += gameTemp.getMobilityDiff(gameContext->turn, gameTemp.currentRule, gameContext->nPiecesInHand_1, gameContext->nPiecesInHand_2, false) * 10;
|
||||
value += dummyPosition.getMobilityDiff(positionContext->turn, dummyPosition.currentRule, positionContext->nPiecesInHand_1, positionContext->nPiecesInHand_2, false) * 10;
|
||||
#endif /* EVALUATE_MOBILITY */
|
||||
|
||||
switch (gameContext->action) {
|
||||
switch (positionContext->action) {
|
||||
// 选子和落子使用相同的评价方法
|
||||
case ACTION_CHOOSE:
|
||||
case ACTION_PLACE:
|
||||
|
@ -93,8 +93,8 @@ value_t Evaluation::getValue(MillGame &gameTemp, GameContext *gameContext, MillG
|
|||
|
||||
// 如果形成去子状态,每有一个可去的子,算128分
|
||||
case ACTION_CAPTURE:
|
||||
nPiecesNeedRemove = (gameContext->turn == PLAYER1) ?
|
||||
gameContext->nPiecesNeedRemove : -(gameContext->nPiecesNeedRemove);
|
||||
nPiecesNeedRemove = (positionContext->turn == PLAYER1) ?
|
||||
positionContext->nPiecesNeedRemove : -(positionContext->nPiecesNeedRemove);
|
||||
value += nPiecesNeedRemove * 128;
|
||||
#ifdef DEBUG_AB_TREE
|
||||
node->nPiecesNeedRemove = nPiecesNeedRemove;
|
||||
|
@ -109,9 +109,9 @@ value_t Evaluation::getValue(MillGame &gameTemp, GameContext *gameContext, MillG
|
|||
// 终局评价最简单
|
||||
case GAME_OVER:
|
||||
// 布局阶段闷棋判断
|
||||
if (gameContext->nPiecesOnBoard_1 + gameContext->nPiecesOnBoard_2 >=
|
||||
if (positionContext->nPiecesOnBoard_1 + positionContext->nPiecesOnBoard_2 >=
|
||||
Board::N_SEATS * Board::N_RINGS) {
|
||||
if (gameTemp.getRule()->isStartingPlayerLoseWhenBoardFull) {
|
||||
if (dummyPosition.getRule()->isStartingPlayerLoseWhenBoardFull) {
|
||||
// winner = PLAYER2;
|
||||
value -= 10000;
|
||||
#ifdef DEBUG_AB_TREE
|
||||
|
@ -123,11 +123,11 @@ value_t Evaluation::getValue(MillGame &gameTemp, GameContext *gameContext, MillG
|
|||
}
|
||||
|
||||
// 走棋阶段被闷判断
|
||||
if (gameContext->action == ACTION_CHOOSE &&
|
||||
gameTemp.context.board.isAllSurrounded(gameContext->turn, gameTemp.currentRule, gameContext->nPiecesOnBoard_1, gameContext->nPiecesOnBoard_2, gameContext->turn) &&
|
||||
gameTemp.getRule()->isLoseWhenNoWay) {
|
||||
if (positionContext->action == ACTION_CHOOSE &&
|
||||
dummyPosition.context.board.isAllSurrounded(positionContext->turn, dummyPosition.currentRule, positionContext->nPiecesOnBoard_1, positionContext->nPiecesOnBoard_2, positionContext->turn) &&
|
||||
dummyPosition.getRule()->isLoseWhenNoWay) {
|
||||
// 规则要求被“闷”判负,则对手获胜
|
||||
if (gameContext->turn == PLAYER1) {
|
||||
if (positionContext->turn == PLAYER1) {
|
||||
value -= 10000;
|
||||
#ifdef DEBUG_AB_TREE
|
||||
node->result = -2;
|
||||
|
@ -141,12 +141,12 @@ value_t Evaluation::getValue(MillGame &gameTemp, GameContext *gameContext, MillG
|
|||
}
|
||||
|
||||
// 剩余棋子个数判断
|
||||
if (gameContext->nPiecesOnBoard_1 < gameTemp.getRule()->nPiecesAtLeast) {
|
||||
if (positionContext->nPiecesOnBoard_1 < dummyPosition.getRule()->nPiecesAtLeast) {
|
||||
value -= 10000;
|
||||
#ifdef DEBUG_AB_TREE
|
||||
node->result = -1;
|
||||
#endif
|
||||
} else if (gameContext->nPiecesOnBoard_2 < gameTemp.getRule()->nPiecesAtLeast) {
|
||||
} else if (positionContext->nPiecesOnBoard_2 < dummyPosition.getRule()->nPiecesAtLeast) {
|
||||
value += 10000;
|
||||
#ifdef DEBUG_AB_TREE
|
||||
node->result = 1;
|
||||
|
|
|
@ -34,7 +34,7 @@ public:
|
|||
|
||||
Evaluation &operator=(const Evaluation &) = delete;
|
||||
|
||||
static value_t getValue(MillGame &gameTemp, GameContext *gameContext, MillGameAi_ab::Node *node);
|
||||
static value_t getValue(Position &dummyPosition, PositionContext *positionContext, MillGameAi_ab::Node *node);
|
||||
|
||||
// 评估子力
|
||||
#ifdef EVALUATE_ENABLE
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "movegen.h"
|
||||
|
||||
void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &gameTemp,
|
||||
void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, Position &dummyPosition,
|
||||
MillGameAi_ab::Node *node, MillGameAi_ab::Node *rootNode,
|
||||
move_t bestMove)
|
||||
{
|
||||
|
@ -32,23 +32,23 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &gameTemp,
|
|||
size_t newCapacity = 24;
|
||||
|
||||
// 留足余量空间避免多次重新分配,此动作本身也占用 CPU/内存 开销
|
||||
switch (gameTemp.getStage()) {
|
||||
switch (dummyPosition.getStage()) {
|
||||
case GAME_PLACING:
|
||||
if (gameTemp.getAction() == ACTION_CAPTURE) {
|
||||
if (gameTemp.whosTurn() == PLAYER1)
|
||||
newCapacity = static_cast<size_t>(gameTemp.getPiecesOnBoardCount_2());
|
||||
if (dummyPosition.getAction() == ACTION_CAPTURE) {
|
||||
if (dummyPosition.whosTurn() == PLAYER1)
|
||||
newCapacity = static_cast<size_t>(dummyPosition.getPiecesOnBoardCount_2());
|
||||
else
|
||||
newCapacity = static_cast<size_t>(gameTemp.getPiecesOnBoardCount_1());
|
||||
newCapacity = static_cast<size_t>(dummyPosition.getPiecesOnBoardCount_1());
|
||||
} else {
|
||||
newCapacity = static_cast<size_t>(gameTemp.getPiecesInHandCount_1() + gameTemp.getPiecesInHandCount_2());
|
||||
newCapacity = static_cast<size_t>(dummyPosition.getPiecesInHandCount_1() + dummyPosition.getPiecesInHandCount_2());
|
||||
}
|
||||
break;
|
||||
case GAME_MOVING:
|
||||
if (gameTemp.getAction() == ACTION_CAPTURE) {
|
||||
if (gameTemp.whosTurn() == PLAYER1)
|
||||
newCapacity = static_cast<size_t>(gameTemp.getPiecesOnBoardCount_2());
|
||||
if (dummyPosition.getAction() == ACTION_CAPTURE) {
|
||||
if (dummyPosition.whosTurn() == PLAYER1)
|
||||
newCapacity = static_cast<size_t>(dummyPosition.getPiecesOnBoardCount_2());
|
||||
else
|
||||
newCapacity = static_cast<size_t>(gameTemp.getPiecesOnBoardCount_1());
|
||||
newCapacity = static_cast<size_t>(dummyPosition.getPiecesOnBoardCount_1());
|
||||
} else {
|
||||
newCapacity = 6;
|
||||
}
|
||||
|
@ -69,28 +69,28 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &gameTemp,
|
|||
}
|
||||
|
||||
// 对手
|
||||
Player opponent = MillGame::getOpponent(gameTemp.context.turn);
|
||||
Player opponent = Position::getOpponent(dummyPosition.context.turn);
|
||||
|
||||
// 列出所有合法的下一招
|
||||
switch (gameTemp.context.action) {
|
||||
switch (dummyPosition.context.action) {
|
||||
// 对于选子和落子动作
|
||||
case ACTION_CHOOSE:
|
||||
case ACTION_PLACE:
|
||||
// 对于摆子阶段
|
||||
if (gameTemp.context.stage & (GAME_PLACING | GAME_NOTSTARTED)) {
|
||||
if (dummyPosition.context.stage & (GAME_PLACING | GAME_NOTSTARTED)) {
|
||||
for (int i : movePriorityTable) {
|
||||
location = i;
|
||||
|
||||
if (gameTemp.board_[location]) {
|
||||
if (dummyPosition.board_[location]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (gameTemp.context.stage != GAME_NOTSTARTED || node != rootNode) {
|
||||
ai_ab.addNode(node, 0, location, bestMove, gameTemp.context.turn);
|
||||
if (dummyPosition.context.stage != GAME_NOTSTARTED || node != rootNode) {
|
||||
ai_ab.addNode(node, 0, location, bestMove, dummyPosition.context.turn);
|
||||
} else {
|
||||
// 若为先手,则抢占星位
|
||||
if (MillGame::isStarPoint(location)) {
|
||||
ai_ab.addNode(node, MillGameAi_ab::INF_VALUE, location, bestMove, gameTemp.context.turn);
|
||||
if (Position::isStarPoint(location)) {
|
||||
ai_ab.addNode(node, MillGameAi_ab::INF_VALUE, location, bestMove, dummyPosition.context.turn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,36 +98,36 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &gameTemp,
|
|||
}
|
||||
|
||||
// 对于移子阶段
|
||||
if (gameTemp.context.stage & GAME_MOVING) {
|
||||
if (dummyPosition.context.stage & GAME_MOVING) {
|
||||
int newLocation, oldLocation;
|
||||
|
||||
// 尽量走理论上较差的位置的棋子
|
||||
for (int i = MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) {
|
||||
oldLocation = movePriorityTable[i];
|
||||
|
||||
if (!gameTemp.choose(oldLocation)) {
|
||||
if (!dummyPosition.choose(oldLocation)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((gameTemp.context.turn == PLAYER1 &&
|
||||
(gameTemp.context.nPiecesOnBoard_1 > gameTemp.currentRule.nPiecesAtLeast || !gameTemp.currentRule.allowFlyWhenRemainThreePieces)) ||
|
||||
(gameTemp.context.turn == PLAYER2 &&
|
||||
(gameTemp.context.nPiecesOnBoard_2 > gameTemp.currentRule.nPiecesAtLeast || !gameTemp.currentRule.allowFlyWhenRemainThreePieces))) {
|
||||
if ((dummyPosition.context.turn == PLAYER1 &&
|
||||
(dummyPosition.context.nPiecesOnBoard_1 > dummyPosition.currentRule.nPiecesAtLeast || !dummyPosition.currentRule.allowFlyWhenRemainThreePieces)) ||
|
||||
(dummyPosition.context.turn == PLAYER2 &&
|
||||
(dummyPosition.context.nPiecesOnBoard_2 > dummyPosition.currentRule.nPiecesAtLeast || !dummyPosition.currentRule.allowFlyWhenRemainThreePieces))) {
|
||||
// 对于棋盘上还有3个子以上,或不允许飞子的情况,要求必须在着法表中
|
||||
for (int moveDirection = MOVE_DIRECTION_CLOCKWISE; moveDirection <= MOVE_DIRECTION_OUTWARD; moveDirection++) {
|
||||
// 对于原有位置,遍历四个方向的着法,如果棋盘上为空位就加到结点列表中
|
||||
newLocation = moveTable[oldLocation][moveDirection];
|
||||
if (newLocation && !gameTemp.board_[newLocation]) {
|
||||
if (newLocation && !dummyPosition.board_[newLocation]) {
|
||||
int move = (oldLocation << 8) + newLocation;
|
||||
ai_ab.addNode(node, 0, move, bestMove, gameTemp.context.turn); // (12%)
|
||||
ai_ab.addNode(node, 0, move, bestMove, dummyPosition.context.turn); // (12%)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 对于棋盘上还有不到3个字,但允许飞子的情况,不要求在着法表中,是空位就行
|
||||
for (newLocation = Board::LOCATION_BEGIN; newLocation < Board::LOCATION_END; newLocation++) {
|
||||
if (!gameTemp.board_[newLocation]) {
|
||||
if (!dummyPosition.board_[newLocation]) {
|
||||
int move = (oldLocation << 8) + newLocation;
|
||||
ai_ab.addNode(node, 0, move, bestMove, gameTemp.context.turn);
|
||||
ai_ab.addNode(node, 0, move, bestMove, dummyPosition.context.turn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -137,12 +137,12 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &gameTemp,
|
|||
|
||||
// 对于吃子动作
|
||||
case ACTION_CAPTURE:
|
||||
if (gameTemp.context.board.isAllInMills(opponent)) {
|
||||
if (dummyPosition.context.board.isAllInMills(opponent)) {
|
||||
// 全成三的情况
|
||||
for (int i = MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) {
|
||||
location = movePriorityTable[i];
|
||||
if (gameTemp.board_[location] & opponent) {
|
||||
ai_ab.addNode(node, 0, -location, bestMove, gameTemp.context.turn);
|
||||
if (dummyPosition.board_[location] & opponent) {
|
||||
ai_ab.addNode(node, 0, -location, bestMove, dummyPosition.context.turn);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -151,9 +151,9 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &gameTemp,
|
|||
// 不是全成三的情况
|
||||
for (int i = MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) {
|
||||
location = movePriorityTable[i];
|
||||
if (gameTemp.board_[location] & opponent) {
|
||||
if (gameTemp.getRule()->allowRemoveMill || !gameTemp.context.board.inHowManyMills(location)) {
|
||||
ai_ab.addNode(node, 0, -location, bestMove, gameTemp.context.turn);
|
||||
if (dummyPosition.board_[location] & opponent) {
|
||||
if (dummyPosition.getRule()->allowRemoveMill || !dummyPosition.context.board.inHowManyMills(location)) {
|
||||
ai_ab.addNode(node, 0, -location, bestMove, dummyPosition.context.turn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &gameTemp,
|
|||
}
|
||||
}
|
||||
|
||||
void MoveList::createMoveTable(MillGame &game)
|
||||
void MoveList::createMoveTable(Position &position)
|
||||
{
|
||||
#if 1
|
||||
const int moveTable_obliqueLine[Board::N_LOCATIONS][N_MOVE_DIRECTIONS] = {
|
||||
|
@ -356,7 +356,7 @@ void MoveList::createMoveTable(MillGame &game)
|
|||
};
|
||||
#endif
|
||||
|
||||
if (game.currentRule.hasObliqueLines) {
|
||||
if (position.currentRule.hasObliqueLines) {
|
||||
memcpy(moveTable, moveTable_obliqueLine, sizeof(moveTable));
|
||||
} else {
|
||||
memcpy(moveTable, moveTable_noObliqueLine, sizeof(moveTable));
|
||||
|
@ -379,14 +379,14 @@ void MoveList::createMoveTable(MillGame &game)
|
|||
#endif
|
||||
}
|
||||
|
||||
void MoveList::shuffleMovePriorityTable(MillGame & game)
|
||||
void MoveList::shuffleMovePriorityTable(Position &position)
|
||||
{
|
||||
array<int, 4> movePriorityTable0 = { 17, 19, 21, 23 }; // 中圈四个顶点 (星位)
|
||||
array<int, 8> movePriorityTable1 = { 25, 27, 29, 31, 9, 11, 13, 15 }; // 外圈和内圈四个顶点
|
||||
array<int, 4> movePriorityTable2 = { 16, 18, 20, 22 }; // 中圈十字架
|
||||
array<int, 8> movePriorityTable3 = { 24, 26, 28, 30, 8, 10, 12, 14 }; // 外内圈十字架
|
||||
|
||||
if (game.getRandomMove() == true) {
|
||||
if (position.getRandomMove() == true) {
|
||||
uint32_t seed = static_cast<uint32_t>(std::chrono::system_clock::now().time_since_epoch().count());
|
||||
|
||||
std::shuffle(movePriorityTable0.begin(), movePriorityTable0.end(), std::default_random_engine(seed));
|
||||
|
|
|
@ -34,15 +34,15 @@ public:
|
|||
MoveList &operator=(const MoveList &) = delete;
|
||||
|
||||
// 生成所有合法的着法并建立子节点
|
||||
static void generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &gameTemp,
|
||||
static void generateLegalMoves(MillGameAi_ab &ai_ab, Position &dummyPosition,
|
||||
MillGameAi_ab::Node *node, MillGameAi_ab::Node *rootNode,
|
||||
move_t bestMove);
|
||||
|
||||
// 生成着法表
|
||||
static void createMoveTable(MillGame &game);
|
||||
static void createMoveTable(Position &position);
|
||||
|
||||
// 随机打乱着法搜索顺序
|
||||
static void shuffleMovePriorityTable(MillGame &game);
|
||||
static void shuffleMovePriorityTable(Position &position);
|
||||
|
||||
// 着法表 // TODO: Move to private
|
||||
inline static int moveTable[Board::N_LOCATIONS][N_MOVE_DIRECTIONS] = { {0} };
|
||||
|
|
|
@ -57,7 +57,7 @@ depth_t MillGameAi_ab::changeDepth(depth_t originalDepth)
|
|||
{
|
||||
depth_t newDepth = originalDepth;
|
||||
|
||||
if ((gameTemp.context.stage) & (GAME_PLACING)) {
|
||||
if ((dummyPosition.context.stage) & (GAME_PLACING)) {
|
||||
#ifdef GAME_PLACING_DYNAMIC_DEPTH
|
||||
#ifdef DEAL_WITH_HORIZON_EFFECT
|
||||
#ifdef TRANSPOSITION_TABLE_ENABLE
|
||||
|
@ -77,7 +77,7 @@ depth_t MillGameAi_ab::changeDepth(depth_t originalDepth)
|
|||
depth_t depthTable[] = { 2, 13, 13, 13, 12, 11, 10, 9, 9, 8, 8, 7, 1 };
|
||||
#endif
|
||||
#endif // DEAL_WITH_HORIZON_EFFECT
|
||||
newDepth = depthTable[gameTemp.getPiecesInHandCount_1()];
|
||||
newDepth = depthTable[dummyPosition.getPiecesInHandCount_1()];
|
||||
#elif defined GAME_PLACING_FIXED_DEPTH
|
||||
newDepth = GAME_PLACING_FIXED_DEPTH;
|
||||
#endif // GAME_PLACING_DYNAMIC_DEPTH
|
||||
|
@ -85,7 +85,7 @@ depth_t MillGameAi_ab::changeDepth(depth_t originalDepth)
|
|||
|
||||
#ifdef GAME_MOVING_FIXED_DEPTH
|
||||
// 走棋阶段将深度调整
|
||||
if ((gameTemp.context.stage) & (GAME_MOVING)) {
|
||||
if ((dummyPosition.context.stage) & (GAME_MOVING)) {
|
||||
newDepth = GAME_MOVING_FIXED_DEPTH;
|
||||
}
|
||||
#endif /* GAME_MOVING_FIXED_DEPTH */
|
||||
|
@ -141,8 +141,8 @@ struct MillGameAi_ab::Node *MillGameAi_ab::addNode(
|
|||
|
||||
#ifdef DEBUG_AB_TREE
|
||||
newNode->root = rootNode;
|
||||
newNode->stage = gameTemp.context.stage;
|
||||
newNode->action = gameTemp.context.action;
|
||||
newNode->stage = dummyPosition.context.stage;
|
||||
newNode->action = dummyPosition.context.action;
|
||||
newNode->evaluated = false;
|
||||
newNode->nPiecesInHandDiff = INT_MAX;
|
||||
newNode->nPiecesOnBoardDiff = INT_MAX;
|
||||
|
@ -156,15 +156,15 @@ struct MillGameAi_ab::Node *MillGameAi_ab::addNode(
|
|||
char cmd[32] = { 0 };
|
||||
|
||||
if (move < 0) {
|
||||
gameTemp.context.board.locationToPolar(-move, r, s);
|
||||
dummyPosition.context.board.locationToPolar(-move, r, s);
|
||||
sprintf(cmd, "-(%1u,%1u)", r, s);
|
||||
} else if (move & 0x7f00) {
|
||||
int r1, s1;
|
||||
gameTemp.context.board.locationToPolar(move >> 8, r1, s1);
|
||||
gameTemp.context.board.locationToPolar(move & 0x00ff, r, s);
|
||||
dummyPosition.context.board.locationToPolar(move >> 8, r1, s1);
|
||||
dummyPosition.context.board.locationToPolar(move & 0x00ff, r, s);
|
||||
sprintf(cmd, "(%1u,%1u)->(%1u,%1u)", r1, s1, r, s);
|
||||
} else {
|
||||
gameTemp.context.board.locationToPolar(move & 0x007f, r, s);
|
||||
dummyPosition.context.board.locationToPolar(move & 0x007f, r, s);
|
||||
sprintf(cmd, "(%1u,%1u)", r, s);
|
||||
}
|
||||
|
||||
|
@ -176,7 +176,7 @@ struct MillGameAi_ab::Node *MillGameAi_ab::addNode(
|
|||
if (bestMove == 0 || move != bestMove) {
|
||||
#ifdef MILL_FIRST
|
||||
// 优先成三
|
||||
if (gameTemp.getStage() == GAME_PLACING && move > 0 && gameTemp.context.board.isInMills(move, true)) {
|
||||
if (dummyPosition.getStage() == GAME_PLACING && move > 0 && dummyPosition.context.board.isInMills(move, true)) {
|
||||
parent->children.insert(parent->children.begin(), newNode);
|
||||
} else {
|
||||
parent->children.push_back(newNode);
|
||||
|
@ -233,7 +233,7 @@ void MillGameAi_ab::sortLegalMoves(Node *node)
|
|||
{
|
||||
// 这个函数对效率的影响很大,排序好的话,剪枝较早,节省时间,但不能在此函数耗费太多时间
|
||||
|
||||
if (gameTemp.whosTurn() == PLAYER1) {
|
||||
if (dummyPosition.whosTurn() == PLAYER1) {
|
||||
std::stable_sort(node->children.begin(), node->children.end(), nodeGreater);
|
||||
} else {
|
||||
std::stable_sort(node->children.begin(), node->children.end(), nodeLess);
|
||||
|
@ -260,10 +260,10 @@ void MillGameAi_ab::deleteTree(Node *node)
|
|||
#endif
|
||||
}
|
||||
|
||||
void MillGameAi_ab::setGame(const MillGame &game)
|
||||
void MillGameAi_ab::setPosition(const Position &position)
|
||||
{
|
||||
// 如果规则改变,重建hashmap
|
||||
if (strcmp(this->game_.currentRule.name, game.currentRule.name) != 0) {
|
||||
if (strcmp(this->position_.currentRule.name, position.currentRule.name) != 0) {
|
||||
#ifdef TRANSPOSITION_TABLE_ENABLE
|
||||
TranspositionTable::clearTranspositionTable();
|
||||
#endif // TRANSPOSITION_TABLE_ENABLE
|
||||
|
@ -277,9 +277,9 @@ void MillGameAi_ab::setGame(const MillGame &game)
|
|||
positions.clear();
|
||||
}
|
||||
|
||||
this->game_ = game;
|
||||
gameTemp = game;
|
||||
gameContext = &(gameTemp.context);
|
||||
this->position_ = position;
|
||||
dummyPosition = position;
|
||||
positionContext = &(dummyPosition.context);
|
||||
requiredQuit = false;
|
||||
deleteTree(rootNode);
|
||||
#ifdef MEMORY_POOL
|
||||
|
@ -313,11 +313,11 @@ int MillGameAi_ab::alphaBetaPruning(depth_t depth)
|
|||
chrono::steady_clock::time_point timeEnd;
|
||||
|
||||
#ifdef BOOK_LEARNING
|
||||
if (game_.getStage() == GAME_PLACING)
|
||||
if (position_.getStage() == GAME_PLACING)
|
||||
{
|
||||
if (game_.context.nPiecesInHand_1 <= 10) {
|
||||
if (position_.context.nPiecesInHand_1 <= 10) {
|
||||
// 开局库只记录摆棋阶段最后的局面
|
||||
openingBook.push_back(game_.getHash());
|
||||
openingBook.push_back(position_.getHash());
|
||||
} else {
|
||||
// 暂时在此处清空开局库
|
||||
openingBook.clear();
|
||||
|
@ -328,8 +328,8 @@ int MillGameAi_ab::alphaBetaPruning(depth_t depth)
|
|||
#ifdef THREEFOLD_REPETITION
|
||||
static int nRepetition = 0;
|
||||
|
||||
if (game_.getStage() == GAME_MOVING) {
|
||||
hash_t hash = game_.getHash();
|
||||
if (position_.getStage() == GAME_MOVING) {
|
||||
hash_t hash = position_.getHash();
|
||||
|
||||
if (std::find(positions.begin(), positions.end(), hash) != positions.end()) {
|
||||
nRepetition++;
|
||||
|
@ -342,13 +342,13 @@ int MillGameAi_ab::alphaBetaPruning(depth_t depth)
|
|||
}
|
||||
}
|
||||
|
||||
if (game_.getStage() == GAME_PLACING) {
|
||||
if (position_.getStage() == GAME_PLACING) {
|
||||
positions.clear();
|
||||
}
|
||||
#endif // THREEFOLD_REPETITION
|
||||
|
||||
// 随机打乱着法顺序
|
||||
MoveList::shuffleMovePriorityTable(game_);
|
||||
MoveList::shuffleMovePriorityTable(position_);
|
||||
|
||||
#ifdef IDS_SUPPORT
|
||||
// 深化迭代
|
||||
|
@ -404,7 +404,7 @@ value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t alpha, value_t be
|
|||
enum TranspositionTable::HashType hashf = TranspositionTable::hashfALPHA;
|
||||
|
||||
// 获取哈希值
|
||||
hash_t hash = gameTemp.getHash();
|
||||
hash_t hash = dummyPosition.getHash();
|
||||
#ifdef DEBUG_AB_TREE
|
||||
node->hash = hash;
|
||||
#endif
|
||||
|
@ -432,7 +432,7 @@ value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t alpha, value_t be
|
|||
|
||||
#if 0
|
||||
// TODO: 有必要针对深度微调 value?
|
||||
if (gameContext->turn == PLAYER1)
|
||||
if (positionContext->turn == PLAYER1)
|
||||
node->value += hashValue.depth - depth;
|
||||
else
|
||||
node->value -= hashValue.depth - depth;
|
||||
|
@ -447,7 +447,7 @@ value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t alpha, value_t be
|
|||
#ifdef DEBUG_AB_TREE
|
||||
node->depth = depth;
|
||||
node->root = rootNode;
|
||||
// node->player = gameContext->turn;
|
||||
// node->player = positionContext->turn;
|
||||
// 初始化
|
||||
node->isLeaf = false;
|
||||
node->isTimeout = false;
|
||||
|
@ -459,9 +459,9 @@ value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t alpha, value_t be
|
|||
#endif // DEBUG_AB_TREE
|
||||
|
||||
// 搜索到叶子节点(决胜局面) // TODO: 对哈希进行特殊处理
|
||||
if (gameContext->stage == GAME_OVER) {
|
||||
if (positionContext->stage == GAME_OVER) {
|
||||
// 局面评估
|
||||
node->value = Evaluation::getValue(gameTemp, gameContext, node);
|
||||
node->value = Evaluation::getValue(dummyPosition, positionContext, node);
|
||||
evaluatedNodeCount++;
|
||||
|
||||
// 为争取速胜,value 值 +- 深度
|
||||
|
@ -486,11 +486,11 @@ value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t alpha, value_t be
|
|||
// 搜索到第0层或需要退出
|
||||
if (!depth || requiredQuit) {
|
||||
// 局面评估
|
||||
node->value = Evaluation::getValue(gameTemp, gameContext, node);
|
||||
node->value = Evaluation::getValue(dummyPosition, positionContext, node);
|
||||
evaluatedNodeCount++;
|
||||
|
||||
// 为争取速胜,value 值 +- 深度 (有必要?)
|
||||
if (gameContext->turn == PLAYER1) {
|
||||
if (positionContext->turn == PLAYER1) {
|
||||
node->value += depth;
|
||||
} else {
|
||||
node->value -= depth;
|
||||
|
@ -504,8 +504,8 @@ value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t alpha, value_t be
|
|||
|
||||
#ifdef BOOK_LEARNING
|
||||
// 检索开局库
|
||||
if (gameContext->stage == GAME_PLACING && findBookHash(hash, hashValue)) {
|
||||
if (gameContext->turn == PLAYER2) {
|
||||
if (positionContext->stage == GAME_PLACING && findBookHash(hash, hashValue)) {
|
||||
if (positionContext->turn == PLAYER2) {
|
||||
// 是否需对后手扣分 // TODO: 先后手都处理
|
||||
node->value += 1;
|
||||
}
|
||||
|
@ -521,18 +521,18 @@ value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t alpha, value_t be
|
|||
}
|
||||
|
||||
// 生成子节点树,即生成每个合理的着法
|
||||
MoveList::generateLegalMoves(*this, gameTemp, node, rootNode, bestMove);
|
||||
MoveList::generateLegalMoves(*this, dummyPosition, node, rootNode, bestMove);
|
||||
|
||||
// 根据演算模型执行 MiniMax 检索,对先手,搜索 Max, 对后手,搜索 Min
|
||||
|
||||
minMax = gameTemp.whosTurn() == PLAYER1 ? -INF_VALUE : INF_VALUE;
|
||||
minMax = dummyPosition.whosTurn() == PLAYER1 ? -INF_VALUE : INF_VALUE;
|
||||
|
||||
for (auto child : node->children) {
|
||||
// 上下文入栈保存,以便后续撤销着法
|
||||
contextStack.push(gameTemp.context);
|
||||
contextStack.push(dummyPosition.context);
|
||||
|
||||
// 执行着法
|
||||
gameTemp.command(child->move);
|
||||
dummyPosition.command(child->move);
|
||||
|
||||
#ifdef DEAL_WITH_HORIZON_EFFECT
|
||||
// 克服“水平线效应”: 若遇到吃子,则搜索深度增加
|
||||
|
@ -553,10 +553,10 @@ value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t alpha, value_t be
|
|||
value = alphaBetaPruning(depth - 1 + epsilon, alpha, beta, child);
|
||||
|
||||
// 上下文弹出栈,撤销着法
|
||||
gameTemp.context = contextStack.top();
|
||||
dummyPosition.context = contextStack.top();
|
||||
contextStack.pop();
|
||||
|
||||
if (gameTemp.whosTurn() == PLAYER1) {
|
||||
if (dummyPosition.whosTurn() == PLAYER1) {
|
||||
// 为走棋一方的层, 局面对走棋的一方来说是以 α 为评价
|
||||
|
||||
// 取最大值
|
||||
|
@ -686,10 +686,10 @@ const char* MillGameAi_ab::bestMove()
|
|||
|
||||
// 检查是否必败
|
||||
|
||||
if (game_.getGiveUpIfMostLose() == true) {
|
||||
if (position_.getGiveUpIfMostLose() == true) {
|
||||
bool isMostLose = true; // 是否必败
|
||||
|
||||
Player whosTurn = game_.whosTurn();
|
||||
Player whosTurn = position_.whosTurn();
|
||||
|
||||
for (auto child : rootNode->children) {
|
||||
// TODO: 使用常量代替
|
||||
|
@ -750,15 +750,15 @@ const char *MillGameAi_ab::move2string(move_t move)
|
|||
int r, s;
|
||||
|
||||
if (move < 0) {
|
||||
gameTemp.context.board.locationToPolar(-move, r, s);
|
||||
dummyPosition.context.board.locationToPolar(-move, r, s);
|
||||
sprintf(cmdline, "-(%1u,%1u)", r, s);
|
||||
} else if (move & 0x7f00) {
|
||||
int r1, s1;
|
||||
gameTemp.context.board.locationToPolar(move >> 8, r1, s1);
|
||||
gameTemp.context.board.locationToPolar(move & 0x00ff, r, s);
|
||||
dummyPosition.context.board.locationToPolar(move >> 8, r1, s1);
|
||||
dummyPosition.context.board.locationToPolar(move & 0x00ff, r, s);
|
||||
sprintf(cmdline, "(%1u,%1u)->(%1u,%1u)", r1, s1, r, s);
|
||||
} else {
|
||||
gameTemp.context.board.locationToPolar(move & 0x007f, r, s);
|
||||
dummyPosition.context.board.locationToPolar(move & 0x007f, r, s);
|
||||
sprintf(cmdline, "(%1u,%1u)", r, s);
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ public:
|
|||
bool isTimeout; // 是否遍历到此结点时因为超时而被迫退出
|
||||
bool isLeaf; // 是否为叶子结点, 叶子结点是决胜局面
|
||||
bool visited; // 是否在遍历时访问过
|
||||
GameStage stage; // 摆棋阶段还是走棋阶段
|
||||
PositionStage stage; // 摆棋阶段还是走棋阶段
|
||||
Action action; // 动作状态
|
||||
int nPiecesOnBoardDiff; // 场上棋子个数和对手的差值
|
||||
int nPiecesInHandDiff; // 手中的棋子个数和对手的差值
|
||||
|
@ -97,7 +97,7 @@ public:
|
|||
MillGameAi_ab();
|
||||
~MillGameAi_ab();
|
||||
|
||||
void setGame(const MillGame &game);
|
||||
void setPosition(const Position &position);
|
||||
|
||||
void quit()
|
||||
{
|
||||
|
@ -185,15 +185,15 @@ protected:
|
|||
|
||||
private:
|
||||
// 原始模型
|
||||
MillGame game_;
|
||||
Position position_;
|
||||
|
||||
// 演算用的模型
|
||||
MillGame gameTemp;
|
||||
Position dummyPosition;
|
||||
|
||||
GameContext *gameContext {};
|
||||
PositionContext *positionContext {};
|
||||
|
||||
// hash 计算时,各种转换用的模型
|
||||
MillGame gameTempShift;
|
||||
Position dummyPositionShift;
|
||||
|
||||
// 根节点
|
||||
Node *rootNode {nullptr};
|
||||
|
@ -218,9 +218,9 @@ private:
|
|||
|
||||
// 局面数据栈
|
||||
//#ifdef MEMORY_POOL
|
||||
// StackAlloc<MillGame::GameContext, MemoryPool<MillGame::GameContext> > contextStack;
|
||||
// StackAlloc<MillGame::PositionContext, MemoryPool<MillGame::PositionContext> > contextStack;
|
||||
//#else
|
||||
stack<GameContext> contextStack;
|
||||
stack<PositionContext> contextStack;
|
||||
//#endif
|
||||
|
||||
// 标识,用于跳出剪枝算法,立即返回
|
||||
|
|
|
@ -50,17 +50,17 @@ bool TranspositionTable::findHash(hash_t hash, TranspositionTable::HashValue &ha
|
|||
return iter;
|
||||
|
||||
// 变换局面,查找 hash (废弃)
|
||||
gameTempShift = gameTemp;
|
||||
dummyPositionShift = dummyPosition;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (i)
|
||||
gameTempShift.mirror(false);
|
||||
dummyPositionShift.mirror(false);
|
||||
|
||||
for (int j = 0; j < 2; j++) {
|
||||
if (j)
|
||||
gameTempShift.turn(false);
|
||||
dummyPositionShift.turn(false);
|
||||
for (int k = 0; k < 4; k++) {
|
||||
gameTempShift.rotate(k * 90, false);
|
||||
iter = hashmap.find(gameTempShift.getHash());
|
||||
dummyPositionShift.rotate(k * 90, false);
|
||||
iter = hashmap.find(dummyPositionShift.getHash());
|
||||
if (iter != hashmap.end())
|
||||
return iter;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
AiThread::AiThread(int id, QObject *parent) :
|
||||
QThread(parent),
|
||||
waiting_(false),
|
||||
game_(nullptr),
|
||||
position_(nullptr),
|
||||
aiDepth(2),
|
||||
aiTime(3600)
|
||||
{
|
||||
|
@ -60,12 +60,12 @@ AiThread::~AiThread()
|
|||
wait();
|
||||
}
|
||||
|
||||
void AiThread::setAi(const MillGame &game)
|
||||
void AiThread::setAi(const Position &position)
|
||||
{
|
||||
mutex.lock();
|
||||
|
||||
this->game_ = &game;
|
||||
ai_ab.setGame(*(this->game_));
|
||||
this->position_ = &position;
|
||||
ai_ab.setPosition(*(this->position_));
|
||||
|
||||
#ifdef TRANSPOSITION_TABLE_ENABLE
|
||||
// 新下一盘前清除哈希表 (注意可能同时存在每步之前清除)
|
||||
|
@ -77,11 +77,11 @@ void AiThread::setAi(const MillGame &game)
|
|||
mutex.unlock();
|
||||
}
|
||||
|
||||
void AiThread::setAi(const MillGame &game, depth_t depth, int time)
|
||||
void AiThread::setAi(const Position &position, depth_t depth, int time)
|
||||
{
|
||||
mutex.lock();
|
||||
this->game_ = &game;
|
||||
ai_ab.setGame(game);
|
||||
this->position_ = &position;
|
||||
ai_ab.setPosition(position);
|
||||
aiDepth = depth;
|
||||
aiTime = time;
|
||||
mutex.unlock();
|
||||
|
@ -107,7 +107,7 @@ void AiThread::run()
|
|||
while (!isInterruptionRequested()) {
|
||||
mutex.lock();
|
||||
|
||||
i = MillGame::playerToId(game_->whosTurn());
|
||||
i = Position::playerToId(position_->whosTurn());
|
||||
|
||||
if (i != id || waiting_) {
|
||||
pauseCondition.wait(&mutex);
|
||||
|
@ -115,7 +115,7 @@ void AiThread::run()
|
|||
continue;
|
||||
}
|
||||
|
||||
ai_ab.setGame(*game_);
|
||||
ai_ab.setPosition(*position_);
|
||||
emit calcStarted();
|
||||
mutex.unlock();
|
||||
|
||||
|
|
|
@ -54,8 +54,8 @@ protected:
|
|||
|
||||
public:
|
||||
// AI设置
|
||||
void setAi(const MillGame &game);
|
||||
void setAi(const MillGame &game, depth_t depth, int time);
|
||||
void setAi(const Position &position);
|
||||
void setAi(const Position &position, depth_t depth, int time);
|
||||
|
||||
Server *getServer()
|
||||
{
|
||||
|
@ -107,7 +107,7 @@ private:
|
|||
QWaitCondition pauseCondition;
|
||||
|
||||
// 主线程棋对象的引用
|
||||
const MillGame *game_;
|
||||
const Position *position_;
|
||||
|
||||
// Alpha-Beta剪枝算法类
|
||||
MillGameAi_ab ai_ab;
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include "search.h"
|
||||
#include "movegen.h"
|
||||
|
||||
MillGame::MillGame()
|
||||
Position::Position()
|
||||
{
|
||||
// 单独提出 board 等数据,免得每次都写 context.board;
|
||||
board_ = context.board.locations;
|
||||
|
@ -44,40 +44,40 @@ MillGame::MillGame()
|
|||
score_1 = score_2 = score_draw = 0;
|
||||
}
|
||||
|
||||
MillGame::~MillGame() = default;
|
||||
Position::~Position() = default;
|
||||
|
||||
MillGame::MillGame(const MillGame &game)
|
||||
Position::Position(const Position &position)
|
||||
{
|
||||
*this = game;
|
||||
*this = position;
|
||||
}
|
||||
|
||||
MillGame &MillGame::operator= (const MillGame &game)
|
||||
Position &Position::operator= (const Position &position)
|
||||
{
|
||||
if (this == &game)
|
||||
if (this == &position)
|
||||
return *this;
|
||||
|
||||
currentRule = game.currentRule;
|
||||
context = game.context;
|
||||
currentStep = game.currentStep;
|
||||
moveStep = game.moveStep;
|
||||
randomMove_ = game.randomMove_;
|
||||
giveUpIfMostLose_ = game.giveUpIfMostLose_;
|
||||
currentRule = position.currentRule;
|
||||
context = position.context;
|
||||
currentStep = position.currentStep;
|
||||
moveStep = position.moveStep;
|
||||
randomMove_ = position.randomMove_;
|
||||
giveUpIfMostLose_ = position.giveUpIfMostLose_;
|
||||
board_ = context.board.locations;
|
||||
currentLocation = game.currentLocation;
|
||||
winner = game.winner;
|
||||
startTime = game.startTime;
|
||||
currentTime = game.currentTime;
|
||||
elapsedSeconds_1 = game.elapsedSeconds_1;
|
||||
elapsedSeconds_2 = game.elapsedSeconds_2;
|
||||
move_ = game.move_;
|
||||
memcpy(cmdline, game.cmdline, sizeof(cmdline));
|
||||
cmdlist = game.cmdlist;
|
||||
tips = game.tips;
|
||||
currentLocation = position.currentLocation;
|
||||
winner = position.winner;
|
||||
startTime = position.startTime;
|
||||
currentTime = position.currentTime;
|
||||
elapsedSeconds_1 = position.elapsedSeconds_1;
|
||||
elapsedSeconds_2 = position.elapsedSeconds_2;
|
||||
move_ = position.move_;
|
||||
memcpy(cmdline, position.cmdline, sizeof(cmdline));
|
||||
cmdlist = position.cmdlist;
|
||||
tips = position.tips;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
int MillGame::playerToId(enum Player player)
|
||||
int Position::playerToId(enum Player player)
|
||||
{
|
||||
if (player == PLAYER1)
|
||||
return 1;
|
||||
|
@ -87,7 +87,7 @@ int MillGame::playerToId(enum Player player)
|
|||
return 0;
|
||||
}
|
||||
|
||||
Player MillGame::getOpponent(Player player)
|
||||
Player Position::getOpponent(Player player)
|
||||
{
|
||||
switch (player)
|
||||
{
|
||||
|
@ -103,7 +103,7 @@ Player MillGame::getOpponent(Player player)
|
|||
}
|
||||
|
||||
// 设置配置
|
||||
bool MillGame::configure(bool giveUpIfMostLose, bool randomMove)
|
||||
bool Position::configure(bool giveUpIfMostLose, bool randomMove)
|
||||
{
|
||||
// 设置是否必败时认输
|
||||
this->giveUpIfMostLose_ = giveUpIfMostLose;
|
||||
|
@ -115,7 +115,7 @@ bool MillGame::configure(bool giveUpIfMostLose, bool randomMove)
|
|||
}
|
||||
|
||||
// 设置棋局状态和棋盘数据,用于初始化
|
||||
bool MillGame::setContext(const struct Rule *rule, step_t maxStepsLedToDraw, int maxTimeLedToLose,
|
||||
bool Position::setContext(const struct Rule *rule, step_t maxStepsLedToDraw, int maxTimeLedToLose,
|
||||
step_t initialStep, int flags, const char *board,
|
||||
int nPiecesInHand_1, int nPiecesInHand_2, int nPiecesNeedRemove)
|
||||
{
|
||||
|
@ -266,7 +266,7 @@ bool MillGame::setContext(const struct Rule *rule, step_t maxStepsLedToDraw, int
|
|||
return false;
|
||||
}
|
||||
|
||||
void MillGame::getContext(struct Rule &rule, step_t &step, int &flags,
|
||||
void Position::getContext(struct Rule &rule, step_t &step, int &flags,
|
||||
int *&board, int &nPiecesInHand_1, int &nPiecesInHand_2, int &nPiecesNeedRemove)
|
||||
{
|
||||
rule = this->currentRule;
|
||||
|
@ -278,7 +278,7 @@ void MillGame::getContext(struct Rule &rule, step_t &step, int &flags,
|
|||
nPiecesNeedRemove = context.nPiecesNeedRemove;
|
||||
}
|
||||
|
||||
bool MillGame::reset()
|
||||
bool Position::reset()
|
||||
{
|
||||
if (context.stage == GAME_NOTSTARTED &&
|
||||
elapsedSeconds_1 == elapsedSeconds_2 == 0) {
|
||||
|
@ -351,7 +351,7 @@ bool MillGame::reset()
|
|||
return false;
|
||||
}
|
||||
|
||||
bool MillGame::start()
|
||||
bool Position::start()
|
||||
{
|
||||
switch (context.stage) {
|
||||
// 如果游戏已经开始,则返回false
|
||||
|
@ -374,7 +374,7 @@ bool MillGame::start()
|
|||
}
|
||||
}
|
||||
|
||||
bool MillGame::place(int location, int time_p, int8_t rs)
|
||||
bool Position::place(int location, int time_p, int8_t rs)
|
||||
{
|
||||
// 如果局面为“结局”,返回false
|
||||
if (context.stage == GAME_OVER)
|
||||
|
@ -556,7 +556,7 @@ out:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool MillGame::_place(int r, int s, int time_p)
|
||||
bool Position::_place(int r, int s, int time_p)
|
||||
{
|
||||
// 转换为 location
|
||||
int location = context.board.polarToLocation(r, s);
|
||||
|
@ -564,7 +564,7 @@ bool MillGame::_place(int r, int s, int time_p)
|
|||
return place(location, time_p, true);
|
||||
}
|
||||
|
||||
bool MillGame::_capture(int r, int s, int time_p)
|
||||
bool Position::_capture(int r, int s, int time_p)
|
||||
{
|
||||
// 转换为 location
|
||||
int location = context.board.polarToLocation(r, s);
|
||||
|
@ -572,7 +572,7 @@ bool MillGame::_capture(int r, int s, int time_p)
|
|||
return capture(location, time_p, 1);
|
||||
}
|
||||
|
||||
bool MillGame::capture(int location, int time_p, int8_t cp)
|
||||
bool Position::capture(int location, int time_p, int8_t cp)
|
||||
{
|
||||
// 如果局面为"未开局"或“结局”,返回false
|
||||
if (context.stage == GAME_NOTSTARTED || context.stage == GAME_OVER)
|
||||
|
@ -714,7 +714,7 @@ out:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool MillGame::choose(int location)
|
||||
bool Position::choose(int location)
|
||||
{
|
||||
// 如果局面不是"中局”,返回false
|
||||
if (context.stage != GAME_MOVING)
|
||||
|
@ -745,12 +745,12 @@ bool MillGame::choose(int location)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool MillGame::choose(int r, int s)
|
||||
bool Position::choose(int r, int s)
|
||||
{
|
||||
return choose(context.board.polarToLocation(r, s));
|
||||
}
|
||||
|
||||
bool MillGame::giveup(Player loser)
|
||||
bool Position::giveup(Player loser)
|
||||
{
|
||||
if (context.stage == GAME_NOTSTARTED ||
|
||||
context.stage == GAME_OVER ||
|
||||
|
@ -778,7 +778,7 @@ bool MillGame::giveup(Player loser)
|
|||
}
|
||||
|
||||
// 打算用个C++的命令行解析库的,简单的没必要,但中文编码有极小概率出问题
|
||||
bool MillGame::command(const char *cmd)
|
||||
bool Position::command(const char *cmd)
|
||||
{
|
||||
int r, t;
|
||||
step_t s;
|
||||
|
@ -863,7 +863,7 @@ bool MillGame::command(const char *cmd)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool MillGame::command(int move)
|
||||
bool Position::command(int move)
|
||||
{
|
||||
if (move < 0) {
|
||||
return capture(-move);
|
||||
|
@ -880,7 +880,7 @@ bool MillGame::command(int move)
|
|||
return false;
|
||||
}
|
||||
|
||||
inline int MillGame::update(int time_p /*= -1*/)
|
||||
inline int Position::update(int time_p /*= -1*/)
|
||||
{
|
||||
int ret = -1;
|
||||
time_t *player_ms = (context.turn == PLAYER1 ? &elapsedSeconds_1 : &elapsedSeconds_2);
|
||||
|
@ -911,13 +911,13 @@ inline int MillGame::update(int time_p /*= -1*/)
|
|||
}
|
||||
|
||||
// 是否分出胜负
|
||||
bool MillGame::win()
|
||||
bool Position::win()
|
||||
{
|
||||
return win(false);
|
||||
}
|
||||
|
||||
// 是否分出胜负
|
||||
bool MillGame::win(bool forceDraw)
|
||||
bool Position::win(bool forceDraw)
|
||||
{
|
||||
if (context.stage == GAME_OVER) {
|
||||
return true;
|
||||
|
@ -1043,7 +1043,7 @@ bool MillGame::win(bool forceDraw)
|
|||
}
|
||||
|
||||
// 计算玩家1和玩家2的棋子活动能力之差
|
||||
int MillGame::getMobilityDiff(enum Player turn, const Rule &rule, int nPiecesOnBoard_1, int nPiecesOnBoard_2, bool includeFobidden)
|
||||
int Position::getMobilityDiff(enum Player turn, const Rule &rule, int nPiecesOnBoard_1, int nPiecesOnBoard_2, bool includeFobidden)
|
||||
{
|
||||
int *board = context.board.locations;
|
||||
int mobility1 = 0;
|
||||
|
@ -1066,7 +1066,7 @@ int MillGame::getMobilityDiff(enum Player turn, const Rule &rule, int nPiecesOnB
|
|||
return diff;
|
||||
}
|
||||
|
||||
void MillGame::cleanForbiddenPoints()
|
||||
void Position::cleanForbiddenPoints()
|
||||
{
|
||||
int location = 0;
|
||||
|
||||
|
@ -1082,7 +1082,7 @@ void MillGame::cleanForbiddenPoints()
|
|||
}
|
||||
}
|
||||
|
||||
enum Player MillGame::changeTurn()
|
||||
enum Player Position::changeTurn()
|
||||
{
|
||||
// 设置轮到谁走
|
||||
context.turn = (context.turn == PLAYER1) ? PLAYER2 : PLAYER1;
|
||||
|
@ -1090,7 +1090,7 @@ enum Player MillGame::changeTurn()
|
|||
return context.turn;
|
||||
}
|
||||
|
||||
void MillGame::setTips()
|
||||
void Position::setTips()
|
||||
{
|
||||
switch (context.stage) {
|
||||
case GAME_NOTSTARTED:
|
||||
|
@ -1156,7 +1156,7 @@ void MillGame::setTips()
|
|||
}
|
||||
}
|
||||
|
||||
void MillGame::getElapsedTime(time_t &p1_ms, time_t &p2_ms)
|
||||
void Position::getElapsedTime(time_t &p1_ms, time_t &p2_ms)
|
||||
{
|
||||
update();
|
||||
|
||||
|
@ -1174,7 +1174,7 @@ void MillGame::getElapsedTime(time_t &p1_ms, time_t &p2_ms)
|
|||
* 0位:轮流标识,0为先手,1为后手
|
||||
*/
|
||||
|
||||
void MillGame::constructHash()
|
||||
void Position::constructHash()
|
||||
{
|
||||
context.hash = 0;
|
||||
|
||||
|
@ -1182,7 +1182,7 @@ void MillGame::constructHash()
|
|||
memcpy(context.zobrist, zobrist0, sizeof(hash_t) * Board::N_LOCATIONS * POINT_TYPE_COUNT);
|
||||
}
|
||||
|
||||
hash_t MillGame::getHash()
|
||||
hash_t Position::getHash()
|
||||
{
|
||||
// TODO: 每次获取哈希值时更新 hash 值低8位,放在此处调用不优雅
|
||||
updateHashMisc();
|
||||
|
@ -1190,7 +1190,7 @@ hash_t MillGame::getHash()
|
|||
return context.hash;
|
||||
}
|
||||
|
||||
hash_t MillGame::updateHash(int location)
|
||||
hash_t Position::updateHash(int location)
|
||||
{
|
||||
// PieceType is board_[location]
|
||||
|
||||
|
@ -1203,12 +1203,12 @@ hash_t MillGame::updateHash(int location)
|
|||
return context.hash;
|
||||
}
|
||||
|
||||
hash_t MillGame::revertHash(int location)
|
||||
hash_t Position::revertHash(int location)
|
||||
{
|
||||
return updateHash(location);
|
||||
}
|
||||
|
||||
hash_t MillGame::updateHashMisc()
|
||||
hash_t Position::updateHashMisc()
|
||||
{
|
||||
// 清除标记位
|
||||
context.hash &= static_cast<hash_t>(~0xFF);
|
||||
|
@ -1224,7 +1224,7 @@ hash_t MillGame::updateHashMisc()
|
|||
}
|
||||
|
||||
context.hash |= static_cast<hash_t>(context.nPiecesNeedRemove) << 2;
|
||||
context.hash |= static_cast<hash_t>(context.nPiecesInHand_1) << 4; // TODO: 或许换 game.stage 也可以?
|
||||
context.hash |= static_cast<hash_t>(context.nPiecesInHand_1) << 4; // TODO: 或许换 position.stage 也可以?
|
||||
|
||||
return context.hash;
|
||||
}
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MILLGAME_H
|
||||
#define MILLGAME_H
|
||||
#ifndef POSITION_H
|
||||
#define POSITION_H
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
@ -36,7 +36,7 @@ using namespace std;
|
|||
|
||||
// 棋局结构体,算法相关,包含当前棋盘数据
|
||||
// 单独分离出来供AI判断局面用,生成置换表时使用
|
||||
class GameContext
|
||||
class PositionContext
|
||||
{
|
||||
public:
|
||||
Board board;
|
||||
|
@ -48,7 +48,7 @@ public:
|
|||
hash_t zobrist[Board::N_LOCATIONS][POINT_TYPE_COUNT]{};
|
||||
|
||||
// 局面阶段标识
|
||||
enum GameStage stage;
|
||||
enum PositionStage stage;
|
||||
|
||||
// 轮流状态标识
|
||||
enum Player turn;
|
||||
|
@ -75,9 +75,9 @@ public:
|
|||
};
|
||||
|
||||
// 棋类(在数据模型内,玩家只分先后手,不分黑白)
|
||||
// 注意:MillGame类不是线程安全的!
|
||||
// 所以不能跨线程修改MillGame类的静态成员变量,切记!
|
||||
class MillGame
|
||||
// 注意:Position 类不是线程安全的!
|
||||
// 所以不能跨线程修改 Position 类的静态成员变量,切记!
|
||||
class Position
|
||||
{
|
||||
// AI友元类
|
||||
friend class MillGameAi_ab;
|
||||
|
@ -111,14 +111,14 @@ private:
|
|||
void constructHash();
|
||||
|
||||
public:
|
||||
explicit MillGame();
|
||||
virtual ~MillGame();
|
||||
explicit Position();
|
||||
virtual ~Position();
|
||||
|
||||
// 拷贝构造函数
|
||||
explicit MillGame(const MillGame &);
|
||||
explicit Position(const Position &);
|
||||
|
||||
// 运算符重载
|
||||
MillGame &operator=(const MillGame &);
|
||||
Position &operator=(const Position &);
|
||||
|
||||
// 设置配置
|
||||
bool configure(bool giveUpIfMostLose, bool randomMove);
|
||||
|
@ -188,7 +188,7 @@ public:
|
|||
}
|
||||
|
||||
// 获取局面阶段标识
|
||||
enum GameStage getStage() const
|
||||
enum PositionStage getStage() const
|
||||
{
|
||||
return context.stage;
|
||||
}
|
||||
|
@ -328,7 +328,7 @@ public:
|
|||
|
||||
public: /* TODO: move to private */
|
||||
// 棋局上下文
|
||||
GameContext context;
|
||||
PositionContext context;
|
||||
|
||||
// 当前使用的规则
|
||||
struct Rule currentRule
|
||||
|
@ -404,4 +404,4 @@ private:
|
|||
string tips;
|
||||
};
|
||||
|
||||
#endif /* MILLGAME_H */
|
||||
#endif /* POSITION_H */
|
||||
|
|
|
@ -66,7 +66,7 @@ enum Player : uint8_t
|
|||
};
|
||||
|
||||
// 局面阶段标识
|
||||
enum GameStage : uint16_t
|
||||
enum PositionStage : uint16_t
|
||||
{
|
||||
GAME_NONE = 0x0000,
|
||||
GAME_NOTSTARTED = 0x0001, // 未开局
|
||||
|
|
|
@ -117,9 +117,9 @@ const QMap<int, QStringList> GameController::getActions()
|
|||
|
||||
void GameController::gameStart()
|
||||
{
|
||||
game_.configure(giveUpIfMostLose_, randomMove_);
|
||||
game_.start();
|
||||
gameTemp = game_;
|
||||
position_.configure(giveUpIfMostLose_, randomMove_);
|
||||
position_.start();
|
||||
dummyPosition = position_;
|
||||
|
||||
// 每隔100毫秒调用一次定时器处理函数
|
||||
if (timeID == 0) {
|
||||
|
@ -137,15 +137,15 @@ void GameController::gameReset()
|
|||
timeID = 0;
|
||||
|
||||
// 棋未下完,则算对手得分
|
||||
if (game_.getStage() == GAME_MOVING &&
|
||||
game_.whoWin() == PLAYER_NOBODY) {
|
||||
if (position_.getStage() == GAME_MOVING &&
|
||||
position_.whoWin() == PLAYER_NOBODY) {
|
||||
giveUp();
|
||||
}
|
||||
|
||||
// 重置游戏
|
||||
game_.configure(giveUpIfMostLose_, randomMove_);
|
||||
game_.reset();
|
||||
gameTemp = game_;
|
||||
position_.configure(giveUpIfMostLose_, randomMove_);
|
||||
position_.reset();
|
||||
dummyPosition = position_;
|
||||
|
||||
// 停掉线程
|
||||
if (!isAutoRestart) {
|
||||
|
@ -161,7 +161,7 @@ void GameController::gameReset()
|
|||
currentPiece = nullptr;
|
||||
|
||||
// 重新绘制棋盘
|
||||
scene.setDiagonal(game_.getRule()->hasObliqueLines);
|
||||
scene.setDiagonal(position_.getRule()->hasObliqueLines);
|
||||
|
||||
// 绘制所有棋子,放在起始位置
|
||||
// 0: 先手第1子; 1:后手第1子
|
||||
|
@ -170,7 +170,7 @@ void GameController::gameReset()
|
|||
PieceItem::Models md;
|
||||
PieceItem *newP;
|
||||
|
||||
for (int i = 0; i < game_.getRule()->nTotalPiecesEachSide; i++) {
|
||||
for (int i = 0; i < position_.getRule()->nTotalPiecesEachSide; i++) {
|
||||
// 先手的棋子
|
||||
md = isInverted ? PieceItem::whitePiece : PieceItem::blackPiece;
|
||||
newP = new PieceItem;
|
||||
|
@ -179,7 +179,7 @@ void GameController::gameReset()
|
|||
newP->setNum(i + 1);
|
||||
|
||||
// 如果重复三连不可用,则显示棋子序号,九连棋专用玩法
|
||||
if (!(game_.getRule()->allowRemovePiecesRepeatedly))
|
||||
if (!(position_.getRule()->allowRemovePiecesRepeatedly))
|
||||
newP->setShowNum(true);
|
||||
|
||||
pieceList.append(newP);
|
||||
|
@ -193,7 +193,7 @@ void GameController::gameReset()
|
|||
newP->setNum(i + 1);
|
||||
|
||||
// 如果重复三连不可用,则显示棋子序号,九连棋专用玩法
|
||||
if (!(game_.getRule()->allowRemovePiecesRepeatedly))
|
||||
if (!(position_.getRule()->allowRemovePiecesRepeatedly))
|
||||
newP->setShowNum(true);
|
||||
|
||||
pieceList.append(newP);
|
||||
|
@ -201,7 +201,7 @@ void GameController::gameReset()
|
|||
}
|
||||
|
||||
// 读取规则限时要求
|
||||
timeLimit = game_.getRule()->maxTimeLedToLose;
|
||||
timeLimit = position_.getRule()->maxTimeLedToLose;
|
||||
|
||||
// 如果规则不要求计时,则time1和time2表示已用时间
|
||||
if (timeLimit <= 0) {
|
||||
|
@ -215,7 +215,7 @@ void GameController::gameReset()
|
|||
// 更新棋谱
|
||||
manualListModel.removeRows(0, manualListModel.rowCount());
|
||||
manualListModel.insertRow(0);
|
||||
manualListModel.setData(manualListModel.index(0), game_.getCmdLine());
|
||||
manualListModel.setData(manualListModel.index(0), position_.getCmdLine());
|
||||
currentRow = 0;
|
||||
|
||||
// 发出信号通知主窗口更新LCD显示
|
||||
|
@ -224,13 +224,13 @@ void GameController::gameReset()
|
|||
emit time2Changed(qtime.toString("hh:mm:ss"));
|
||||
|
||||
// 发信号更新状态栏
|
||||
message = QString::fromStdString(game_.getTips());
|
||||
message = QString::fromStdString(position_.getTips());
|
||||
emit statusBarChanged(message);
|
||||
|
||||
// 更新比分 LCD 显示
|
||||
emit score1Changed(QString::number(game_.score_1, 10));
|
||||
emit score2Changed(QString::number(game_.score_2, 10));
|
||||
emit scoreDrawChanged(QString::number(game_.score_draw, 10));
|
||||
emit score1Changed(QString::number(position_.score_1, 10));
|
||||
emit score2Changed(QString::number(position_.score_2, 10));
|
||||
emit scoreDrawChanged(QString::number(position_.score_draw, 10));
|
||||
|
||||
// 播放音效
|
||||
//playSound(":/sound/resources/sound/newgame.wav");
|
||||
|
@ -275,8 +275,8 @@ void GameController::setRule(int ruleNo, step_t stepLimited /*= -1*/, int timeLi
|
|||
}
|
||||
|
||||
// 设置模型规则,重置游戏
|
||||
game_.setContext(&RULES[ruleNo], stepsLimit, timeLimit);
|
||||
gameTemp = game_;
|
||||
position_.setContext(&RULES[ruleNo], stepsLimit, timeLimit);
|
||||
dummyPosition = position_;
|
||||
|
||||
// 重置游戏
|
||||
gameReset();
|
||||
|
@ -284,11 +284,11 @@ void GameController::setRule(int ruleNo, step_t stepLimited /*= -1*/, int timeLi
|
|||
|
||||
void GameController::setEngine1(bool arg)
|
||||
{
|
||||
game_.configure(giveUpIfMostLose_, randomMove_);
|
||||
position_.configure(giveUpIfMostLose_, randomMove_);
|
||||
|
||||
isAiPlayer1 = arg;
|
||||
if (arg) {
|
||||
ai1.setAi(game_);
|
||||
ai1.setAi(position_);
|
||||
if (ai1.isRunning())
|
||||
ai1.resume();
|
||||
else
|
||||
|
@ -300,11 +300,11 @@ void GameController::setEngine1(bool arg)
|
|||
|
||||
void GameController::setEngine2(bool arg)
|
||||
{
|
||||
game_.configure(giveUpIfMostLose_, randomMove_);
|
||||
position_.configure(giveUpIfMostLose_, randomMove_);
|
||||
|
||||
isAiPlayer2 = arg;
|
||||
if (arg) {
|
||||
ai2.setAi(game_);
|
||||
ai2.setAi(position_);
|
||||
if (ai2.isRunning())
|
||||
ai2.resume();
|
||||
else
|
||||
|
@ -325,8 +325,8 @@ void GameController::setAiDepthTime(depth_t depth1, int time1, depth_t depth2, i
|
|||
ai2.wait();
|
||||
}
|
||||
|
||||
ai1.setAi(game_, depth1, time1);
|
||||
ai2.setAi(game_, depth2, time2);
|
||||
ai1.setAi(position_, depth1, time1);
|
||||
ai2.setAi(position_, depth2, time2);
|
||||
|
||||
if (isAiPlayer1) {
|
||||
ai1.start();
|
||||
|
@ -398,13 +398,13 @@ void GameController::flip()
|
|||
ai2.wait();
|
||||
}
|
||||
|
||||
game_.context.board.mirror(game_.cmdlist, game_.cmdline, game_.move_, game_.currentRule, game_.currentLocation);
|
||||
game_.context.board.rotate(180, game_.cmdlist, game_.cmdline, game_.move_, game_.currentRule, game_.currentLocation);
|
||||
gameTemp = game_;
|
||||
position_.context.board.mirror(position_.cmdlist, position_.cmdline, position_.move_, position_.currentRule, position_.currentLocation);
|
||||
position_.context.board.rotate(180, position_.cmdlist, position_.cmdline, position_.move_, position_.currentRule, position_.currentLocation);
|
||||
dummyPosition = position_;
|
||||
|
||||
// 更新棋谱
|
||||
int row = 0;
|
||||
for (const auto &str : *(game_.getCmdList())) {
|
||||
for (const auto &str : *(position_.getCmdList())) {
|
||||
manualListModel.setData(manualListModel.index(row++), str.c_str());
|
||||
}
|
||||
|
||||
|
@ -414,8 +414,8 @@ void GameController::flip()
|
|||
else
|
||||
stageChange(currentRow, true);
|
||||
|
||||
ai1.setAi(game_);
|
||||
ai2.setAi(game_);
|
||||
ai1.setAi(position_);
|
||||
ai2.setAi(position_);
|
||||
|
||||
if (isAiPlayer1) {
|
||||
ai1.start();
|
||||
|
@ -438,13 +438,13 @@ void GameController::mirror()
|
|||
ai2.wait();
|
||||
}
|
||||
|
||||
game_.context.board.mirror(game_.cmdlist, game_.cmdline, game_.move_, game_.currentRule, game_.currentLocation);
|
||||
gameTemp = game_;
|
||||
position_.context.board.mirror(position_.cmdlist, position_.cmdline, position_.move_, position_.currentRule, position_.currentLocation);
|
||||
dummyPosition = position_;
|
||||
|
||||
// 更新棋谱
|
||||
int row = 0;
|
||||
|
||||
for (const auto &str : *(game_.getCmdList())) {
|
||||
for (const auto &str : *(position_.getCmdList())) {
|
||||
manualListModel.setData(manualListModel.index(row++), str.c_str());
|
||||
}
|
||||
|
||||
|
@ -456,8 +456,8 @@ void GameController::mirror()
|
|||
else
|
||||
stageChange(currentRow, true);
|
||||
|
||||
ai1.setAi(game_);
|
||||
ai2.setAi(game_);
|
||||
ai1.setAi(position_);
|
||||
ai2.setAi(position_);
|
||||
|
||||
if (isAiPlayer1) {
|
||||
ai1.start();
|
||||
|
@ -480,13 +480,13 @@ void GameController::turnRight()
|
|||
ai2.wait();
|
||||
}
|
||||
|
||||
game_.context.board.rotate(-90, game_.cmdlist, game_.cmdline, game_.move_, game_.currentRule, game_.currentLocation);
|
||||
gameTemp = game_;
|
||||
position_.context.board.rotate(-90, position_.cmdlist, position_.cmdline, position_.move_, position_.currentRule, position_.currentLocation);
|
||||
dummyPosition = position_;
|
||||
|
||||
// 更新棋谱
|
||||
int row = 0;
|
||||
|
||||
for (const auto &str : *(game_.getCmdList())) {
|
||||
for (const auto &str : *(position_.getCmdList())) {
|
||||
manualListModel.setData(manualListModel.index(row++), str.c_str());
|
||||
}
|
||||
|
||||
|
@ -496,8 +496,8 @@ void GameController::turnRight()
|
|||
else
|
||||
stageChange(currentRow, true);
|
||||
|
||||
ai1.setAi(game_);
|
||||
ai2.setAi(game_);
|
||||
ai1.setAi(position_);
|
||||
ai2.setAi(position_);
|
||||
|
||||
if (isAiPlayer1) {
|
||||
ai1.start();
|
||||
|
@ -520,20 +520,20 @@ void GameController::turnLeft()
|
|||
ai2.wait();
|
||||
}
|
||||
|
||||
game_.context.board.rotate(90, game_.cmdlist, game_.cmdline, game_.move_, game_.currentRule, game_.currentLocation);
|
||||
gameTemp = game_;
|
||||
position_.context.board.rotate(90, position_.cmdlist, position_.cmdline, position_.move_, position_.currentRule, position_.currentLocation);
|
||||
dummyPosition = position_;
|
||||
|
||||
// 更新棋谱
|
||||
int row = 0;
|
||||
for (const auto &str : *(game_.getCmdList())) {
|
||||
for (const auto &str : *(position_.getCmdList())) {
|
||||
manualListModel.setData(manualListModel.index(row++), str.c_str());
|
||||
}
|
||||
|
||||
// 刷新显示
|
||||
updateScence();
|
||||
|
||||
ai1.setAi(game_);
|
||||
ai2.setAi(game_);
|
||||
ai1.setAi(position_);
|
||||
ai2.setAi(position_);
|
||||
if (isAiPlayer1) {
|
||||
ai1.start();
|
||||
}
|
||||
|
@ -548,7 +548,7 @@ void GameController::timerEvent(QTimerEvent *event)
|
|||
static QTime qt1, qt2;
|
||||
|
||||
// 玩家的已用时间
|
||||
game_.getElapsedTime(remainingTime1, remainingTime2);
|
||||
position_.getElapsedTime(remainingTime1, remainingTime2);
|
||||
|
||||
// 如果规则要求计时,则time1和time2表示倒计时
|
||||
if (timeLimit > 0) {
|
||||
|
@ -564,7 +564,7 @@ void GameController::timerEvent(QTimerEvent *event)
|
|||
emit time2Changed(qt2.toString("hh:mm:ss"));
|
||||
|
||||
// 如果胜负已分
|
||||
if (game_.whoWin() != PLAYER_NOBODY) {
|
||||
if (position_.whoWin() != PLAYER_NOBODY) {
|
||||
// 停止计时
|
||||
killTimer(timeID);
|
||||
|
||||
|
@ -572,7 +572,7 @@ void GameController::timerEvent(QTimerEvent *event)
|
|||
timeID = 0;
|
||||
|
||||
// 发信号更新状态栏
|
||||
message = QString::fromStdString(game_.getTips());
|
||||
message = QString::fromStdString(position_.getTips());
|
||||
emit statusBarChanged(message);
|
||||
|
||||
// 弹框
|
||||
|
@ -609,8 +609,8 @@ void GameController::timerEvent(QTimerEvent *event)
|
|||
|
||||
bool GameController::isAIsTurn()
|
||||
{
|
||||
return ((game_.whosTurn() == PLAYER1 && isAiPlayer1) ||
|
||||
(game_.whosTurn() == PLAYER2 && isAiPlayer2));
|
||||
return ((position_.whosTurn() == PLAYER1 && isAiPlayer1) ||
|
||||
(position_.whosTurn() == PLAYER2 && isAiPlayer2));
|
||||
}
|
||||
|
||||
// 关键槽函数,根据QGraphicsScene的信号和状态来执行选子、落子或去子
|
||||
|
@ -643,17 +643,17 @@ bool GameController::actionPiece(QPointF pos)
|
|||
|
||||
if (QMessageBox::Ok == msgBox.exec()) {
|
||||
#endif /* !MOBILE_APP_UI */
|
||||
game_ = gameTemp;
|
||||
position_ = dummyPosition;
|
||||
manualListModel.removeRows(currentRow + 1, manualListModel.rowCount() - currentRow - 1);
|
||||
|
||||
// 如果再决出胜负后悔棋,则重新启动计时
|
||||
if (game_.whoWin() == PLAYER_NOBODY) {
|
||||
if (position_.whoWin() == PLAYER_NOBODY) {
|
||||
|
||||
// 重新启动计时
|
||||
timeID = startTimer(100);
|
||||
|
||||
// 发信号更新状态栏
|
||||
message = QString::fromStdString(game_.getTips());
|
||||
message = QString::fromStdString(position_.getTips());
|
||||
emit statusBarChanged(message);
|
||||
#ifndef MOBILE_APP_UI
|
||||
}
|
||||
|
@ -664,7 +664,7 @@ bool GameController::actionPiece(QPointF pos)
|
|||
}
|
||||
|
||||
// 如果未开局则开局
|
||||
if (game_.getStage() == GAME_NOTSTARTED)
|
||||
if (position_.getStage() == GAME_NOTSTARTED)
|
||||
gameStart();
|
||||
|
||||
// 判断执行选子、落子或去子
|
||||
|
@ -672,10 +672,10 @@ bool GameController::actionPiece(QPointF pos)
|
|||
PieceItem *piece = nullptr;
|
||||
QGraphicsItem *item = scene.itemAt(pos, QTransform());
|
||||
|
||||
switch (game_.getAction()) {
|
||||
switch (position_.getAction()) {
|
||||
case ACTION_PLACE:
|
||||
if (game_._place(r, s)) {
|
||||
if (game_.getAction() == ACTION_CAPTURE) {
|
||||
if (position_._place(r, s)) {
|
||||
if (position_.getAction() == ACTION_CAPTURE) {
|
||||
// 播放成三音效
|
||||
playSound(":/sound/resources/sound/capture.wav");
|
||||
} else {
|
||||
|
@ -693,7 +693,7 @@ bool GameController::actionPiece(QPointF pos)
|
|||
piece = qgraphicsitem_cast<PieceItem *>(item);
|
||||
if (!piece)
|
||||
break;
|
||||
if (game_.choose(r, s)) {
|
||||
if (position_.choose(r, s)) {
|
||||
// 播放选子音效
|
||||
playSound(":/sound/resources/sound/choose.wav");
|
||||
result = true;
|
||||
|
@ -704,7 +704,7 @@ bool GameController::actionPiece(QPointF pos)
|
|||
break;
|
||||
|
||||
case ACTION_CAPTURE:
|
||||
if (game_._capture(r, s)) {
|
||||
if (position_._capture(r, s)) {
|
||||
// 播放音效
|
||||
playSound(":/sound/resources/sound/remove.wav");
|
||||
result = true;
|
||||
|
@ -721,7 +721,7 @@ bool GameController::actionPiece(QPointF pos)
|
|||
|
||||
if (result) {
|
||||
// 发信号更新状态栏
|
||||
message = QString::fromStdString(game_.getTips());
|
||||
message = QString::fromStdString(position_.getTips());
|
||||
emit statusBarChanged(message);
|
||||
|
||||
// 将新增的棋谱行插入到ListModel
|
||||
|
@ -729,7 +729,7 @@ bool GameController::actionPiece(QPointF pos)
|
|||
int k = 0;
|
||||
|
||||
// 输出命令行
|
||||
for (const auto & i : *(game_.getCmdList())) {
|
||||
for (const auto & i : *(position_.getCmdList())) {
|
||||
// 跳过已添加的,因标准list容器没有下标
|
||||
if (k++ <= currentRow)
|
||||
continue;
|
||||
|
@ -739,16 +739,16 @@ bool GameController::actionPiece(QPointF pos)
|
|||
|
||||
// 播放胜利或失败音效
|
||||
#ifndef DONOT_PLAY_WIN_SOUND
|
||||
if (game_.whoWin() != PLAYER_NOBODY &&
|
||||
if (position_.whoWin() != PLAYER_NOBODY &&
|
||||
(manualListModel.data(manualListModel.index(currentRow - 1))).toString().contains("Time over."))
|
||||
playSound(":/sound/resources/sound/win.wav");
|
||||
#endif
|
||||
|
||||
// AI设置
|
||||
if (&game_ == &(this->game_)) {
|
||||
if (&position_ == &(this->position_)) {
|
||||
// 如果还未决出胜负
|
||||
if (game_.whoWin() == PLAYER_NOBODY) {
|
||||
if (game_.whosTurn() == PLAYER1) {
|
||||
if (position_.whoWin() == PLAYER_NOBODY) {
|
||||
if (position_.whosTurn() == PLAYER1) {
|
||||
if (isAiPlayer1) {
|
||||
ai1.resume();
|
||||
}
|
||||
|
@ -768,7 +768,7 @@ bool GameController::actionPiece(QPointF pos)
|
|||
ai2.stop();
|
||||
|
||||
// 弹框
|
||||
//message = QString::fromStdString(game_.getTips());
|
||||
//message = QString::fromStdString(position_.getTips());
|
||||
//QMessageBox::about(NULL, "游戏结果", message);
|
||||
}
|
||||
}
|
||||
|
@ -782,11 +782,11 @@ bool GameController::giveUp()
|
|||
{
|
||||
bool result = false;
|
||||
|
||||
if (game_.whosTurn() == PLAYER1) {
|
||||
result = game_.giveup(PLAYER1);
|
||||
if (position_.whosTurn() == PLAYER1) {
|
||||
result = position_.giveup(PLAYER1);
|
||||
}
|
||||
else if (game_.whosTurn() == PLAYER2) {
|
||||
result = game_.giveup(PLAYER2);
|
||||
else if (position_.whosTurn() == PLAYER2) {
|
||||
result = position_.giveup(PLAYER2);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
|
@ -795,14 +795,14 @@ bool GameController::giveUp()
|
|||
int k = 0;
|
||||
|
||||
// 输出命令行
|
||||
for (const auto & i : *(game_.getCmdList())) {
|
||||
for (const auto & i : *(position_.getCmdList())) {
|
||||
// 跳过已添加的,因标准list容器没有下标
|
||||
if (k++ <= currentRow)
|
||||
continue;
|
||||
manualListModel.insertRow(++currentRow);
|
||||
manualListModel.setData(manualListModel.index(currentRow), i.c_str());
|
||||
}
|
||||
if (game_.whoWin() != PLAYER_NOBODY)
|
||||
if (position_.whoWin() != PLAYER_NOBODY)
|
||||
playSound(":/sound/resources/sound/loss.wav");
|
||||
}
|
||||
|
||||
|
@ -824,7 +824,7 @@ bool GameController::command(const QString &cmd, bool update /* = true */)
|
|||
// 声音
|
||||
QString sound;
|
||||
|
||||
switch (game_.getAction()) {
|
||||
switch (position_.getAction()) {
|
||||
case ACTION_CHOOSE:
|
||||
case ACTION_PLACE:
|
||||
sound = ":/sound/resources/sound/drog.wav";
|
||||
|
@ -837,44 +837,44 @@ bool GameController::command(const QString &cmd, bool update /* = true */)
|
|||
}
|
||||
|
||||
// 如果未开局则开局
|
||||
if (game_.getStage() == GAME_NOTSTARTED) {
|
||||
if (position_.getStage() == GAME_NOTSTARTED) {
|
||||
gameStart();
|
||||
}
|
||||
|
||||
if (!game_.command(cmd.toStdString().c_str()))
|
||||
if (!position_.command(cmd.toStdString().c_str()))
|
||||
return false;
|
||||
|
||||
if (sound == ":/sound/resources/sound/drog.wav" && game_.getAction() == ACTION_CAPTURE) {
|
||||
if (sound == ":/sound/resources/sound/drog.wav" && position_.getAction() == ACTION_CAPTURE) {
|
||||
sound = ":/sound/resources/sound/capture.wav";
|
||||
}
|
||||
|
||||
if (update) {
|
||||
playSound(sound);
|
||||
updateScence(game_);
|
||||
updateScence(position_);
|
||||
}
|
||||
|
||||
// 发信号更新状态栏
|
||||
message = QString::fromStdString(game_.getTips());
|
||||
message = QString::fromStdString(position_.getTips());
|
||||
emit statusBarChanged(message);
|
||||
|
||||
// 对于新开局
|
||||
if (game_.getCmdList()->size() <= 1) {
|
||||
if (position_.getCmdList()->size() <= 1) {
|
||||
manualListModel.removeRows(0, manualListModel.rowCount());
|
||||
manualListModel.insertRow(0);
|
||||
manualListModel.setData(manualListModel.index(0), game_.getCmdLine());
|
||||
manualListModel.setData(manualListModel.index(0), position_.getCmdLine());
|
||||
currentRow = 0;
|
||||
}
|
||||
// 对于当前局
|
||||
else {
|
||||
currentRow = manualListModel.rowCount() - 1;
|
||||
// 跳过已添加行,迭代器不支持+运算符,只能一个个++
|
||||
auto i = (game_.getCmdList()->begin());
|
||||
for (int r = 0; i != (game_.getCmdList())->end(); i++) {
|
||||
auto i = (position_.getCmdList()->begin());
|
||||
for (int r = 0; i != (position_.getCmdList())->end(); i++) {
|
||||
if (r++ > currentRow)
|
||||
break;
|
||||
}
|
||||
// 将新增的棋谱行插入到ListModel
|
||||
while (i != game_.getCmdList()->end()) {
|
||||
while (i != position_.getCmdList()->end()) {
|
||||
manualListModel.insertRow(++currentRow);
|
||||
manualListModel.setData(manualListModel.index(currentRow), (*i++).c_str());
|
||||
}
|
||||
|
@ -882,17 +882,17 @@ bool GameController::command(const QString &cmd, bool update /* = true */)
|
|||
|
||||
// 播放胜利或失败音效
|
||||
#ifndef DONOT_PLAY_WIN_SOUND
|
||||
if (game_.whoWin() != PLAYER_NOBODY &&
|
||||
if (position_.whoWin() != PLAYER_NOBODY &&
|
||||
(manualListModel.data(manualListModel.index(currentRow - 1))).toString().contains("Time over.")) {
|
||||
playSound(":/sound/resources/sound/win.wav");
|
||||
}
|
||||
#endif
|
||||
|
||||
// AI设置
|
||||
if (&game_ == &(this->game_)) {
|
||||
if (&position_ == &(this->position_)) {
|
||||
// 如果还未决出胜负
|
||||
if (game_.whoWin() == PLAYER_NOBODY) {
|
||||
if (game_.whosTurn() == PLAYER1) {
|
||||
if (position_.whoWin() == PLAYER_NOBODY) {
|
||||
if (position_.whosTurn() == PLAYER1) {
|
||||
if (isAiPlayer1) {
|
||||
ai1.resume();
|
||||
}
|
||||
|
@ -925,7 +925,7 @@ bool GameController::command(const QString &cmd, bool update /* = true */)
|
|||
|
||||
#ifdef MESSAGEBOX_ENABLE
|
||||
// 弹框
|
||||
message = QString::fromStdString(game_.getTips());
|
||||
message = QString::fromStdString(position_.getTips());
|
||||
QMessageBox::about(NULL, "游戏结果", message);
|
||||
#endif
|
||||
}
|
||||
|
@ -958,24 +958,24 @@ bool GameController::stageChange(int row, bool forceUpdate)
|
|||
|
||||
for (int i = 0; i <= row; i++) {
|
||||
loggerDebug("%s\n", mlist.at(i).toStdString().c_str());
|
||||
gameTemp.command(mlist.at(i).toStdString().c_str());
|
||||
dummyPosition.command(mlist.at(i).toStdString().c_str());
|
||||
}
|
||||
|
||||
// 下面这步关键,会让悔棋者承担时间损失
|
||||
gameTemp.setStartTime(game_.getStartTimeb());
|
||||
dummyPosition.setStartTime(position_.getStartTimeb());
|
||||
|
||||
// 刷新棋局场景
|
||||
updateScence(gameTemp);
|
||||
updateScence(dummyPosition);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameController::updateScence()
|
||||
{
|
||||
return updateScence(game_);
|
||||
return updateScence(position_);
|
||||
}
|
||||
|
||||
bool GameController::updateScence(MillGame &game)
|
||||
bool GameController::updateScence(Position &game)
|
||||
{
|
||||
const int *board = game.getBoard();
|
||||
QPointF pos;
|
||||
|
|
|
@ -192,7 +192,7 @@ public slots:
|
|||
|
||||
// 更新棋局显示,每步后执行才能刷新局面
|
||||
bool updateScence();
|
||||
bool updateScence(MillGame &game);
|
||||
bool updateScence(Position &game);
|
||||
|
||||
// 显示网络配置窗口
|
||||
void showNetworkWindow();
|
||||
|
@ -204,10 +204,10 @@ protected:
|
|||
|
||||
private:
|
||||
// 棋对象的数据模型
|
||||
MillGame game_;
|
||||
Position position_;
|
||||
|
||||
// 棋对象的数据模型(临时)
|
||||
MillGame gameTemp;
|
||||
Position dummyPosition;
|
||||
|
||||
// 2个AI的线程
|
||||
AiThread ai1, ai2;
|
||||
|
|
Loading…
Reference in New Issue