ai: 将评估函数分离到 evaluate.cpp

This commit is contained in:
CalciteM 2019-09-08 01:59:04 +08:00
parent 019738c7ce
commit d2df0c1c13
7 changed files with 235 additions and 201 deletions

View File

@ -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 \

View File

@ -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" />

View File

@ -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">

144
src/ai/evaluate.cpp Normal file
View File

@ -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;
}

76
src/ai/evaluate.h Normal file
View File

@ -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 */

View File

@ -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) {

View File

@ -493,7 +493,6 @@ public:
// 局面逆时针旋转
void rotate(int degrees, bool cmdChange = true);
protected:
// 判断棋盘pos处的棋子处于几个“三连”中
int isInMills(int pos, bool test = false);