endgame: 以残局库代码取代开局库代码

* 目前残局库依赖于自动认输开启.
This commit is contained in:
CalciteM Team 2019-09-17 01:58:27 +08:00
parent c4ba910072
commit c9ec23603a
11 changed files with 180 additions and 106 deletions

View File

@ -74,7 +74,7 @@
//#define RAPID_GAME
//#define BOOK_LEARNING
#define ENDGAME_LEARNING
#define THREEFOLD_REPETITION

View File

@ -22,6 +22,7 @@ INCLUDEPATH += src/game
INCLUDEPATH += src/ui/qt
SOURCES += \
src/ai/endgame.cpp \
src/ai/evaluate.cpp \
src/ai/movegen.cpp \
src/ai/tt.cpp \
@ -47,6 +48,7 @@ HEADERS += \
include/config.h \
include/version.h \
include/version.h.template \
src/ai/endgame.h \
src/ai/evaluate.h \
src/ai/movegen.h \
src/ai/tt.h \
@ -59,7 +61,6 @@ HEADERS += \
src/base/stackalloc.h \
src/base/thread.h \
src/ai/search.h \
src/ai/zobrist.h \
src/base/zobrist.h \
src/game/board.h \
src/game/player.h \

View File

@ -441,6 +441,7 @@
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
</AdditionalInputs>
</CustomBuild>
<ClInclude Include="src\ai\endgame.h" />
<ClInclude Include="src\ai\evaluate.h" />
<ClInclude Include="src\ai\movegen.h" />
<ClInclude Include="src\ai\search.h" />
@ -692,6 +693,7 @@
</None>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\ai\endgame.cpp" />
<ClCompile Include="src\ai\evaluate.cpp" />
<ClCompile Include="src\ai\movegen.cpp" />
<ClCompile Include="src\ai\search.cpp" />

View File

@ -117,6 +117,9 @@
<ClInclude Include="src\base\zobrist.h">
<Filter>base</Filter>
</ClInclude>
<ClInclude Include="src\ai\endgame.h">
<Filter>ai</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="debug\moc_predefs.h.cbt">
@ -335,6 +338,9 @@
<ClCompile Include="src\base\zobrist.cpp">
<Filter>base</Filter>
</ClCompile>
<ClCompile Include="src\ai\endgame.cpp">
<Filter>ai</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="millgame.rc">

27
src/ai/endgame.cpp Normal file
View File

@ -0,0 +1,27 @@
/*****************************************************************************
* Copyright (C) 2018-2019 MillGame authors
*
* Authors: liuweilhy <liuweilhy@163.com>
* Calcitem <calcitem@outlook.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "endgame.h"
#ifdef ENDGAME_LEARNING
static constexpr int endgameHashsize = 0x1000000; // 16M
HashMap<hash_t, Endgame> endgameHashMap(endgameHashsize);
#endif // ENDGAME_LEARNING

56
src/ai/endgame.h Normal file
View File

@ -0,0 +1,56 @@
/*****************************************************************************
* Copyright (C) 2018-2019 MillGame authors
*
* Authors: liuweilhy <liuweilhy@163.com>
* Calcitem <calcitem@outlook.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef ENDGAME_H
#define ENDGAME_H
#include "config.h"
#ifdef ENDGAME_LEARNING
#include <vector>
#include "types.h"
#include "hashmap.h"
using namespace std;
using namespace CTSL;
enum endgame_t : uint8_t
{
ENDGAME_NONE,
ENDGAME_PLAYER_1_WIN,
ENDGAME_PLAYER_2_WIN,
ENDGAME_DRAW,
};
//#pragma pack (push, 1)
struct Endgame
{
endgame_t type;
};
//#pragma pack(pop)
extern HashMap<hash_t, Endgame> endgameHashMap;
#endif // ENDGAME_LEARNING
#endif // ENDGAME_H

View File

@ -40,49 +40,49 @@ public:
#ifdef EVALUATE_ENABLE
#ifdef EVALUATE_MATERIAL
static value_t evaluateMaterial(MillGameAi_ab::Node *node)
static value_t evaluateMaterial(AIAlgorithm::Node *node)
{
return 0;
}
#endif
#ifdef EVALUATE_SPACE
static value_t evaluateSpace(MillGameAi_ab::Node *node)
static value_t evaluateSpace(AIAlgorithm::Node *node)
{
return 0;
}
#endif
#ifdef EVALUATE_MOBILITY
static value_t evaluateMobility(MillGameAi_ab::Node *node)
static value_t evaluateMobility(AIAlgorithm::Node *node)
{
return 0;
}
#endif
#ifdef EVALUATE_TEMPO
static value_t evaluateTempo(MillGameAi_ab::Node *node)
static value_t evaluateTempo(AIAlgorithm::Node *node)
{
return 0;
}
#endif
#ifdef EVALUATE_THREAT
static value_t evaluateThreat(MillGameAi_ab::Node *node)
static value_t evaluateThreat(AIAlgorithm::Node *node)
{
return 0;
}
#endif
#ifdef EVALUATE_SHAPE
static value_t evaluateShape(MillGameAi_ab::Node *node)
static value_t evaluateShape(AIAlgorithm::Node *node)
{
return 0;
}
#endif
#ifdef EVALUATE_MOTIF
static value_t MillGameAi_ab::evaluateMotif(MillGameAi_ab::Node *node)
static value_t AIAlgorithm::evaluateMotif(AIAlgorithm::Node *node)
{
return 0;
}

View File

@ -29,16 +29,11 @@
#include "movegen.h"
#include "hashmap.h"
#include "tt.h"
#include "endgame.h"
#include "types.h"
using namespace CTSL;
#ifdef BOOK_LEARNING
static constexpr int bookHashsize = 0x1000000; // 16M
HashMap<hash_t, MillGameAi_ab::HashValue> bookHashMap(bookHashsize);
vector<hash_t> openingBook;
#endif // BOOK_LEARNING
// 用于检测重复局面 (Position)
vector<hash_t> history;
@ -79,11 +74,19 @@ depth_t AIAlgorithm::changeDepth(depth_t origDepth)
12, 12, 13, 14, /* 20 ~ 23 */
};
#ifdef ENDGAME_LEARNING
const depth_t movingDiffDepthTable[] = {
0, 0, 0, /* 0 ~ 2 */
0, 0, 0, 0, 0, /* 3 ~ 7 */
0, 0, 0, 0, 0 /* 8 ~ 12 */
};
#else
const depth_t movingDiffDepthTable[] = {
0, 0, 0, /* 0 ~ 2 */
11, 10, 9, 8, 7, /* 3 ~ 7 */
6, 5, 4, 3, 2 /* 8 ~ 12 */
};
#endif /* ENDGAME_LEARNING */
if ((tempGame.position.phase) & (PHASE_PLACING)) {
d = placingDepthTable[tempGame.getPiecesInHandCount(1)];
@ -288,11 +291,11 @@ void AIAlgorithm::setGame(const Game &game)
TranspositionTable::clear();
#endif // TRANSPOSITION_TABLE_ENABLE
#ifdef BOOK_LEARNING
// TODO: 规则改变时清空学习表
//clearBookHashMap();
//openingBook.clear();
#endif // BOOK_LEARNING
#ifdef ENDGAME_LEARNING
// TODO: 规则改变时清空残局库
//clearEndgameHashMap();
//endgameList.clear();
#endif // ENDGAME_LEARNING
history.clear();
}
@ -332,19 +335,6 @@ int AIAlgorithm::search(depth_t depth)
auto timeStart = chrono::steady_clock::now();
chrono::steady_clock::time_point timeEnd;
#ifdef BOOK_LEARNING
if (position_.getPhase() == GAME_PLACING)
{
if (position_.position.nPiecesInHand[1] <= 10) {
// 开局库只记录摆棋阶段最后的局面
openingBook.push_back(position_.getHash());
} else {
// 暂时在此处清空开局库
openingBook.clear();
}
}
#endif
#ifdef THREEFOLD_REPETITION
static int nRepetition = 0;
@ -415,18 +405,37 @@ value_t AIAlgorithm::search(depth_t depth, value_t alpha, value_t beta, Node *no
// 子节点的最优着法
move_t bestMove = MOVE_NONE;
#if ((defined TRANSPOSITION_TABLE_ENABLE) || (defined BOOK_LEARNING))
// 哈希类型
enum TranspositionTable::HashType hashf = TranspositionTable::hashfALPHA;
#if defined (TRANSPOSITION_TABLE_ENABLE) || defined(ENDGAME_LEARNING)
// 获取哈希值
hash_t hash = tempGame.getHash();
#endif
#ifdef ENDGAME_LEARNING
// 检索残局库
Endgame endgame;
if (findEndgameHash(hash, endgame)) {
switch (endgame.type) {
case ENDGAME_PLAYER_1_WIN:
node->value = VALUE_WIN;
case ENDGAME_PLAYER_2_WIN:
node->value = -VALUE_WIN;
default:
break;
}
return node->value;
}
#endif /* ENDGAME_LEARNING */
#ifdef TRANSPOSITION_TABLE_ENABLE
// 哈希类型
enum TranspositionTable::HashType hashf = TranspositionTable::hashfALPHA;
#ifdef DEBUG_AB_TREE
node->hash = hash;
#endif
#endif
#ifdef TRANSPOSITION_TABLE_ENABLE
TranspositionTable::HashType type = TranspositionTable::hashfEMPTY;
value_t probeVal = TranspositionTable::probeHash(hash, depth, alpha, beta, bestMove, type);
@ -455,7 +464,7 @@ value_t AIAlgorithm::search(depth_t depth, value_t alpha, value_t beta, Node *no
#endif
return node->value;
}
}
//hashMapMutex.unlock();
#endif /* TRANSPOSITION_TABLE_ENABLE */
@ -515,16 +524,6 @@ value_t AIAlgorithm::search(depth_t depth, value_t alpha, value_t beta, Node *no
}
#endif
#ifdef BOOK_LEARNING
// 检索开局库
if (position->phase == GAME_PLACING && findBookHash(hash, hashValue)) {
if (position->turn == ???) {
// TODO:
node->value += 1;
}
}
#endif
#ifdef TRANSPOSITION_TABLE_ENABLE
// 记录确切的哈希值
TranspositionTable::recordHash(node->value, depth, TranspositionTable::hashfEXACT, hash, MOVE_NONE);
@ -702,6 +701,13 @@ const char* AIAlgorithm::bestMove()
// 自动认输
if (isMostLose) {
#ifdef ENDGAME_LEARNING
Endgame endgame;
endgame.type = game_.position.sideToMove == PLAYER_1 ?
ENDGAME_PLAYER_2_WIN : ENDGAME_PLAYER_1_WIN;
recordEndgameHash(this->game_.getHash(), endgame);
#endif /* ENDGAME_LEARNING */
sprintf(cmdline, "Player%d give up!", game_.position.sideId);
return cmdline;
}
@ -760,58 +766,37 @@ const char *AIAlgorithm::moveToCommand(move_t move)
return cmdline;
}
#ifdef BOOK_LEARNING
bool AIAlgorithm::findBookHash(hash_t hash, HashValue &hashValue)
#ifdef ENDGAME_LEARNING
bool AIAlgorithm::findEndgameHash(hash_t hash, Endgame &endgame)
{
return bookHashMap.find(hash, hashValue);
return endgameHashMap.find(hash, endgame);
}
int AIAlgorithm::recordBookHash(hash_t hash, const HashValue &hashValue)
int AIAlgorithm::recordEndgameHash(hash_t hash, const Endgame &endgame)
{
//hashMapMutex.lock();
bookHashMap.insert(hash, hashValue);
endgameHashMap.insert(hash, endgame);
//hashMapMutex.unlock();
return 0;
}
void AIAlgorithm::clearBookHashMap()
void AIAlgorithm::clearEndgameHashMap()
{
//hashMapMutex.lock();
bookHashMap.clear();
endgameHashMap.clear();
//hashMapMutex.unlock();
}
void AIAlgorithm::recordOpeningBookToHashMap()
void AIAlgorithm::recordEndgameHashMapToFile()
{
HashValue hashValue;
hash_t hash = 0;
for (auto iter = openingBook.begin(); iter != openingBook.end(); ++iter)
{
#if 0
if (findBookHash(*iter, hashValue))
{
}
#endif
memset(&hashValue, 0, sizeof(HashValue));
hash = *iter;
recordBookHash(hash, hashValue); // 暂时使用直接覆盖策略
}
openingBook.clear();
const QString filename = "endgame.txt";
endgameHashMap.dump(filename);
}
void AIAlgorithm::recordOpeningBookHashMapToFile()
void AIAlgorithm::loadEndgameFileToHashMap()
{
const QString bookFileName = "opening-book.txt";
bookHashMap.dump(bookFileName);
const QString filename = "endgame.txt";
endgameHashMap.load(filename);
}
void AIAlgorithm::loadOpeningBookFileToHashMap()
{
const QString bookFileName = "opening-book.txt";
bookHashMap.load(bookFileName);
}
#endif // BOOK_LEARNING
#endif // ENDGAME_LEARNING

View File

@ -36,7 +36,10 @@
#include <array>
#include "position.h"
#include "tt.h"
#include "hashmap.h"
#include "endgame.h"
#include "types.h"
#ifdef MEMORY_POOL
#include "MemoryPool.h"
@ -111,7 +114,7 @@ public:
// 返回最佳走法的命令行
const char *bestMove();
#if ((defined TRANSPOSITION_TABLE_ENABLE) || (defined BOOK_LEARNING))
#ifdef TRANSPOSITION_TABLE_ENABLE
// 清空哈希表
void clearTranspositionTable();
#endif
@ -120,14 +123,14 @@ public:
static bool nodeLess(const Node *first, const Node *second);
static bool nodeGreater(const Node *first, const Node *second);
#ifdef BOOK_LEARNING
bool findBookHash(hash_t hash, HashValue &hashValue);
static int recordBookHash(hash_t hash, const HashValue &hashValue);
void clearBookHashMap();
static void recordOpeningBookToHashMap();
static void recordOpeningBookHashMapToFile();
static void loadOpeningBookFileToHashMap();
#endif // BOOK_LEARNING
#ifdef ENDGAME_LEARNING
bool findEndgameHash(hash_t hash, Endgame &endgame);
static int recordEndgameHash(hash_t hash, const Endgame &endgame);
void clearEndgameHashMap();
static void recordEndgameHashMapToFile();
static void loadEndgameFileToHashMap();
#endif // ENDGAME_LEARNING
public: /* TODO: Move to private or protected */
// 增加新节点

View File

@ -34,9 +34,9 @@ Game::Game()
// 创建哈希数据
constructHash();
#ifdef BOOK_LEARNING
// TODO: 开局库文件被加载了多次
MillGameAi_ab::loadOpeningBookFileToHashMap();
#ifdef ENDGAME_LEARNING
// TODO: 残局文件被加载了多次
AIAlgorithm::loadEndgameFileToHashMap();
#endif
// 默认选择第1号规则即“打三棋”
@ -886,9 +886,6 @@ bool Game::win(bool forceDraw)
position.phase = PHASE_GAMEOVER;
sprintf(cmdline, "Player%d win!", o);
cmdlist.emplace_back(string(cmdline));
#ifdef BOOK_LEARNING
MillGameAi_ab::recordOpeningBookToHashMap(); // TODO: 目前是对"双方"失败都记录到开局库
#endif /* BOOK_LEARNING */
return true;
}
@ -922,9 +919,6 @@ bool Game::win(bool forceDraw)
int winnerId = Player::toId(winner);
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: 目前是对所有的失败记录到开局库
#endif /* BOOK_LEARNING */
return true;
}

View File

@ -98,9 +98,9 @@ GameController::~GameController()
delete ai[1];
delete ai[2];
#ifdef BOOK_LEARNING
MillGameAi_ab::recordOpeningBookHashMapToFile();
#endif /* BOOK_LEARNING */
#ifdef ENDGAME_LEARNING
AIAlgorithm::recordEndgameHashMapToFile();
#endif /* ENDGAME_LEARNING */
}
const QMap<int, QStringList> GameController::getActions()