diff --git a/NineChess/src/config.h b/NineChess/src/config.h index 9ec26cce..7a818699 100644 --- a/NineChess/src/config.h +++ b/NineChess/src/config.h @@ -11,6 +11,8 @@ #define HASH_MAP_ENABLE +//#define BOOK_LEARNING + //#define DONOT_DELETE_TREE #define MOVE_PRIORITY_TABLE_SUPPORT diff --git a/NineChess/src/ninechess.cpp b/NineChess/src/ninechess.cpp index 53096aca..24fa8f5e 100644 --- a/NineChess/src/ninechess.cpp +++ b/NineChess/src/ninechess.cpp @@ -10,6 +10,7 @@ #include #include "ninechess.h" +#include "ninechessai_ab.h" // 对静态常量数组的定义要放在类外,不要放在头文件 // 预定义的4套规则 @@ -122,7 +123,7 @@ NineChess::NineChess() // 单独提出 board 等数据,免得每次都写 context.board; board_ = context.board; -#ifdef HASH_MAP_ENABLE + #if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) //hash_ = &context.hash; //zobrist_ = &context.zobrist; @@ -375,14 +376,13 @@ bool NineChess::setContext(const struct Rule *rule, int maxStepsLedToDraw, int m // 当前棋局(3×8) if (board == nullptr) { memset(context.board, 0, sizeof(context.board)); -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) context.hash = 0ull; #endif } else { memcpy(context.board, board, sizeof(context.board)); -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) //context.hash = hash; - //context.hashCheckCode = hashCheckCode; #endif } @@ -489,7 +489,7 @@ void NineChess::getContext(struct Rule &rule, int &step, int &flags, nPiecesInHand_1 = context.nPiecesInHand_1; nPiecesInHand_2 = context.nPiecesInHand_2; num_NeedRemove = context.nPiecesNeedRemove; -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) //hash = context.hash; #endif } @@ -535,7 +535,7 @@ bool NineChess::reset() // 用时置零 elapsedMS_1 = elapsedMS_2 = 0; -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) // 哈希归零 context.hash = 0; #endif @@ -693,7 +693,7 @@ bool NineChess::place(int c, int p, long time_p /* = -1*/) board_[pos] = piece; -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) updateHash(pos); #endif move_ = pos; @@ -784,11 +784,11 @@ bool NineChess::place(int c, int p, long time_p /* = -1*/) c, p, player_ms / 60000, (player_ms % 60000) / 1000, player_ms % 1000); cmdlist.push_back(string(cmdline)); board_[pos] = board_[currentPos]; -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) updateHash(pos); #endif board_[currentPos] = '\x00'; -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) revertHash(currentPos); #endif currentPos = pos; @@ -860,15 +860,15 @@ bool NineChess::capture(int c, int p, long time_p /* = -1*/) // 去子(设置禁点) if (currentRule.hasForbiddenPoint && context.stage == GAME_PLACING) { -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) revertHash(pos); #endif board_[pos] = '\x0f'; -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) updateHash(pos); #endif } else { // 去子 -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) revertHash(pos); #endif board_[pos] = '\x00'; @@ -1036,7 +1036,7 @@ bool NineChess::place(int pos) board_[pos] = piece; -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) updateHash(pos); #endif move_ = pos; @@ -1114,11 +1114,11 @@ bool NineChess::place(int pos) // 移子 move_ = (currentPos << 8) + pos; board_[pos] = board_[currentPos]; -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) updateHash(pos); #endif board_[currentPos] = '\x00'; -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) revertHash(currentPos); #endif currentPos = pos; @@ -1184,15 +1184,15 @@ bool NineChess::capture(int pos) } if (currentRule.hasForbiddenPoint && context.stage == GAME_PLACING) { -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) revertHash(pos); #endif board_[pos] = '\x0f'; -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) updateHash(pos); #endif } else { // 去子 -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) revertHash(pos); #endif board_[pos] = '\x00'; @@ -1206,7 +1206,7 @@ bool NineChess::capture(int pos) move_ = -pos; currentPos = 0; context.nPiecesNeedRemove--; -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) updateHash(pos); #endif //step++; @@ -1515,6 +1515,9 @@ bool NineChess::win() context.stage = GAME_OVER; sprintf(cmdline, "Player1 win!"); cmdlist.push_back(string(cmdline)); +#ifdef BOOK_LEARNING + NineChessAi_ab::recordOpeningBookToHashMap(); // 暂时只对后手的失败记录到开局库 +#endif /* BOOK_LEARNING */ return true; } // 如果摆满了,根据规则判断胜负 @@ -1550,6 +1553,9 @@ bool NineChess::win() context.stage = GAME_OVER; sprintf(cmdline, "Player2 no way to go. Player1 win!"); cmdlist.push_back(string(cmdline)); +#ifdef BOOK_LEARNING + NineChessAi_ab::recordOpeningBookToHashMap(); // 暂时只对后手的失败记录到开局库 +#endif /* BOOK_LEARNING */ return true; } } @@ -1745,7 +1751,7 @@ void NineChess::cleanForbiddenPoints() for (int j = 0; j < N_SEATS; j++) { pos = i * N_SEATS + j; if (board_[pos] == '\x0f') { -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) revertHash(pos); #endif board_[pos] = '\x00'; @@ -2294,7 +2300,7 @@ void NineChess::rotate(int degrees, bool cmdChange /*= true*/) } } -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) #if 0 /* diff --git a/NineChess/src/ninechess.h b/NineChess/src/ninechess.h index c939b0f0..d53aed45 100644 --- a/NineChess/src/ninechess.h +++ b/NineChess/src/ninechess.h @@ -184,7 +184,7 @@ public: */ int board[N_POINTS]; -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) // 局面的哈希值 uint64_t hash; @@ -493,7 +493,7 @@ protected: bool place(int pos); bool capture(int pos); -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) // hash相关 uint64_t getHash(); uint64_t revertHash(int pos); diff --git a/NineChess/src/ninechessai_ab.cpp b/NineChess/src/ninechessai_ab.cpp index 5eff439c..615b331c 100644 --- a/NineChess/src/ninechessai_ab.cpp +++ b/NineChess/src/ninechessai_ab.cpp @@ -20,7 +20,13 @@ using namespace CTSL; #ifdef HASH_MAP_ENABLE static constexpr int hashsize = 0x8000000; // 128M HashMap hashmap(hashsize); -#endif +#endif // HASH_MAP_ENABLE + +#ifdef BOOK_LEARNING +static constexpr int bookHashsize = 0x8000000; // 128M +HashMap bookHashMap(bookHashsize); +vector openingBook; +#endif // BOOK_LEARNING NineChessAi_ab::NineChessAi_ab() : rootNode(nullptr), @@ -63,8 +69,11 @@ struct NineChessAi_ab::Node *NineChessAi_ab::addNode(Node *parent, int value, in newNode->pruned = false; -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) newNode->hash = 0; +#endif + +#ifdef HASH_MAP_ENABLE newNode->isHash = false; #endif @@ -394,12 +403,17 @@ void NineChessAi_ab::deleteTree(Node *node) void NineChessAi_ab::setChess(const NineChess &chess) { -#ifdef HASH_MAP_ENABLE // 如果规则改变,重建hashmap if (strcmp(this->chess_.currentRule.name, chess.currentRule.name)) { +#ifdef HASH_MAP_ENABLE clearHashMap(); +#endif // HASH_MAP_ENABLE + +#ifdef BOOK_LEARNING + //clearBookHashMap(); + //openingBook.clear(); +#endif // BOOK_LEARNING } -#endif this->chess_ = chess; chessTemp = chess; @@ -610,6 +624,19 @@ int NineChessAi_ab::alphaBetaPruning(int depth) time1.start(); +#ifdef BOOK_LEARNING + if (chess_.getStage() == NineChess::GAME_PLACING) + { + if (chess_.context.nPiecesInHand_1 < 8) { + // 不是一开始就记录到开局库 + openingBook.push_back(chess_.getHash()); + } else { + // 暂时在此处清空开局库 + openingBook.clear(); + } + } +#endif + #ifdef MOVE_PRIORITY_TABLE_SUPPORT #ifdef RANDOM_MOVE shuffleMovePriorityTable(); @@ -654,7 +681,12 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node) // 子节点的最优着法 int bestMove = 0; -#ifdef HASH_MAP_ENABLE +#ifdef BOOK_LEARNING + // 是否在开局库中出现过 + bool hitBook = false; +#endif + +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) // 哈希值 HashValue hashValue; memset(&hashValue, 0, sizeof(hashValue)); @@ -665,7 +697,19 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node) // 获取哈希值 uint64_t hash = chessTemp.getHash(); node->hash = hash; +#endif +#ifdef BOOK_LEARNING + // 检索开局库 + if (findBookHash(hash, hashValue)) { + if (chessContext->turn == NineChess::PLAYER2) { + // 是否需对后手扣分 + hitBook = true; + } + } +#endif /* BOOK_LEARNING */ + +#ifdef HASH_MAP_ENABLE // 检索 hashmap //hashMapMutex.lock(); @@ -729,8 +773,6 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node) minMax = chessTemp.whosTurn() == NineChess::PLAYER1 ? -INF_VALUE : INF_VALUE; - - if (alpha >= beta) { node->value = hashValue.value; return node->value; @@ -940,6 +982,12 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node) #endif #endif /* HASH_MAP_ENABLE */ +#ifdef BOOK_LEARNING + if (hitBook) { + node->value++; + } +#endif + // 返回 return node->value; } @@ -1088,7 +1136,7 @@ bool NineChessAi_ab::findHash(uint64_t hash, HashValue &hashValue) if (iter != hashmap.end()) return iter; - // 变换局面,查找hash + // 变换局面,查找hash (废弃) chessTempShift = chessTemp; for (int i = 0; i < 2; i++) { if (i) @@ -1108,6 +1156,10 @@ bool NineChessAi_ab::findHash(uint64_t hash, HashValue &hashValue) #endif } +#endif + +#ifdef HASH_MAP_ENABLE + int NineChessAi_ab::recordHash(const HashValue &hashValue) { //hashMapMutex.lock(); @@ -1156,3 +1208,46 @@ void NineChessAi_ab::clearHashMap() //hashMapMutex.unlock(); } #endif /* HASH_MAP_ENABLE */ + +#ifdef BOOK_LEARNING + +bool NineChessAi_ab::findBookHash(uint64_t hash, HashValue &hashValue) +{ + return bookHashMap.find(hash, hashValue); +} + +int NineChessAi_ab::recordBookHash(const HashValue &hashValue) +{ + //hashMapMutex.lock(); + bookHashMap.insert(hashValue.hash, hashValue); + //hashMapMutex.unlock(); + + return 0; +} + +void NineChessAi_ab::clearBookHashMap() +{ + //hashMapMutex.lock(); + bookHashMap.clear(); + //hashMapMutex.unlock(); +} + +void NineChessAi_ab::recordOpeningBookToHashMap() +{ + HashValue hashValue; + + for (auto iter = openingBook.begin(); iter != openingBook.end(); ++iter) + { +#if 0 + if (findBookHash(*iter, hashValue)) + { + } +#endif + memset(&hashValue, 0, sizeof(HashValue)); + hashValue.hash = *iter; + recordBookHash(hashValue); // 暂时使用直接覆盖策略 + } + + openingBook.clear(); +} +#endif // BOOK_LEARNING diff --git a/NineChess/src/ninechessai_ab.h b/NineChess/src/ninechessai_ab.h index 1b7c5147..b045db6d 100644 --- a/NineChess/src/ninechessai_ab.h +++ b/NineChess/src/ninechessai_ab.h @@ -38,8 +38,10 @@ public: struct Node* parent; // 父节点 size_t id; // 结点编号 bool pruned; // 是否在此处剪枝 -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) uint64_t hash; // 哈希值 +#endif +#ifdef HASH_MAP_ENABLE bool isHash; // 是否从 Hash 读取 #endif /* HASH_MAP_ENABLE */ #ifdef DEBUG_AB_TREE @@ -79,7 +81,7 @@ public: #endif }; -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) // 定义哈希值的类型 enum HashType { @@ -98,7 +100,7 @@ public: enum HashType type; int bestMove; }; -#endif /* HASH_MAP_ENABLE */ +#endif public: NineChessAi_ab(); @@ -118,7 +120,7 @@ public: // 返回最佳走法的命令行 const char *bestMove(); -#ifdef HASH_MAP_ENABLE +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING)) // 清空哈希表 void clearHashMap(); #endif @@ -127,6 +129,13 @@ public: static bool nodeLess(const Node *first, const Node *second); static bool nodeGreater(const Node *first, const Node *second); +#ifdef BOOK_LEARNING + bool findBookHash(uint64_t hash, HashValue &hashValue); + static int recordBookHash(const HashValue &hashValue); + void clearBookHashMap(); + static void recordOpeningBookToHashMap(); +#endif // BOOK_LEARNING + protected: // 生成所有合法的着法并建立子节点 void generateLegalMoves(Node *node, int bestMove); @@ -170,7 +179,7 @@ protected: // 插入哈希表 int recordHash(const HashValue &hashValue); int recordHash(int value, int depth, HashType type, uint64_t hash, int bestMove); -#endif +#endif // HASH_MAP_ENABLE private: // 原始模型