ai: 将评估函数分离到 evaluate.cpp
This commit is contained in:
parent
019738c7ce
commit
d2df0c1c13
|
@ -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 \
|
||||
|
|
|
@ -441,6 +441,7 @@
|
|||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
</AdditionalInputs>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="src\ai\evaluate.h" />
|
||||
<ClInclude Include="src\ai\search.h" />
|
||||
<ClInclude Include="src\ai\zobrist.h" />
|
||||
<ClInclude Include="src\base\debug.h" />
|
||||
|
@ -690,6 +691,7 @@
|
|||
<Text Include="..\Sample.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\ai\evaluate.cpp" />
|
||||
<ClCompile Include="src\ai\search.cpp" />
|
||||
<ClCompile Include="src\base\thread.cpp" />
|
||||
<ClCompile Include="src\game\millgame.cpp" />
|
||||
|
|
|
@ -96,6 +96,9 @@
|
|||
<ClInclude Include="src\base\debug.h">
|
||||
<Filter>base</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\ai\evaluate.h">
|
||||
<Filter>ai</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="debug\moc_predefs.h.cbt">
|
||||
|
@ -304,6 +307,9 @@
|
|||
<ClCompile Include="src\ai\search.cpp">
|
||||
<Filter>ai</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ai\evaluate.cpp">
|
||||
<Filter>ai</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="millgame.rc">
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -26,6 +26,7 @@
|
|||
#include <algorithm>
|
||||
|
||||
#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) {
|
||||
|
|
|
@ -493,7 +493,6 @@ public:
|
|||
// 局面逆时针旋转
|
||||
void rotate(int degrees, bool cmdChange = true);
|
||||
|
||||
protected:
|
||||
// 判断棋盘pos处的棋子处于几个“三连”中
|
||||
int isInMills(int pos, bool test = false);
|
||||
|
||||
|
|
Loading…
Reference in New Issue