From d2df0c1c138660fb1971fe9d342f76386ed37359 Mon Sep 17 00:00:00 2001 From: CalciteM Date: Sun, 8 Sep 2019 01:59:04 +0800 Subject: [PATCH] =?UTF-8?q?ai:=20=E5=B0=86=E8=AF=84=E4=BC=B0=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E5=88=86=E7=A6=BB=E5=88=B0=20evaluate.cpp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- millgame.pro | 2 + millgame.vcxproj | 2 + millgame.vcxproj.filters | 6 ++ src/ai/evaluate.cpp | 144 +++++++++++++++++++++++++++ src/ai/evaluate.h | 76 +++++++++++++++ src/ai/search.cpp | 205 +-------------------------------------- src/game/millgame.h | 1 - 7 files changed, 235 insertions(+), 201 deletions(-) create mode 100644 src/ai/evaluate.cpp create mode 100644 src/ai/evaluate.h diff --git a/millgame.pro b/millgame.pro index 26fb4655..1c4dd341 100644 --- a/millgame.pro +++ b/millgame.pro @@ -22,6 +22,7 @@ INCLUDEPATH += src/game INCLUDEPATH += src/ui/qt SOURCES += \ + src/ai/evaluate.cpp \ src/game/millgame.cpp \ src/main.cpp \ src/base/thread.cpp \ @@ -39,6 +40,7 @@ HEADERS += \ include/config.h \ include/version.h \ include/version.h.template \ + src/ai/evaluate.h \ src/base/HashNode.h \ src/base/debug.h \ src/base/hashMap.h \ diff --git a/millgame.vcxproj b/millgame.vcxproj index 43f6bbe9..81a112ea 100644 --- a/millgame.vcxproj +++ b/millgame.vcxproj @@ -441,6 +441,7 @@ + @@ -690,6 +691,7 @@ + diff --git a/millgame.vcxproj.filters b/millgame.vcxproj.filters index dca92590..728cc3c0 100644 --- a/millgame.vcxproj.filters +++ b/millgame.vcxproj.filters @@ -96,6 +96,9 @@ base + + ai + @@ -304,6 +307,9 @@ ai + + ai + diff --git a/src/ai/evaluate.cpp b/src/ai/evaluate.cpp new file mode 100644 index 00000000..91d2daae --- /dev/null +++ b/src/ai/evaluate.cpp @@ -0,0 +1,144 @@ +#include "evaluate.h" + +Evaluation::value_t Evaluation::getValue(MillGame &chessTemp, MillGame::ChessContext *chessContext, MillGameAi_ab::Node *node) +{ + // 初始评估值为0,对先手有利则增大,对后手有利则减小 + value_t value = 0; + + int nPiecesInHandDiff = INT_MAX; + int nPiecesOnBoardDiff = INT_MAX; + int nPiecesNeedRemove = 0; + +#ifdef DEBUG_AB_TREE + node->stage = chessContext->stage; + node->action = chessContext->action; + node->evaluated = true; +#endif + + switch (chessContext->stage) { + case MillGame::GAME_NOTSTARTED: + break; + + case MillGame::GAME_PLACING: + // 按手中的棋子计分,不要break; + nPiecesInHandDiff = chessContext->nPiecesInHand_1 - chessContext->nPiecesInHand_2; + value += nPiecesInHandDiff * 50; +#ifdef DEBUG_AB_TREE + node->nPiecesInHandDiff = nPiecesInHandDiff; +#endif + + // 按场上棋子计分 + nPiecesOnBoardDiff = chessContext->nPiecesOnBoard_1 - chessContext->nPiecesOnBoard_2; + value += nPiecesOnBoardDiff * 100; +#ifdef DEBUG_AB_TREE + node->nPiecesOnBoardDiff = nPiecesOnBoardDiff; +#endif + + switch (chessContext->action) { + // 选子和落子使用相同的评价方法 + case MillGame::ACTION_CHOOSE: + case MillGame::ACTION_PLACE: + break; + + // 如果形成去子状态,每有一个可去的子,算100分 + case MillGame::ACTION_CAPTURE: + nPiecesNeedRemove = (chessContext->turn == MillGame::PLAYER1) ? + chessContext->nPiecesNeedRemove : -(chessContext->nPiecesNeedRemove); + value += nPiecesNeedRemove * 100; +#ifdef DEBUG_AB_TREE + node->nPiecesNeedRemove = nPiecesNeedRemove; +#endif + break; + default: + break; + } + + break; + + case MillGame::GAME_MOVING: + // 按场上棋子计分 + value += chessContext->nPiecesOnBoard_1 * 100 - chessContext->nPiecesOnBoard_2 * 100; + +#ifdef EVALUATE_MOBILITY + // 按棋子活动能力计分 + value += chessTemp.getMobilityDiff(false) * 10; +#endif /* EVALUATE_MOBILITY */ + + switch (chessContext->action) { + // 选子和落子使用相同的评价方法 + case MillGame::ACTION_CHOOSE: + case MillGame::ACTION_PLACE: + break; + + // 如果形成去子状态,每有一个可去的子,算128分 + case MillGame::ACTION_CAPTURE: + nPiecesNeedRemove = (chessContext->turn == MillGame::PLAYER1) ? + chessContext->nPiecesNeedRemove : -(chessContext->nPiecesNeedRemove); + value += nPiecesNeedRemove * 128; +#ifdef DEBUG_AB_TREE + node->nPiecesNeedRemove = nPiecesNeedRemove; +#endif + break; + default: + break; + } + + break; + + // 终局评价最简单 + case MillGame::GAME_OVER: + // 布局阶段闷棋判断 + if (chessContext->nPiecesOnBoard_1 + chessContext->nPiecesOnBoard_2 >= + MillGame::N_SEATS * MillGame::N_RINGS) { + if (chessTemp.getRule()->isStartingPlayerLoseWhenBoardFull) { + // winner = PLAYER2; + value -= 10000; +#ifdef DEBUG_AB_TREE + node->result = -3; +#endif + } else { + value = 0; + } + } + + // 走棋阶段被闷判断 + if (chessContext->action == MillGame::ACTION_CHOOSE && + chessTemp.isAllSurrounded(chessContext->turn) && + chessTemp.getRule()->isLoseWhenNoWay) { + // 规则要求被“闷”判负,则对手获胜 + if (chessContext->turn == MillGame::PLAYER1) { + value -= 10000; +#ifdef DEBUG_AB_TREE + node->result = -2; +#endif + } else { + value += 10000; +#ifdef DEBUG_AB_TREE + node->result = 2; +#endif + } + } + + // 剩余棋子个数判断 + if (chessContext->nPiecesOnBoard_1 < chessTemp.getRule()->nPiecesAtLeast) { + value -= 10000; +#ifdef DEBUG_AB_TREE + node->result = -1; +#endif + } else if (chessContext->nPiecesOnBoard_2 < chessTemp.getRule()->nPiecesAtLeast) { + value += 10000; +#ifdef DEBUG_AB_TREE + node->result = 1; +#endif + } + + break; + + default: + break; + } + + // 赋值返回 + node->value = value; + return value; +} \ No newline at end of file diff --git a/src/ai/evaluate.h b/src/ai/evaluate.h new file mode 100644 index 00000000..452f170d --- /dev/null +++ b/src/ai/evaluate.h @@ -0,0 +1,76 @@ +#ifndef EVALUATE_H +#define EVALUATE_H + +#include "config.h" + +#include "millgame.h" +#include "search.h" + +class Evaluation +{ +public: + Evaluation() = delete; + + Evaluation &operator=(const Evaluation &) = delete; + + using value_t = MillGameAi_ab::value_t; + + static value_t getValue(MillGame &chessTemp, MillGame::ChessContext *chessContext, MillGameAi_ab::Node *node); + + // +#ifdef EVALUATE_ENABLE + +#ifdef EVALUATE_MATERIAL + static value_t evaluateMaterial(Node *node) + { + return 0; + } +#endif + +#ifdef EVALUATE_SPACE + static value_t evaluateSpace(Node *node) + { + return 0; + } +#endif + +#ifdef EVALUATE_MOBILITY + static value_t evaluateMobility(Node *node) + { + return 0; + } +#endif + +#ifdef EVALUATE_TEMPO + static value_t evaluateTempo(Node *node) + { + return 0; + } +#endif + +#ifdef EVALUATE_THREAT + static value_t evaluateThreat(Node *node) + { + return 0; + } +#endif + +#ifdef EVALUATE_SHAPE + static value_t evaluateShape(Node *node) + { + return 0; + } +#endif + +#ifdef EVALUATE_MOTIF + static value_t MillGameAi_ab::evaluateMotif(Node *node) + { + return 0; + } +#endif +#endif /* EVALUATE_ENABLE */ + +private: +}; + +#endif /* EVALUATE_H */ diff --git a/src/ai/search.cpp b/src/ai/search.cpp index 97244fd8..3c4c256c 100644 --- a/src/ai/search.cpp +++ b/src/ai/search.cpp @@ -26,6 +26,7 @@ #include #include "search.h" +#include "evaluate.h" #include "hashmap.h" using namespace CTSL; @@ -479,204 +480,6 @@ void MillGameAi_ab::setChess(const MillGame &chess) #endif } -// 评估子力 -#ifdef EVALUATE_ENABLE - -#ifdef EVALUATE_MATERIAL -MillGameAi_ab::value_t MillGameAi_ab::evaluateMaterial(Node *node) -{ - return 0; -} -#endif - -#ifdef EVALUATE_SPACE -MillGameAi_ab::value_t MillGameAi_ab::evaluateSpace(Node *node) -{ - return 0; -} -#endif - -#ifdef EVALUATE_MOBILITY -MillGameAi_ab::value_t MillGameAi_ab::evaluateMobility(Node *node) -{ - return 0; -} -#endif - -#ifdef EVALUATE_TEMPO -MillGameAi_ab::value_t MillGameAi_ab::evaluateTempo(Node *node) -{ - return 0; -} -#endif - -#ifdef EVALUATE_THREAT -MillGameAi_ab::value_t MillGameAi_ab::evaluateThreat(Node *node) -{ - return 0; -} -#endif - -#ifdef EVALUATE_SHAPE -MillGameAi_ab::value_t MillGameAi_ab::evaluateShape(Node *node) -{ - return 0; -} -#endif - -#ifdef EVALUATE_MOTIF -MillGameAi_ab::value_t MillGameAi_ab::evaluateMotif(Node *node) -{ - return 0; -} -#endif -#endif /* EVALUATE_ENABLE */ - -MillGameAi_ab::value_t MillGameAi_ab::evaluate(Node *node) -{ - // 初始评估值为0,对先手有利则增大,对后手有利则减小 - value_t value = 0; - - int nPiecesInHandDiff = INT_MAX; - int nPiecesOnBoardDiff = INT_MAX; - int nPiecesNeedRemove = 0; - - evaluatedNodeCount++; - -#ifdef DEBUG_AB_TREE - node->stage = chessContext->stage; - node->action = chessContext->action; - node->evaluated = true; -#endif - - switch (chessContext->stage) { - case MillGame::GAME_NOTSTARTED: - break; - - case MillGame::GAME_PLACING: - // 按手中的棋子计分,不要break; - nPiecesInHandDiff = chessContext->nPiecesInHand_1 - chessContext->nPiecesInHand_2; - value += nPiecesInHandDiff * 50; -#ifdef DEBUG_AB_TREE - node->nPiecesInHandDiff = nPiecesInHandDiff; -#endif - - // 按场上棋子计分 - nPiecesOnBoardDiff = chessContext->nPiecesOnBoard_1 - chessContext->nPiecesOnBoard_2; - value += nPiecesOnBoardDiff * 100; -#ifdef DEBUG_AB_TREE - node->nPiecesOnBoardDiff = nPiecesOnBoardDiff; -#endif - - switch (chessContext->action) { - // 选子和落子使用相同的评价方法 - case MillGame::ACTION_CHOOSE: - case MillGame::ACTION_PLACE: - break; - - // 如果形成去子状态,每有一个可去的子,算100分 - case MillGame::ACTION_CAPTURE: - nPiecesNeedRemove = (chessContext->turn == MillGame::PLAYER1) ? - chessContext->nPiecesNeedRemove : -(chessContext->nPiecesNeedRemove); - value += nPiecesNeedRemove * 100; -#ifdef DEBUG_AB_TREE - node->nPiecesNeedRemove = nPiecesNeedRemove; -#endif - break; - default: - break; - } - - break; - - case MillGame::GAME_MOVING: - // 按场上棋子计分 - value += chessContext->nPiecesOnBoard_1 * 100 - chessContext->nPiecesOnBoard_2 * 100; - -#ifdef EVALUATE_MOBILITY - // 按棋子活动能力计分 - value += chessTemp.getMobilityDiff(false) * 10; -#endif /* EVALUATE_MOBILITY */ - - switch (chessContext->action) { - // 选子和落子使用相同的评价方法 - case MillGame::ACTION_CHOOSE: - case MillGame::ACTION_PLACE: - break; - - // 如果形成去子状态,每有一个可去的子,算128分 - case MillGame::ACTION_CAPTURE: - nPiecesNeedRemove = (chessContext->turn == MillGame::PLAYER1) ? - chessContext->nPiecesNeedRemove : -(chessContext->nPiecesNeedRemove); - value += nPiecesNeedRemove * 128; -#ifdef DEBUG_AB_TREE - node->nPiecesNeedRemove = nPiecesNeedRemove; -#endif - break; - default: - break; - } - - break; - - // 终局评价最简单 - case MillGame::GAME_OVER: - // 布局阶段闷棋判断 - if (chessContext->nPiecesOnBoard_1 + chessContext->nPiecesOnBoard_2 >= - MillGame::N_SEATS * MillGame::N_RINGS) { - if (chessTemp.currentRule.isStartingPlayerLoseWhenBoardFull) { - // winner = PLAYER2; - value -= 10000; -#ifdef DEBUG_AB_TREE - node->result = -3; -#endif - } else { - value = 0; - } - } - - // 走棋阶段被闷判断 - if (chessContext->action == MillGame::ACTION_CHOOSE && - chessTemp.isAllSurrounded(chessContext->turn) && - chessTemp.currentRule.isLoseWhenNoWay) { - // 规则要求被“闷”判负,则对手获胜 - if (chessContext->turn == MillGame::PLAYER1) { - value -= 10000; -#ifdef DEBUG_AB_TREE - node->result = -2; -#endif - } else { - value += 10000; -#ifdef DEBUG_AB_TREE - node->result = 2; -#endif - } - } - - // 剩余棋子个数判断 - if (chessContext->nPiecesOnBoard_1 < chessTemp.currentRule.nPiecesAtLeast) { - value -= 10000; -#ifdef DEBUG_AB_TREE - node->result = -1; -#endif - } else if (chessContext->nPiecesOnBoard_2 < chessTemp.currentRule.nPiecesAtLeast) { - value += 10000; -#ifdef DEBUG_AB_TREE - node->result = 1; -#endif - } - - break; - - default: - break; - } - - // 赋值返回 - node->value = value; - return value; -} - int MillGameAi_ab::alphaBetaPruning(depth_t depth) { value_t value = 0; @@ -841,7 +644,8 @@ MillGameAi_ab::value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t al // 搜索到叶子节点(决胜局面) // TODO: 对哈希进行特殊处理 if (chessContext->stage == MillGame::GAME_OVER) { // 局面评估 - node->value = evaluate(node); + node->value = Evaluation::getValue(chessTemp, chessContext, node); + evaluatedNodeCount++; // 为争取速胜,value 值 +- 深度 if (node->value > 0) { @@ -865,7 +669,8 @@ MillGameAi_ab::value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t al // 搜索到第0层或需要退出 if (!depth || requiredQuit) { // 局面评估 - node->value = evaluate(node); + node->value = Evaluation::getValue(chessTemp, chessContext, node); + evaluatedNodeCount++; // 为争取速胜,value 值 +- 深度 (有必要?) if (chessContext->turn == MillGame::PLAYER1) { diff --git a/src/game/millgame.h b/src/game/millgame.h index da8dad51..80854146 100644 --- a/src/game/millgame.h +++ b/src/game/millgame.h @@ -493,7 +493,6 @@ public: // 局面逆时针旋转 void rotate(int degrees, bool cmdChange = true); -protected: // 判断棋盘pos处的棋子处于几个“三连”中 int isInMills(int pos, bool test = false);