refactor: 简化部分函数和参数变量命名

This commit is contained in:
CalciteM Team 2019-09-15 21:38:18 +08:00
parent 4a6b8963f7
commit 0d931e4877
15 changed files with 258 additions and 260 deletions

View File

@ -63,7 +63,7 @@ value_t Evaluation::getValue(Game &tempGame, Position *position, AIAlgorithm::No
// 如果形成去子状态每有一个可去的子算100分
case ACTION_CAPTURE:
nPiecesNeedRemove = (position->turn == PLAYER_1) ?
nPiecesNeedRemove = (position->sideToMove == PLAYER_1) ?
position->nPiecesNeedRemove : -(position->nPiecesNeedRemove);
value += nPiecesNeedRemove * VALUE_EACH_PIECE_NEEDREMOVE;
#ifdef DEBUG_AB_TREE
@ -94,7 +94,7 @@ value_t Evaluation::getValue(Game &tempGame, Position *position, AIAlgorithm::No
// 如果形成去子状态每有一个可去的子算128分
case ACTION_CAPTURE:
nPiecesNeedRemove = (position->turn == PLAYER_1) ?
nPiecesNeedRemove = (position->sideToMove == PLAYER_1) ?
position->nPiecesNeedRemove : -(position->nPiecesNeedRemove);
value += nPiecesNeedRemove * VALUE_EACH_PIECE_NEEDREMOVE_2;
#ifdef DEBUG_AB_TREE
@ -121,10 +121,10 @@ value_t Evaluation::getValue(Game &tempGame, Position *position, AIAlgorithm::No
// 走棋阶段被闷判断
if (position->action == ACTION_CHOOSE &&
tempGame.position.board.isAllSurrounded(position->turn, tempGame.currentRule, position->nPiecesOnBoard, position->turn) &&
tempGame.position.board.isAllSurrounded(position->sideToMove, tempGame.currentRule, position->nPiecesOnBoard, position->sideToMove) &&
tempGame.getRule()->isLoseWhenNoWay) {
// 规则要求被“闷”判负,则对手获胜
value_t delta = position->turn == PLAYER_1 ? -VALUE_WIN : VALUE_WIN;
value_t delta = position->sideToMove == PLAYER_1 ? -VALUE_WIN : VALUE_WIN;
value += delta;
}

View File

@ -25,7 +25,7 @@
#include "player.h"
#include "misc.h"
void MoveList::generateLegalMoves(AIAlgorithm &ai, Game &tempGame,
void MoveList::generate(AIAlgorithm &ai, Game &tempGame,
AIAlgorithm::Node *node, AIAlgorithm::Node *root,
move_t bestMove)
{
@ -65,7 +65,7 @@ void MoveList::generateLegalMoves(AIAlgorithm &ai, Game &tempGame,
}
// 对手
player_t opponent = Player::getOpponent(tempGame.position.turn);
player_t opponent = Player::getOpponent(tempGame.position.sideToMove);
// 列出所有合法的下一招
switch (tempGame.position.action) {
@ -82,11 +82,11 @@ void MoveList::generateLegalMoves(AIAlgorithm &ai, Game &tempGame,
}
if (tempGame.position.phase != PHASE_NOTSTARTED || node != root) {
ai.addNode(node, VALUE_ZERO, (move_t)location, bestMove, tempGame.position.turn);
ai.addNode(node, VALUE_ZERO, (move_t)location, bestMove, tempGame.position.sideToMove);
} else {
// 若为先手,则抢占星位
if (Board::isStarLocation(location)) {
ai.addNode(node, VALUE_INFINITE, (move_t)location, bestMove, tempGame.position.turn);
ai.addNode(node, VALUE_INFINITE, (move_t)location, bestMove, tempGame.position.sideToMove);
}
}
}
@ -105,7 +105,7 @@ void MoveList::generateLegalMoves(AIAlgorithm &ai, Game &tempGame,
continue;
}
if (tempGame.position.nPiecesOnBoard[tempGame.position.turnId] > tempGame.currentRule.nPiecesAtLeast ||
if (tempGame.position.nPiecesOnBoard[tempGame.position.sideId] > tempGame.currentRule.nPiecesAtLeast ||
!tempGame.currentRule.allowFlyWhenRemainThreePieces) {
// 对于棋盘上还有3个子以上或不允许飞子的情况要求必须在着法表中
for (int direction = DIRECTION_CLOCKWISE; direction <= DIRECTION_OUTWARD; direction++) {
@ -113,7 +113,7 @@ void MoveList::generateLegalMoves(AIAlgorithm &ai, Game &tempGame,
newLocation = moveTable[oldLocation][direction];
if (newLocation && !tempGame.boardLocations[newLocation]) {
move_t move = move_t((oldLocation << 8) + newLocation);
ai.addNode(node, VALUE_ZERO, move, bestMove, tempGame.position.turn); // (12%)
ai.addNode(node, VALUE_ZERO, move, bestMove, tempGame.position.sideToMove); // (12%)
}
}
} else {
@ -121,7 +121,7 @@ void MoveList::generateLegalMoves(AIAlgorithm &ai, Game &tempGame,
for (newLocation = Board::LOCATION_BEGIN; newLocation < Board::LOCATION_END; newLocation++) {
if (!tempGame.boardLocations[newLocation]) {
move_t move = move_t((oldLocation << 8) + newLocation);
ai.addNode(node, VALUE_ZERO, move, bestMove, tempGame.position.turn);
ai.addNode(node, VALUE_ZERO, move, bestMove, tempGame.position.sideToMove);
}
}
}
@ -136,7 +136,7 @@ void MoveList::generateLegalMoves(AIAlgorithm &ai, Game &tempGame,
for (int i = MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) {
location = movePriorityTable[i];
if (tempGame.boardLocations[location] & opponent) {
ai.addNode(node, VALUE_ZERO, (move_t)-location, bestMove, tempGame.position.turn);
ai.addNode(node, VALUE_ZERO, (move_t)-location, bestMove, tempGame.position.sideToMove);
}
}
break;
@ -147,7 +147,7 @@ void MoveList::generateLegalMoves(AIAlgorithm &ai, Game &tempGame,
location = movePriorityTable[i];
if (tempGame.boardLocations[location] & opponent) {
if (tempGame.getRule()->allowRemoveMill || !tempGame.position.board.inHowManyMills(location)) {
ai.addNode(node, VALUE_ZERO, (move_t)-location, bestMove, tempGame.position.turn);
ai.addNode(node, VALUE_ZERO, (move_t)-location, bestMove, tempGame.position.sideToMove);
}
}
}
@ -158,7 +158,7 @@ void MoveList::generateLegalMoves(AIAlgorithm &ai, Game &tempGame,
}
}
void MoveList::createMoveTable(Game &game)
void MoveList::create(Game &game)
{
// Note: 未严格按 direction_t 中枚举的顺序从左到右排列
#if 1
@ -374,7 +374,7 @@ void MoveList::createMoveTable(Game &game)
#endif
}
void MoveList::shuffleMovePriorityTable(Game &game)
void MoveList::shuffle(Game &game)
{
array<move_t, 4> movePriorityTable0 = { (move_t)17, (move_t)19, (move_t)21, (move_t)23 }; // 中圈四个顶点 (星位)
array<move_t, 8> movePriorityTable1 = { (move_t)25, (move_t)27, (move_t)29, (move_t)31, (move_t)9, (move_t)11, (move_t)13, (move_t)15 }; // 外圈和内圈四个顶点

View File

@ -34,15 +34,15 @@ public:
MoveList &operator=(const MoveList &) = delete;
// 生成所有合法的着法并建立子节点
static void generateLegalMoves(AIAlgorithm &ai, Game &tempGame,
static void generate(AIAlgorithm &ai, Game &tempGame,
AIAlgorithm::Node *node, AIAlgorithm::Node *root,
move_t bestMove);
// 生成着法表
static void createMoveTable(Game &game);
static void create(Game &game);
// 随机打乱着法搜索顺序
static void shuffleMovePriorityTable(Game &game);
static void shuffle(Game &game);
// 着法表 // TODO: Move to private
inline static move_t moveTable[Board::N_LOCATIONS][DIRECTIONS_COUNT] = { {MOVE_NONE} };

View File

@ -53,9 +53,9 @@ AIAlgorithm::~AIAlgorithm()
root = nullptr;
}
depth_t AIAlgorithm::changeDepth(depth_t originalDepth)
depth_t AIAlgorithm::changeDepth(depth_t origDepth)
{
depth_t newDepth = originalDepth;
depth_t newDepth = origDepth;
if ((tempGame.position.phase) & (PHASE_PLACING)) {
#ifdef GAME_PLACING_DYNAMIC_DEPTH
@ -105,7 +105,7 @@ struct AIAlgorithm::Node *AIAlgorithm::addNode(
value_t value,
move_t move,
move_t bestMove,
enum player_t player
player_t side
)
{
#ifdef MEMORY_POOL
@ -137,7 +137,7 @@ struct AIAlgorithm::Node *AIAlgorithm::addNode(
#endif
#endif
newNode->player = player;
newNode->sideToMove = side;
#ifdef DEBUG_AB_TREE
newNode->root = root;
@ -228,11 +228,11 @@ bool AIAlgorithm::nodeGreater(const Node *first, const Node *second)
#endif
}
void AIAlgorithm::sortLegalMoves(Node *node)
void AIAlgorithm::sortMoves(Node *node)
{
// 这个函数对效率的影响很大,排序好的话,剪枝较早,节省时间,但不能在此函数耗费太多时间
auto cmp = tempGame.position.turn == PLAYER_1 ? nodeGreater : nodeLess;
auto cmp = tempGame.position.sideToMove == PLAYER_1 ? nodeGreater : nodeLess;
std::stable_sort(node->children.begin(), node->children.end(), cmp);
}
@ -262,7 +262,7 @@ void AIAlgorithm::setGame(const Game &game)
// 如果规则改变重建hashmap
if (strcmp(this->game_.currentRule.name, game.currentRule.name) != 0) {
#ifdef TRANSPOSITION_TABLE_ENABLE
TranspositionTable::clearTranspositionTable();
TranspositionTable::clear();
#endif // TRANSPOSITION_TABLE_ENABLE
#ifdef BOOK_LEARNING
@ -297,7 +297,7 @@ void AIAlgorithm::setGame(const Game &game)
#endif
}
int AIAlgorithm::alphaBetaPruning(depth_t depth)
int AIAlgorithm::search(depth_t depth)
{
value_t value = VALUE_ZERO;
@ -306,7 +306,7 @@ int AIAlgorithm::alphaBetaPruning(depth_t depth)
time_t time0 = time(nullptr);
srand(static_cast<unsigned int>(time0));
chrono::steady_clock::time_point timeStart = chrono::steady_clock::now();
auto timeStart = chrono::steady_clock::now();
chrono::steady_clock::time_point timeEnd;
#ifdef BOOK_LEARNING
@ -345,17 +345,17 @@ int AIAlgorithm::alphaBetaPruning(depth_t depth)
#endif // THREEFOLD_REPETITION
// 随机打乱着法顺序
MoveList::shuffleMovePriorityTable(game_);
MoveList::shuffle(game_);
#ifdef IDS_SUPPORT
// 深化迭代
for (depth_t i = 2; i < d; i += 1) {
#ifdef TRANSPOSITION_TABLE_ENABLE
#ifdef CLEAR_TRANSPOSITION_TABLE
TranspositionTable::clearTranspositionTable(); // 每次走子前清空哈希表
TranspositionTable::clear(); // 每次走子前清空哈希表
#endif
#endif
alphaBetaPruning(i, -VALUE_INFINITE, VALUE_INFINITE, root);
search(i, -VALUE_INFINITE, VALUE_INFINITE, root);
}
timeEnd = chrono::steady_clock::now();
@ -364,11 +364,11 @@ int AIAlgorithm::alphaBetaPruning(depth_t depth)
#ifdef TRANSPOSITION_TABLE_ENABLE
#ifdef CLEAR_TRANSPOSITION_TABLE
TranspositionTable::clearTranspositionTable(); // 每次走子前清空哈希表
TranspositionTable::clear(); // 每次走子前清空哈希表
#endif
#endif
value = alphaBetaPruning(d, value_t(-VALUE_INFINITE) /* alpha */, VALUE_INFINITE /* beta */, root);
value = search(d, value_t(-VALUE_INFINITE) /* alpha */, VALUE_INFINITE /* beta */, root);
timeEnd = chrono::steady_clock::now();
loggerDebug("Total Time: %llus\n", chrono::duration_cast<chrono::seconds>(timeEnd - timeStart).count());
@ -378,7 +378,7 @@ int AIAlgorithm::alphaBetaPruning(depth_t depth)
return 0;
}
value_t AIAlgorithm::alphaBetaPruning(depth_t depth, value_t alpha, value_t beta, Node *node)
value_t AIAlgorithm::search(depth_t depth, value_t alpha, value_t beta, Node *node)
{
// 评价值
value_t value;
@ -483,7 +483,7 @@ value_t AIAlgorithm::alphaBetaPruning(depth_t depth, value_t alpha, value_t beta
evaluatedNodeCount++;
// 为争取速胜value 值 +- 深度 (有必要?)
value_t delta = value_t(position->turn == PLAYER_1 ? depth : -depth);
value_t delta = value_t(position->sideToMove == PLAYER_1 ? depth : -depth);
node->value += delta;
#ifdef DEBUG_AB_TREE
@ -511,11 +511,11 @@ value_t AIAlgorithm::alphaBetaPruning(depth_t depth, value_t alpha, value_t beta
}
// 生成子节点树,即生成每个合理的着法
MoveList::generateLegalMoves(*this, tempGame, node, root, bestMove);
MoveList::generate(*this, tempGame, node, root, bestMove);
// 根据演算模型执行 MiniMax 检索,对先手,搜索 Max, 对后手,搜索 Min
minMax = tempGame.position.turn == PLAYER_1 ? -VALUE_INFINITE : VALUE_INFINITE;
minMax = tempGame.position.sideToMove == PLAYER_1 ? -VALUE_INFINITE : VALUE_INFINITE;
for (auto child : node->children) {
// 棋局入栈保存,以便后续撤销着法
@ -540,13 +540,13 @@ value_t AIAlgorithm::alphaBetaPruning(depth_t depth, value_t alpha, value_t beta
#endif /* DEEPER_IF_ONLY_ONE_LEGAL_MOVE */
// 递归 Alpha-Beta 剪枝
value = alphaBetaPruning(depth - 1 + epsilon, alpha, beta, child);
value = search(depth - 1 + epsilon, alpha, beta, child);
// 棋局弹出栈,撤销着法
tempGame.position = positionStack.top();
positionStack.pop();
if (tempGame.position.turn == PLAYER_1) {
if (tempGame.position.sideToMove == PLAYER_1) {
// 为走棋一方的层, 局面对走棋的一方来说是以 α 为评价
// 取最大值
@ -622,7 +622,7 @@ value_t AIAlgorithm::alphaBetaPruning(depth_t depth, value_t alpha, value_t beta
#ifdef IDS_SUPPORT
// 排序子节点树
sortLegalMoves(node);
sortMoves(node);
#endif // IDS_SUPPORT
#ifdef TRANSPOSITION_TABLE_ENABLE
@ -654,9 +654,9 @@ const char* AIAlgorithm::bestMove()
&& !child->pruned
#endif
) {
loggerDebug("[%.2d] %d\t%s\t%d *\n", i, child->move, move2string(child->move), child->value);
loggerDebug("[%.2d] %d\t%s\t%d *\n", i, child->move, moveToCommand(child->move), child->value);
} else {
loggerDebug("[%.2d] %d\t%s\t%d\n", i, child->move, move2string(child->move), child->value);
loggerDebug("[%.2d] %d\t%s\t%d\n", i, child->move, moveToCommand(child->move), child->value);
}
i++;
@ -667,7 +667,7 @@ const char* AIAlgorithm::bestMove()
if (game_.getGiveUpIfMostLose() == true) {
bool isMostLose = true; // 是否必败
player_t whosTurn = game_.position.turn;
player_t whosTurn = game_.position.sideToMove;
for (auto child : root->children) {
if ((whosTurn == PLAYER_1 && child->value > -VALUE_WIN) ||
@ -679,7 +679,7 @@ const char* AIAlgorithm::bestMove()
// 自动认输
if (isMostLose) {
sprintf(cmdline, "Player%d give up!", game_.position.turnId);
sprintf(cmdline, "Player%d give up!", game_.position.sideId);
return cmdline;
}
}
@ -714,23 +714,23 @@ const char* AIAlgorithm::bestMove()
return nullptr;
}
return move2string(bestMoves[0]->move);
return moveToCommand(bestMoves[0]->move);
}
const char *AIAlgorithm::move2string(move_t move)
const char *AIAlgorithm::moveToCommand(move_t move)
{
int r, s;
if (move < 0) {
tempGame.position.board.locationToPolar(-move, r, s);
Board::locationToPolar(-move, r, s);
sprintf(cmdline, "-(%1u,%1u)", r, s);
} else if (move & 0x7f00) {
int r1, s1;
tempGame.position.board.locationToPolar(move >> 8, r1, s1);
tempGame.position.board.locationToPolar(move & 0x00ff, r, s);
Board::locationToPolar(move >> 8, r1, s1);
Board::locationToPolar(move & 0x00ff, r, s);
sprintf(cmdline, "(%1u,%1u)->(%1u,%1u)", r1, s1, r, s);
} else {
tempGame.position.board.locationToPolar(move & 0x007f, r, s);
Board::locationToPolar(move & 0x007f, r, s);
sprintf(cmdline, "(%1u,%1u)", r, s);
}
@ -739,12 +739,12 @@ const char *AIAlgorithm::move2string(move_t move)
#ifdef BOOK_LEARNING
bool MillGameAi_ab::findBookHash(hash_t hash, HashValue &hashValue)
bool AIAlgorithm::findBookHash(hash_t hash, HashValue &hashValue)
{
return bookHashMap.find(hash, hashValue);
}
int MillGameAi_ab::recordBookHash(hash_t hash, const HashValue &hashValue)
int AIAlgorithm::recordBookHash(hash_t hash, const HashValue &hashValue)
{
//hashMapMutex.lock();
bookHashMap.insert(hash, hashValue);
@ -753,14 +753,14 @@ int MillGameAi_ab::recordBookHash(hash_t hash, const HashValue &hashValue)
return 0;
}
void MillGameAi_ab::clearBookHashMap()
void AIAlgorithm::clearBookHashMap()
{
//hashMapMutex.lock();
bookHashMap.clear();
//hashMapMutex.unlock();
}
void MillGameAi_ab::recordOpeningBookToHashMap()
void AIAlgorithm::recordOpeningBookToHashMap()
{
HashValue hashValue;
hash_t hash = 0;
@ -780,13 +780,13 @@ void MillGameAi_ab::recordOpeningBookToHashMap()
openingBook.clear();
}
void MillGameAi_ab::recordOpeningBookHashMapToFile()
void AIAlgorithm::recordOpeningBookHashMapToFile()
{
const QString bookFileName = "opening-book.txt";
bookHashMap.dump(bookFileName);
}
void MillGameAi_ab::loadOpeningBookFileToHashMap()
void AIAlgorithm::loadOpeningBookFileToHashMap()
{
const QString bookFileName = "opening-book.txt";
bookHashMap.load(bookFileName);

View File

@ -19,8 +19,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef MILLGAMEAI_AB
#define MILLGAMEAI_AB
#ifndef SEARCH_H
#define SEARCH_H
#include "config.h"
@ -61,7 +61,7 @@ public:
struct Node* parent {}; // 父节点
move_t move {}; // 着法的命令行指令,图上标示为节点前的连线
value_t value {}; // 节点的值
enum player_t player; // 此着是谁下的 (目前仅调试用)
player_t sideToMove; // 此着是谁下的 (目前仅调试用)
#ifdef SORT_CONSIDER_PRUNED
bool pruned {}; // 是否在此处剪枝
#endif
@ -106,7 +106,7 @@ public:
}
// Alpha-Beta剪枝算法
int alphaBetaPruning(depth_t depth);
int search(depth_t depth);
// 返回最佳走法的命令行
const char *bestMove();
@ -133,11 +133,11 @@ public: /* TODO: Move to private or protected */
// 增加新节点
struct Node *addNode(Node *parent, value_t value,
move_t move, move_t bestMove,
enum player_t player);
player_t side);
protected:
// 对合法的着法降序排序
void sortLegalMoves(Node *node);
void sortMoves(Node *node);
// 清空节点树
void deleteTree(Node *node);
@ -172,13 +172,13 @@ protected:
#endif /* EVALUATE_ENABLE */
// Alpha-Beta剪枝算法
value_t alphaBetaPruning(depth_t depth, value_t alpha, value_t beta, Node *node);
value_t search(depth_t depth, value_t alpha, value_t beta, Node *node);
// 返回着法的命令行
const char *move2string(move_t move);
const char *moveToCommand(move_t move);
// 篡改深度
depth_t changeDepth(depth_t originalDepth);
depth_t changeDepth(depth_t origDepth);
private:
// 原始模型
@ -234,4 +234,4 @@ private:
extern vector<hash_t> history;
#endif
#endif
#endif /* SEARCH_H */

View File

@ -94,7 +94,7 @@ int TranspositionTable::recordHash(value_t value, depth_t depth, TranspositionTa
return 0;
}
void TranspositionTable::clearTranspositionTable()
void TranspositionTable::clear()
{
transpositionTable.clear();
}

View File

@ -42,7 +42,7 @@ public:
static int recordHash(value_t value, depth_t depth, HashType type, hash_t hash, move_t bestMove);
// 清空置换表
static void clearTranspositionTable();
static void clear();
};
extern HashMap<hash_t, TranspositionTable::HashValue> transpositionTable;
@ -50,4 +50,3 @@ extern HashMap<hash_t, TranspositionTable::HashValue> transpositionTable;
#endif // TRANSPOSITION_TABLE_ENABLE
#endif /* TT_H */

View File

@ -71,7 +71,7 @@ void AiThread::setAi(const Game &game)
#ifdef TRANSPOSITION_TABLE_ENABLE
// 新下一盘前清除哈希表 (注意可能同时存在每步之前清除)
#ifdef CLEAR_TRANSPOSITION_TABLE
TranspositionTable::clearTranspositionTable();
TranspositionTable::clear();
#endif
#endif
@ -108,7 +108,7 @@ void AiThread::run()
while (!isInterruptionRequested()) {
mutex.lock();
i = Player::toId(game_->position.turn);
i = Player::toId(game_->position.sideToMove);
if (i != id || waiting_) {
pauseCondition.wait(&mutex);
@ -120,7 +120,7 @@ void AiThread::run()
emit calcStarted();
mutex.unlock();
if (ai.alphaBetaPruning(aiDepth) == 3) {
if (ai.search(aiDepth) == 3) {
// 三次重复局面和
loggerDebug("Draw\n\n");
strCommand = "draw";

View File

@ -65,7 +65,7 @@ Board &Board::operator= (const Board &other)
return *this;
}
void Board::createMillTable(const Rule &currentRule)
void Board::createMillTable(const Rule &rule)
{
const int millTable_noObliqueLine[Board::N_LOCATIONS][LINE_TYPES_COUNT][2] = {
/* 0 */ {{0, 0}, {0, 0}, {0, 0}},
@ -161,7 +161,7 @@ void Board::createMillTable(const Rule &currentRule)
/* 39 */ {{0, 0}, {0, 0}, {0, 0}}
};
if (currentRule.hasObliqueLines) {
if (rule.hasObliqueLines) {
memcpy(millTable, millTable_hasObliqueLines, sizeof(millTable));
} else {
memcpy(millTable, millTable_noObliqueLine, sizeof(millTable));
@ -209,7 +209,6 @@ int Board::polarToLocation(int r, int s)
return r * N_SEATS + s - 1;
}
int Board::inHowManyMills(int location)
{
int n = 0;
@ -226,7 +225,7 @@ int Board::inHowManyMills(int location)
return n;
}
int Board::addMills(const Rule &currentRule, int location)
int Board::addMills(const Rule &rule, int location)
{
// 成三用一个64位整数了规则如下
// 0x 00 00 00 00 00 00 00 00
@ -276,7 +275,7 @@ int Board::addMills(const Rule &currentRule, int location)
+ static_cast<uint64_t>(p[2]);
// 如果允许相同三连反复去子
if (currentRule.allowRemovePiecesRepeatedly) {
if (rule.allowRemovePiecesRepeatedly) {
n++;
continue;
}
@ -302,7 +301,7 @@ int Board::addMills(const Rule &currentRule, int location)
return n;
}
bool Board::isAllInMills(enum player_t player)
bool Board::isAllInMills(player_t player)
{
for (int i = LOCATION_BEGIN; i < LOCATION_END; i++) {
if (locations[i] & (uint8_t)player) {
@ -316,13 +315,13 @@ bool Board::isAllInMills(enum player_t player)
}
// 判断玩家的棋子周围有几个空位
int Board::getSurroundedEmptyLocationCount(int turnId, const Rule &currentRule, int nPiecesOnBoard[],
int Board::getSurroundedEmptyLocationCount(int sideId, const Rule &rule, int nPiecesOnBoard[],
int location, bool includeFobidden)
{
int count = 0;
if (nPiecesOnBoard[turnId] > currentRule.nPiecesAtLeast ||
!currentRule.allowFlyWhenRemainThreePieces) {
if (nPiecesOnBoard[sideId] > rule.nPiecesAtLeast ||
!rule.allowFlyWhenRemainThreePieces) {
int moveLocation;
for (direction_t d = DIRECTION_BEGIN; d < DIRECTIONS_COUNT; d = (direction_t)(d + 1)) {
moveLocation = MoveList::moveTable[location][d];
@ -339,11 +338,11 @@ int Board::getSurroundedEmptyLocationCount(int turnId, const Rule &currentRule,
}
// 判断玩家的棋子是否被围
bool Board::isSurrounded(int turnId, const Rule &currentRule, int nPiecesOnBoard[], int location)
bool Board::isSurrounded(int sideId, const Rule &rule, int nPiecesOnBoard[], int location)
{
// 判断location处的棋子是否被“闷”
if (nPiecesOnBoard[turnId] > currentRule.nPiecesAtLeast ||
!currentRule.allowFlyWhenRemainThreePieces) {
if (nPiecesOnBoard[sideId] > rule.nPiecesAtLeast ||
!rule.allowFlyWhenRemainThreePieces) {
int i, moveLocation;
for (i = 0; i < 4; i++) {
moveLocation = MoveList::moveTable[location][i];
@ -358,15 +357,15 @@ bool Board::isSurrounded(int turnId, const Rule &currentRule, int nPiecesOnBoard
return false;
}
bool Board::isAllSurrounded(int turnId, const Rule &currentRule, int nPiecesOnBoard[], char ch)
bool Board::isAllSurrounded(int sideId, const Rule &rule, int nPiecesOnBoard[], char ch)
{
// 如果摆满
if (nPiecesOnBoard[1] + nPiecesOnBoard[2] >= N_SEATS * N_RINGS)
return true;
// 判断是否可以飞子
if (nPiecesOnBoard[turnId] <= currentRule.nPiecesAtLeast &&
currentRule.allowFlyWhenRemainThreePieces) {
if (nPiecesOnBoard[sideId] <= rule.nPiecesAtLeast &&
rule.allowFlyWhenRemainThreePieces) {
return false;
}
@ -388,15 +387,15 @@ bool Board::isAllSurrounded(int turnId, const Rule &currentRule, int nPiecesOnBo
}
// 判断玩家的棋子是否全部被围
bool Board::isAllSurrounded(int turnId, const Rule &currentRule, int nPiecesOnBoard[], enum player_t ply)
bool Board::isAllSurrounded(int sideId, const Rule &rule, int nPiecesOnBoard[], player_t player)
{
char t = 0x30 & ply;
char t = 0x30 & player;
return isAllSurrounded(turnId, currentRule, nPiecesOnBoard, t);
return isAllSurrounded(sideId, rule, nPiecesOnBoard, t);
}
#if 0
enum player_t Board::getWhosPiece(int r, int s)
player_t Board::getWhosPiece(int r, int s)
{
int location = polarToLocation(r, s);
@ -409,7 +408,7 @@ enum player_t Board::getWhosPiece(int r, int s)
return PLAYER_NOBODY;
}
bool Board::getPieceRS(const player_t &player, const int &number, int &r, int &s, struct Rule &currentRule)
bool Board::getPieceRS(const player_t &player, const int &number, int &r, int &s, struct Rule &rule)
{
int piece;
@ -421,7 +420,7 @@ bool Board::getPieceRS(const player_t &player, const int &number, int &r, int &s
return false;
}
if (number > 0 && number <= currentRule.nTotalPiecesEachSide)
if (number > 0 && number <= rule.nTotalPiecesEachSide)
piece &= number;
else
return false;
@ -466,7 +465,7 @@ bool Board::isStarLocation(int location)
location == 23);
}
void Board::mirror(list <string> &cmdlist, char* cmdline, int32_t move_, struct Rule &currentRule, int location, bool cmdChange /*= true*/)
void Board::mirror(list <string> &cmdlist, char* cmdline, int32_t move_, struct Rule &rule, int location, bool cmdChange /*= true*/)
{
int ch;
int r, s;
@ -510,7 +509,7 @@ void Board::mirror(list <string> &cmdlist, char* cmdline, int32_t move_, struct
location = r * N_SEATS + s;
}
if (currentRule.allowRemovePiecesRepeatedly) {
if (rule.allowRemovePiecesRepeatedly) {
for (auto &mill : millList) {
llp[0] = (mill & 0x000000ff00000000) >> 32;
llp[1] = (mill & 0x0000000000ff0000) >> 16;
@ -578,7 +577,7 @@ void Board::mirror(list <string> &cmdlist, char* cmdline, int32_t move_, struct
}
}
void Board::turn(list <string> &cmdlist, char *cmdline, int32_t move_, const Rule &currentRule, int location, bool cmdChange /*= true*/)
void Board::turn(list <string> &cmdlist, char *cmdline, int32_t move_, const Rule &rule, int location, bool cmdChange /*= true*/)
{
int ch;
int r, s;
@ -635,7 +634,7 @@ void Board::turn(list <string> &cmdlist, char *cmdline, int32_t move_, const Rul
location = r * N_SEATS + s;
}
if (currentRule.allowRemovePiecesRepeatedly) {
if (rule.allowRemovePiecesRepeatedly) {
for (auto &mill : millList) {
llp[0] = (mill & 0x000000ff00000000) >> 32;
llp[1] = (mill & 0x0000000000ff0000) >> 16;
@ -743,7 +742,7 @@ void Board::turn(list <string> &cmdlist, char *cmdline, int32_t move_, const Rul
}
}
void Board::rotate(int degrees, list <string> &cmdlist, char *cmdline, int32_t move_, const Rule &currentRule, int location, bool cmdChange /*= true*/)
void Board::rotate(int degrees, list <string> &cmdlist, char *cmdline, int32_t move_, const Rule &rule, int location, bool cmdChange /*= true*/)
{
// 将degrees转化为0~359之间的数
degrees = degrees % 360;
@ -831,7 +830,7 @@ void Board::rotate(int degrees, list <string> &cmdlist, char *cmdline, int32_t m
location = r * N_SEATS + s;
}
if (currentRule.allowRemovePiecesRepeatedly) {
if (rule.allowRemovePiecesRepeatedly) {
for (auto &mill : millList) {
llp[0] = (mill & 0x000000ff00000000) >> 32;
llp[1] = (mill & 0x0000000000ff0000) >> 16;

View File

@ -63,40 +63,40 @@ public:
static int millTable[N_LOCATIONS][LINE_TYPES_COUNT][N_RINGS - 1];
// 生成成三表
void createMillTable(const Rule &currentRule);
void createMillTable(const Rule &rule);
// 局面左右镜像
void mirror(list <string> &cmdlist, char *cmdline, int32_t move_, struct Rule &currentRule, int currentPos, bool cmdChange = true);
void mirror(list <string> &cmdlist, char *cmdline, int32_t move_, struct Rule &rule, int currentPos, bool cmdChange = true);
// 局面内外翻转
void turn(list <string> &cmdlist, char *cmdline, int32_t move_, const Rule &currentRule, int currentPos, bool cmdChange = true);
void turn(list <string> &cmdlist, char *cmdline, int32_t move_, const Rule &rule, int currentPos, bool cmdChange = true);
// 局面逆时针旋转
void rotate(int degrees, list <string> &cmdlist, char *cmdline, int32_t move_, const Rule &currentRule, int currentPos, bool cmdChange = true);
void rotate(int degrees, list <string> &cmdlist, char *cmdline, int32_t move_, const Rule &rule, int currentPos, bool cmdChange = true);
// 判断棋盘location处的棋子处于几个“三连”中
int inHowManyMills(int location);
// 判断玩家的所有棋子是否都处于“三连”状态
bool isAllInMills(enum player_t);
bool isAllInMills(player_t);
// 判断玩家的棋子周围有几个空位
int getSurroundedEmptyLocationCount(int turnId, const Rule &currentRule, int nPiecesOnBoard[], int location, bool includeFobidden);
int getSurroundedEmptyLocationCount(int sideId, const Rule &rule, int nPiecesOnBoard[], int location, bool includeFobidden);
// 判断玩家的棋子是否被围
bool isSurrounded(int turnId, const Rule &currentRule, int nPiecesOnBoard[], int location);
bool isSurrounded(int sideId, const Rule &rule, int nPiecesOnBoard[], int location);
// 判断玩家的棋子是否全部被围
bool isAllSurrounded(int turnId, const Rule &currentRule, int nPiecesOnBoard[], char ch);
bool isAllSurrounded(int sideId, const Rule &rule, int nPiecesOnBoard[], char ch);
bool isAllSurrounded(int turnId, const Rule &currentRule, int nPiecesOnBoard[], enum player_t ply);
bool isAllSurrounded(int sideId, const Rule &rule, int nPiecesOnBoard[], player_t ply);
// 三连加入列表
int addMills(const Rule &currentRule, int location);
int addMills(const Rule &rule, int location);
#if 0
// 获取位置点棋子的归属人
enum player_t getWhosPiece(int r, int s);
player_t getWhosPiece(int r, int s);
bool getPieceRS(const player_t &player, const int &number, int &r, int &s, struct Rule &currentRule);
#endif
@ -105,10 +105,10 @@ public:
bool getCurrentPiece(player_t &player, int &number, int currentPos);
// 将棋盘下标形式转化为第r圈第s位r和s下标都从1开始
void locationToPolar(int location, int &r, int &s);
static void locationToPolar(int location, int &r, int &s);
// 将第c圈第p位转化为棋盘下标形式r和s下标都从1开始
int polarToLocation(int r, int s);
static int polarToLocation(int r, int s);
static void printBoard();

View File

@ -43,9 +43,9 @@ public:
return id;
}
inline static int toId(player_t who)
inline static int toId(player_t player)
{
return who == PLAYER_1 ? 1 : 2;
return player == PLAYER_1 ? 1 : 2;
}
inline static player_t idToPlayer(int id)

View File

@ -94,7 +94,7 @@ bool Game::configure(bool giveUpIfMostLose, bool randomMove)
// 设置棋局状态和棋盘数据,用于初始化
bool Game::setPosition(const struct Rule *rule, step_t maxStepsLedToDraw, int maxTimeLedToLose,
step_t initialStep,
phase_t phase, player_t turn, action_t action,
phase_t phase, player_t side, action_t action,
const char *locations,
int nPiecesInHand_1, int nPiecesInHand_2, int nPiecesNeedRemove)
{
@ -118,7 +118,7 @@ bool Game::setPosition(const struct Rule *rule, step_t maxStepsLedToDraw, int ma
position.phase = phase;
// 轮流状态标识
setTurn(turn);
setSideToMove(side);
// 动作状态标识
position.action = action;
@ -189,7 +189,7 @@ bool Game::setPosition(const struct Rule *rule, step_t maxStepsLedToDraw, int ma
winner = PLAYER_NOBODY;
// 生成着法表
MoveList::createMoveTable(*this);
MoveList::create(*this);
// 生成成三表
position.board.createMillTable(currentRule);
@ -236,7 +236,7 @@ bool Game::reset()
position.phase = PHASE_NOTSTARTED;
// 设置轮流状态
setTurn(PLAYER_1);
setSideToMove(PLAYER_1);
// 动作状态标识
position.action = ACTION_PLACE;
@ -317,7 +317,7 @@ bool Game::start()
}
}
bool Game::place(int location, int time_p, int8_t rs)
bool Game::place(int location, int time_p, int8_t updateCmdlist)
{
// 如果局面为“结局”返回false
if (position.phase == PHASE_GAMEOVER)
@ -338,7 +338,7 @@ bool Game::place(int location, int time_p, int8_t rs)
// 格式转换
int r = 0;
int s = 0;
position.board.locationToPolar(location, r, s);
Board::locationToPolar(location, r, s);
// 时间的临时变量
int player_ms = -1;
@ -348,8 +348,8 @@ bool Game::place(int location, int time_p, int8_t rs)
int n = 0;
if (position.phase == PHASE_PLACING) {
int playerId = Player::toId(position.turn);
piece = (0x01 | position.turn) + currentRule.nTotalPiecesEachSide - position.nPiecesInHand[playerId];
int playerId = Player::toId(position.sideToMove);
piece = (0x01 | position.sideToMove) + currentRule.nTotalPiecesEachSide - position.nPiecesInHand[playerId];
position.nPiecesInHand[playerId]--;
position.nPiecesOnBoard[playerId]++;
@ -359,7 +359,7 @@ bool Game::place(int location, int time_p, int8_t rs)
move_ = static_cast<move_t>(location);
if (rs) {
if (updateCmdlist) {
player_ms = update(time_p);
sprintf(cmdline, "(%1u,%1u) %02u:%02u",
r, s, player_ms / 60, player_ms % 60);
@ -386,9 +386,9 @@ bool Game::place(int location, int time_p, int8_t rs)
// 设置轮到谁走
if (currentRule.isDefenderMoveFirst) {
setTurn(PLAYER_2);
setSideToMove(PLAYER_2);
} else {
setTurn(PLAYER_1);
setSideToMove(PLAYER_1);
}
// 再决胜负
@ -399,7 +399,7 @@ bool Game::place(int location, int time_p, int8_t rs)
// 如果双方还有子
else {
// 设置轮到谁走
changeTurn();
changeSideToMove();
}
}
// 如果成三
@ -422,7 +422,7 @@ bool Game::place(int location, int time_p, int8_t rs)
// 对于中局落子 (ontext.phase == GAME_MOVING)
// 如果落子不合法
if (position.nPiecesOnBoard[position.turnId] > currentRule.nPiecesAtLeast ||
if (position.nPiecesOnBoard[position.sideId] > currentRule.nPiecesAtLeast ||
!currentRule.allowFlyWhenRemainThreePieces) {
int i;
for (i = 0; i < 4; i++) {
@ -439,7 +439,7 @@ bool Game::place(int location, int time_p, int8_t rs)
// 移子
move_ = static_cast<move_t>((currentLocation << 8) + location);
if (rs) {
if (updateCmdlist) {
player_ms = update(time_p);
sprintf(cmdline, "(%1u,%1u)->(%1u,%1u) %02u:%02u", currentLocation / Board::N_SEATS, currentLocation % Board::N_SEATS + 1,
r, s, player_ms / 60, player_ms % 60);
@ -465,7 +465,7 @@ bool Game::place(int location, int time_p, int8_t rs)
position.action = ACTION_CHOOSE;
// 设置轮到谁走
changeTurn();
changeSideToMove();
// 如果决出胜负
if (win()) {
@ -482,7 +482,7 @@ bool Game::place(int location, int time_p, int8_t rs)
}
out:
if (rs) {
if (updateCmdlist) {
setTips();
}
@ -492,7 +492,7 @@ out:
bool Game::_place(int r, int s, int time_p)
{
// 转换为 location
int location = position.board.polarToLocation(r, s);
int location = Board::polarToLocation(r, s);
return place(location, time_p, true);
}
@ -500,12 +500,12 @@ bool Game::_place(int r, int s, int time_p)
bool Game::_capture(int r, int s, int time_p)
{
// 转换为 location
int location = position.board.polarToLocation(r, s);
int location = Board::polarToLocation(r, s);
return capture(location, time_p, 1);
}
bool Game::capture(int location, int time_p, int8_t cp)
bool Game::capture(int location, int time_p, int8_t updateCmdlist)
{
// 如果局面为"未开局"或“结局”返回false
if (position.phase == PHASE_NOTSTARTED || position.phase == PHASE_GAMEOVER)
@ -522,12 +522,12 @@ bool Game::capture(int location, int time_p, int8_t cp)
// 格式转换
int r = 0;
int s = 0;
position.board.locationToPolar(location, r, s);
Board::locationToPolar(location, r, s);
// 时间的临时变量
int player_ms = -1;
player_t opponent = Player::getOpponent(position.turn);
player_t opponent = Player::getOpponent(position.sideToMove);
// 判断去子是不是对手棋
if (!(opponent & boardLocations[location]))
@ -554,7 +554,7 @@ bool Game::capture(int location, int time_p, int8_t cp)
move_ = static_cast<move_t>(-location);
if (cp) {
if (updateCmdlist) {
player_ms = update(time_p);
sprintf(cmdline, "-(%1u,%1u) %02u:%02u", r, s, player_ms / 60, player_ms % 60);
cmdlist.emplace_back(string(cmdline));
@ -597,9 +597,9 @@ bool Game::capture(int location, int time_p, int8_t cp)
// 设置轮到谁走
if (currentRule.isDefenderMoveFirst) {
setTurn(PLAYER_2);
setSideToMove(PLAYER_2);
} else {
setTurn(PLAYER_1);
setSideToMove(PLAYER_1);
}
// 再决胜负
@ -613,7 +613,7 @@ bool Game::capture(int location, int time_p, int8_t cp)
position.action = ACTION_PLACE;
// 设置轮到谁走
changeTurn();
changeSideToMove();
// 如果决出胜负
if (win()) {
@ -627,7 +627,7 @@ bool Game::capture(int location, int time_p, int8_t cp)
position.action = ACTION_CHOOSE;
// 设置轮到谁走
changeTurn();
changeSideToMove();
// 如果决出胜负
if (win()) {
@ -636,7 +636,7 @@ bool Game::capture(int location, int time_p, int8_t cp)
}
out:
if (cp) {
if (updateCmdlist) {
setTips();
}
@ -654,9 +654,9 @@ bool Game::choose(int location)
return false;
// 判断选子是否可选
if (boardLocations[location] & position.turn) {
if (boardLocations[location] & position.sideToMove) {
// 判断location处的棋子是否被“闷”
if (position.board.isSurrounded(position.turnId, currentRule, position.nPiecesOnBoard, location)) {
if (position.board.isSurrounded(position.sideId, currentRule, position.nPiecesOnBoard, location)) {
return false;
}
@ -674,7 +674,7 @@ bool Game::choose(int location)
bool Game::choose(int r, int s)
{
return choose(position.board.polarToLocation(r, s));
return choose(Board::polarToLocation(r, s));
}
bool Game::giveup(player_t loser)
@ -803,7 +803,7 @@ bool Game::command(int move)
inline int Game::update(int time_p /*= -1*/)
{
int ret = -1;
time_t *player_ms = &elapsedSeconds[position.turnId];
time_t *player_ms = &elapsedSeconds[position.sideId];
time_t playerNext_ms = elapsedSeconds[position.opponentId];
// 根据局面调整计时器
@ -912,15 +912,15 @@ bool Game::win(bool forceDraw)
}
// 如果中局被“闷”
if (position.phase == PHASE_MOVING && position.action == ACTION_CHOOSE && position.board.isAllSurrounded(position.turn, currentRule, position.nPiecesOnBoard, position.turn)) {
if (position.phase == PHASE_MOVING && position.action == ACTION_CHOOSE && position.board.isAllSurrounded(position.sideToMove, currentRule, position.nPiecesOnBoard, position.sideToMove)) {
// 规则要求被“闷”判负,则对手获胜
position.phase = PHASE_GAMEOVER;
if (currentRule.isLoseWhenNoWay) {
tips = "玩家" + Player::chToStr(position.turnChar) + "无子可走被闷";
winner = Player::getOpponent(position.turn);
tips = "玩家" + Player::chToStr(position.chSide) + "无子可走被闷";
winner = Player::getOpponent(position.sideToMove);
int winnerId = Player::toId(winner);
sprintf(cmdline, "Player%d no way to go. Player%d win!", position.turnId, winnerId);
sprintf(cmdline, "Player%d no way to go. Player%d win!", position.sideId, winnerId);
cmdlist.emplace_back(string(cmdline));
#ifdef BOOK_LEARNING
MillGameAi_ab::recordOpeningBookToHashMap(); // TODO: 目前是对所有的失败记录到开局库
@ -930,7 +930,7 @@ bool Game::win(bool forceDraw)
}
// 否则让棋,由对手走
changeTurn();
changeSideToMove();
return false;
}
@ -951,7 +951,7 @@ bool Game::win(bool forceDraw)
}
// 计算玩家1和玩家2的棋子活动能力之差
int Game::getMobilityDiff(enum player_t turn, const Rule &rule, int nPiecesOnBoard[], bool includeFobidden)
int Game::getMobilityDiff(player_t turn, const Rule &rule, int nPiecesOnBoard[], bool includeFobidden)
{
int *locations = boardLocations;
int mobility1 = 0;
@ -990,30 +990,30 @@ void Game::cleanForbiddenLocations()
}
}
void Game::setTurn(player_t player)
void Game::setSideToMove(player_t player)
{
// 设置轮到谁走
position.turn = player;
position.sideToMove = player;
position.turnId = Player::toId(position.turn);
position.turnChar = Player::idToCh(position.turnId);
position.sideId = Player::toId(position.sideToMove);
position.chSide = Player::idToCh(position.sideId);
position.opponent = Player::getOpponent(player);
position.opponentId = Player::toId(position.opponent);
position.opponentChar = Player::idToCh(position.opponentId);
position.chOpponent = Player::idToCh(position.opponentId);
}
void Game::changeTurn()
void Game::changeSideToMove()
{
setTurn(Player::getOpponent(position.turn));
setSideToMove(Player::getOpponent(position.sideToMove));
}
void Game::setTips()
{
string winnerStr, t;
int winnerId;
string turnStr = Player::chToStr(position.turnChar);
string turnStr = Player::chToStr(position.chSide);
switch (position.phase) {
case PHASE_NOTSTARTED:
@ -1023,7 +1023,7 @@ void Game::setTips()
case PHASE_PLACING:
if (position.action == ACTION_PLACE) {
tips = "轮到玩家" + turnStr + "落子,剩余" + std::to_string(position.nPiecesInHand[position.turnId]) + "";
tips = "轮到玩家" + turnStr + "落子,剩余" + std::to_string(position.nPiecesInHand[position.sideId]) + "";
} else if (position.action == ACTION_CAPTURE) {
tips = "成三!轮到玩家" + turnStr + "去子,需去" + std::to_string(position.nPiecesNeedRemove) + "";
}
@ -1120,7 +1120,7 @@ hash_t Game::updateHashMisc()
// 置位
if (position.turn == PLAYER_2) {
if (position.sideToMove == PLAYER_2) {
position.hash |= 1U;
}

View File

@ -47,13 +47,13 @@ public:
enum phase_t phase;
// 轮流状态标识
enum player_t turn;
int turnId;
char turnChar;
player_t sideToMove;
int sideId;
char chSide;
//string turnStr;
enum player_t opponent;
player_t opponent;
int opponentId;
char opponentChar;
char chOpponent;
//string opponentStr;
// 动作状态标识
@ -169,7 +169,7 @@ public:
}
// 判断胜负
enum player_t whoWin() const
player_t whoWin() const
{
return winner;
}
@ -226,7 +226,7 @@ public:
}
// 计算玩家1和玩家2的棋子活动能力之差
int getMobilityDiff(enum player_t turn, const Rule &rule, int nPiecesOnBoard[], bool includeFobidden);
int getMobilityDiff(player_t turn, const Rule &rule, int nPiecesOnBoard[], bool includeFobidden);
// 游戏重置
bool reset();
@ -260,10 +260,10 @@ public:
void cleanForbiddenLocations();
// 设置轮流
void setTurn(player_t player);
void setSideToMove(player_t player);
// 改变轮流
void changeTurn();
void changeSideToMove();
// 设置提示
void setTips();
@ -328,7 +328,7 @@ private:
// uint64_t hash;
// 胜负标识
enum player_t winner;
player_t winner;
// 当前步数
step_t currentStep {};

View File

@ -609,7 +609,7 @@ void GameController::timerEvent(QTimerEvent *event)
bool GameController::isAIsTurn()
{
return isAiPlayer[game_.position.turnId];
return isAiPlayer[game_.position.sideId];
}
// 关键槽函数根据QGraphicsScene的信号和状态来执行选子、落子或去子
@ -747,7 +747,7 @@ bool GameController::actionPiece(QPointF pos)
if (&game_ == &(this->game_)) {
// 如果还未决出胜负
if (game_.whoWin() == PLAYER_NOBODY) {
if (game_.position.turn == PLAYER_1) {
if (game_.position.sideToMove == PLAYER_1) {
if (isAiPlayer[1]) {
ai[1]->resume();
}
@ -779,7 +779,7 @@ bool GameController::actionPiece(QPointF pos)
bool GameController::giveUp()
{
bool result = game_.giveup(game_.position.turn);
bool result = game_.giveup(game_.position.sideToMove);
if (!result) {
return false;
@ -887,7 +887,7 @@ bool GameController::command(const QString &cmd, bool update /* = true */)
if (&game_ == &(this->game_)) {
// 如果还未决出胜负
if (game_.whoWin() == PLAYER_NOBODY) {
if (game_.position.turn == PLAYER_1) {
if (game_.position.sideToMove == PLAYER_1) {
if (isAiPlayer[1]) {
ai[1]->resume();
}