代码清理

This commit is contained in:
CalciteM Team 2019-07-20 18:30:05 +08:00
parent 5aff5d8d29
commit 87ba471531
9 changed files with 160 additions and 341 deletions

View File

@ -11,18 +11,18 @@ namespace CTSL //Concurrent Thread Safe Library
class HashNode
{
public:
HashNode()
HashNode()
#ifndef DISABLE_HASHBUCKET
: next(nullptr)
#endif
{}
HashNode(K key_, V value_) :
HashNode(K key_, V value_) :
#ifndef DISABLE_HASHBUCKET
next(nullptr),
next(nullptr),
#endif
key(key_), value(value_)
{}
~HashNode()
~HashNode()
{
#ifndef DISABLE_HASHBUCKET
next = nullptr;
@ -55,7 +55,7 @@ namespace CTSL //Concurrent Thread Safe Library
~HashBucket() //delete the bucket
{
clear();
}
}
//Function to find an entry in the bucket matching the key
//If key is found, the corresponding value is copied into the parameter "value" and function returns true.
@ -63,7 +63,7 @@ namespace CTSL //Concurrent Thread Safe Library
bool find(const K &key, V &value) const
{
// A shared mutex is used to enable multiple concurrent reads
std::shared_lock<std::shared_timed_mutex> lock(mutex_);
std::shared_lock<std::shared_timed_mutex> lock(mutex_);
HashNode<K, V> * node = head;
#ifdef DISABLE_HASHBUCKET
if (node == nullptr) {
@ -120,14 +120,14 @@ namespace CTSL //Concurrent Thread Safe Library
}
else
{
prev->next = new HashNode<K, V>(key, value);
prev->next = new HashNode<K, V>(key, value);
}
}
else
{
node->setValue(value); //Key found in bucket, update the value
}
#endif // DISABLE_HASHBUCKET
#endif // DISABLE_HASHBUCKET
}
//Function to remove an entry from the bucket, if found
@ -168,7 +168,7 @@ namespace CTSL //Concurrent Thread Safe Library
}
else
{
prev->next = node->next;
prev->next = node->next;
}
delete node; //Free up the memory
}
@ -184,7 +184,7 @@ namespace CTSL //Concurrent Thread Safe Library
if (head != nullptr)
{
delete head;
}
}
#else // DISABLE_HASHBUCKET
HashNode<K, V> * prev = nullptr;
HashNode<K, V> * node = head;

View File

@ -1,11 +1,11 @@
#include <QDebug>
#include "aithread.h"
AiThread::AiThread(int id, QObject *parent) :
QThread(parent),
AiThread::AiThread(int id, QObject *parent) :
QThread(parent),
chess_(nullptr),
waiting_(false),
aiDepth(2),
waiting_(false),
aiDepth(2),
aiTime(120)
{
this->id = id;
@ -35,7 +35,7 @@ void AiThread::setAi(const NineChess &chess)
ai_ab.setChess(*(this->chess_));
#ifdef HASH_MAP_ENABLE
// TODO: 下第二盘时不明原因变慢,临时方案为清除哈希表
// 新下一盘前清除哈希表 (注意可能同时存在每步之前清除)
ai_ab.clearHashMap();
#endif
@ -55,7 +55,7 @@ void AiThread::setAi(const NineChess &chess, int depth, int time)
void AiThread::run()
{
// 测试用数据
#ifdef DEBUG
#ifdef DEBUG_MODE
int iTemp = 0;
#endif
@ -84,14 +84,14 @@ void AiThread::run()
emit calcStarted();
mutex.unlock();
ai_ab.alphaBetaPruning(aiDepth); // 顶层调用 alphaBetaPruning(aiDepth)
ai_ab.alphaBetaPruning(aiDepth); // 顶层调用ab剪枝
const char *str = ai_ab.bestMove();
qDebug() << "Computer:" << str << "\n";
if (strcmp(str, "error!"))
emit command(str);
#ifdef DEBUG
#ifdef DEBUG_MODE
qDebug() << "Thread" << id << "run" << ++iTemp << "times";
#endif

View File

@ -104,13 +104,13 @@ void BoardItem::paint(QPainter *painter,
painter->setPen(fontPen);
QFont font;
font.setPointSize(4);
font.setFamily("Arial");
font.setLetterSpacing(QFont::AbsoluteSpacing, 0);
font.setFamily("Arial");
font.setLetterSpacing(QFont::AbsoluteSpacing, 0);
painter->setFont(font);
for (int i = 0; i < 8; i++) {
char cSeat = '1' + i;
QString strSeat(cSeat);
QString strSeat(cSeat);
painter->drawText(position[(N_RINGS - 1) * N_SEATS + i], strSeat);
}
#endif // DRAW_SEAT_NUMBER

View File

@ -1,7 +1,7 @@
#ifndef CONFIG_H
#define CONFIG_H
//#define DEBUG
//#define DEBUG_MODE
#define RANDOM_MOVE
@ -17,22 +17,18 @@
#define MOVE_PRIORITY_TABLE_SUPPORT
#ifdef DEBUG
#ifdef DEBUG_MODE
#define DONOT_PLAY_SOUND
#define DEBUG_AB_TREE
#endif
#ifndef DEBUG
//#define AB_RANDOM_SORT_CHILDREN
#endif
//#define DONOT_PLAY_SOUND
#ifdef DEBUG
#ifdef DEBUG_MODE
#define GAME_PLACING_FIXED_DEPTH 3
#endif
#ifdef DEBUG
#ifdef DEBUG_MODE
#define GAME_MOVING_FIXED_DEPTH 3
#else // DEBUG
#ifdef DEAL_WITH_HORIZON_EFFECT
@ -46,11 +42,11 @@
#endif // DEAL_WITH_HORIZON_EFFECT
#endif // DEBUG
#ifndef DEBUG
#ifndef DEBUG_MODE
#define GAME_PLACING_DYNAMIC_DEPTH
#endif
#ifdef DEBUG
#ifdef DEBUG_MODE
#define DRAW_SEAT_NUMBER
#endif
@ -67,6 +63,4 @@
// 不使用哈希桶
#define DISABLE_HASHBUCKET
//#define RANDOM_BEST_MOVE
#endif // CONFIG_H

View File

@ -1,11 +1,11 @@
#ifndef HASH_MAP_H_
#define HASH_MAP_H_
#include <cstdint>
#include <iostream>
#include <cstdint>
#include <iostream>
#include <functional>
#include <mutex>
#include "HashNode.h"
#include <mutex>
#include "HashNode.h"
#include "config.h"
@ -19,7 +19,7 @@ namespace CTSL //Concurrent Thread Safe Library
//By default, the std::hash function will be used
//If the hash size is not provided, then a defult size of 1031 will be used
//The hash table itself consists of an array of hash buckets.
//Each hash bucket is implemented as singly linked list with the head as a dummy node created
//Each hash bucket is implemented as singly linked list with the head as a dummy node created
//during the creation of the bucket. All the hash buckets are created during the construction of the map.
//Locks are taken per bucket, hence multiple threads can write simultaneously in different buckets in the hash map
#ifdef HASH_KEY_DISABLE
@ -48,13 +48,13 @@ namespace CTSL //Concurrent Thread Safe Library
//Copy and Move of the HashMap are not supported at this moment
HashMap(const HashMap&) = delete;
HashMap(HashMap&&) = delete;
HashMap& operator=(const HashMap&) = delete;
HashMap& operator=(const HashMap&) = delete;
HashMap& operator=(HashMap&&) = delete;
//Function to find an entry in the hash map matching the key.
//If key is found, the corresponding value is copied into the parameter "value" and function returns true.
//If key is not found, function returns false.
bool find(const K &key, V &value) const
bool find(const K &key, V &value) const
{
size_t hashValue = hashFn(key) & (hashSize - 1) ;
#ifdef DISABLE_HASHBUCKET
@ -66,7 +66,7 @@ namespace CTSL //Concurrent Thread Safe Library
return true;
}
return false;
return false;
#else
return hashTable[hashValue].find(key, value);
#endif
@ -74,7 +74,7 @@ namespace CTSL //Concurrent Thread Safe Library
//Function to insert into the hash map.
//If key already exists, update the value, else insert a new node in the bucket with the <key, value> pair.
void insert(const K &key, const V &value)
void insert(const K &key, const V &value)
{
size_t hashValue = hashFn(key) & (hashSize - 1);
#ifdef DISABLE_HASHBUCKET
@ -87,7 +87,7 @@ namespace CTSL //Concurrent Thread Safe Library
}
//Function to remove an entry from the bucket, if found
void erase(const K &key)
void erase(const K &key)
{
size_t hashValue = hashFn(key) & (hashSize - 1);
#ifdef DISABLE_HASHBUCKET
@ -95,7 +95,7 @@ namespace CTSL //Concurrent Thread Safe Library
#else
hashTable[hashValue].erase(key);
#endif
}
}
//Function to clean up the hasp map, i.e., remove all entries from it
@ -109,7 +109,7 @@ namespace CTSL //Concurrent Thread Safe Library
(hashTable[i]).clear();
}
#endif
}
}
private:
#ifdef DISABLE_HASHBUCKET

View File

@ -123,7 +123,7 @@ NineChess::NineChess()
// 单独提出 board 等数据,免得每次都写 context.board;
board_ = context.board;
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
//hash_ = &context.hash;
//zobrist_ = &context.zobrist;
@ -376,15 +376,15 @@ bool NineChess::setContext(const struct Rule *rule, int maxStepsLedToDraw, int m
// 当前棋局3×8
if (board == nullptr) {
memset(context.board, 0, sizeof(context.board));
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
context.hash = 0ull;
#endif
} else {
memcpy(context.board, board, sizeof(context.board));
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
//context.hash = hash;
#endif
}
}
// 计算盘面子数
// 棋局抽象为一个5×8的数组上下两行留空
@ -419,11 +419,11 @@ bool NineChess::setContext(const struct Rule *rule, int maxStepsLedToDraw, int m
context.nPiecesOnBoard_2 > rule->nTotalPiecesEachSide) {
return false;
}
if (nPiecesInHand_1 < 0 || nPiecesInHand_2 < 0) {
return false;
}
context.nPiecesInHand_1 = rule->nTotalPiecesEachSide - context.nPiecesOnBoard_1;
context.nPiecesInHand_2 = rule->nTotalPiecesEachSide - context.nPiecesOnBoard_2;
context.nPiecesInHand_1 = std::min(nPiecesInHand_1, context.nPiecesInHand_1);
@ -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;
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
//hash = context.hash;
#endif
}
@ -535,7 +535,7 @@ bool NineChess::reset()
// 用时置零
elapsedMS_1 = elapsedMS_2 = 0;
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
// 哈希归零
context.hash = 0;
#endif
@ -662,7 +662,7 @@ bool NineChess::place(int c, int p, long time_p /* = -1*/)
// 如非“落子”状态返回false
if (context.action != ACTION_PLACE)
return false;
// 转换为 pos
int pos = cp2pos(c, p);
@ -693,7 +693,7 @@ bool NineChess::place(int c, int p, long time_p /* = -1*/)
board_[pos] = piece;
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
updateHash(pos);
#endif
move_ = pos;
@ -703,7 +703,7 @@ bool NineChess::place(int c, int p, long time_p /* = -1*/)
cmdlist.push_back(string(cmdline));
currentPos = pos;
currentStep++;
// 如果决出胜负
if (win()) {
setTips();
@ -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];
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
updateHash(pos);
#endif
board_[currentPos] = '\x00';
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#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) {
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
revertHash(pos);
#endif
board_[pos] = '\x0f';
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
updateHash(pos);
#endif
} else { // 去子
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#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;
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#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];
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
updateHash(pos);
#endif
board_[currentPos] = '\x00';
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#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) {
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
revertHash(pos);
#endif
board_[pos] = '\x0f';
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
updateHash(pos);
#endif
} else { // 去子
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#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--;
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
updateHash(pos);
#endif
//step++;
@ -1751,7 +1751,7 @@ void NineChess::cleanForbiddenPoints()
for (int j = 0; j < N_SEATS; j++) {
pos = i * N_SEATS + j;
if (board_[pos] == '\x0f') {
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
revertHash(pos);
#endif
board_[pos] = '\x00';
@ -2300,7 +2300,7 @@ void NineChess::rotate(int degrees, bool cmdChange /*= true*/)
}
}
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if 0
/*
@ -2345,30 +2345,19 @@ void NineChess::constructHash()
uint64_t NineChess::getHash()
{
updateHashMisc(); // 放在此处合适?
// TODO: 每次获取哈希值时更新 hash 值低8位放在此处调用不优雅
updateHashMisc();
return context.hash;
}
uint64_t NineChess::updateHash(int pos)
{
#if 0
// 这里不做
for (int i = POS_BEGIN; i < POS_END; i++) {
hash ^= context.zobrist[i][pointType];
}
#endif
// PieceType is board_[pos]
// 0b00表示空白0b01=1 表示先手棋子0b10=2 表示后手棋子0b11=3 表示禁点
// 0b00 表示空白0b01 = 1 表示先手棋子0b10 = 2 表示后手棋子0b11 = 3 表示禁点
int pointType = (board_[pos] & 0x30) >> 4;
//context.hashCheckCode |= (temp) << ((pos - 8) * 2 + 6);
// TODO: context.hash =
// 清除或者放置棋子
context.hash ^= context.zobrist[pos][pointType];
@ -2395,9 +2384,8 @@ uint64_t NineChess::updateHashMisc()
context.hash |= 1ULL << 1;
}
// TODO: 是否真的需要这几位?
context.hash |= (uint64_t)context.nPiecesNeedRemove << 2;
context.hash |= (uint64_t)context.nPiecesInHand_1 << 4;
context.hash |= (uint64_t)context.nPiecesInHand_1 << 4; // TODO: 或许换 game.stage 也可以?
return context.hash;
}

View File

@ -127,16 +127,16 @@ public:
};
uint64_t rand64(void) {
return rand() ^
return rand() ^
((uint64_t)rand() << 15) ^
((uint64_t)rand() << 30) ^
((uint64_t)rand() << 45) ^
((uint64_t)rand() << 30) ^
((uint64_t)rand() << 45) ^
((uint64_t)rand() << 60);
}
uint64_t rand56(void)
{
return rand64() << 8;
return rand64() << 8;
}
// 玩家标识, 轮流状态, 胜负标识
@ -184,7 +184,7 @@ public:
*/
int board[N_POINTS];
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
// 局面的哈希值
uint64_t hash;
@ -225,7 +225,7 @@ public:
// 尚待去除的子数
int nPiecesNeedRemove;
#if 0
#if 0
struct Mill {
char piece1; // “三连”中最小的棋子
@ -493,13 +493,13 @@ protected:
bool place(int pos);
bool capture(int pos);
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
// hash相关
uint64_t getHash();
uint64_t revertHash(int pos);
uint64_t updateHash(int pos);
uint64_t updateHashMisc();
#endif /* HASH_MAP_ENABLE */
#endif
private:
// 当前使用的规则

View File

@ -56,20 +56,28 @@ void NineChessAi_ab::buildRoot()
rootNode = addNode(nullptr, 0, 0, 0, NineChess::NOBODY);
}
struct NineChessAi_ab::Node *NineChessAi_ab::addNode(Node *parent, int value, int move, int bestMove, enum NineChess::Player player)
struct NineChessAi_ab::Node *NineChessAi_ab::addNode(
Node *parent,
int value,
int move,
int bestMove,
enum NineChess::Player player
)
{
Node *newNode = new Node; // (10%)
Node *newNode = new Node;
newNode->parent = parent;
newNode->value = value;
newNode->move = move;
nodeCount++;
newNode->id = nodeCount;
newNode->pruned = false;
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
player = player; // Remove warning
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
newNode->hash = 0;
#endif
@ -96,34 +104,30 @@ struct NineChessAi_ab::Node *NineChessAi_ab::addNode(Node *parent, int value, in
if (move < 0) {
chessTemp.pos2cp(-move, c, p);
sprintf(cmd, "-(%1u,%1u)", c, p); // (3%)
sprintf(cmd, "-(%1u,%1u)", c, p);
} else if (move & 0x7f00) {
int c1, p1;
chessTemp.pos2cp(move >> 8, c1, p1);
chessTemp.pos2cp(move & 0x00ff, c, p);
sprintf(cmd, "(%1u,%1u)->(%1u,%1u)", c1, p1, c, p); // (7%)
sprintf(cmd, "(%1u,%1u)->(%1u,%1u)", c1, p1, c, p);
} else {
chessTemp.pos2cp(move & 0x007f, c, p);
sprintf(cmd, "(%1u,%1u)", c, p); // (12%)
sprintf(cmd, "(%1u,%1u)", c, p);
}
newNode->cmd = cmd;
#endif // DEBUG_AB_TREE
if (parent) {
if (bestMove != 0 && move == bestMove) {
// 把哈希得到的最优着法换到首位
parent->children.insert(parent->children.begin(), newNode);
// 若没有启用置换表,或启用了但为叶子节点,则 bestMove 为0
if (bestMove == 0 || move != bestMove) {
parent->children.push_back(newNode);
} else {
parent->children.push_back(newNode); // (7%)
// 如果启用了置换表并且不是叶子结点,把哈希得到的最优着法换到首位
parent->children.insert(parent->children.begin(), newNode);
}
}
// if (bestMove != 0 && move == bestMove) {
// std::swap(parent->children[0], parent->children[parent->children.size() - 1]);
// }
return newNode;
}
@ -173,11 +177,12 @@ void NineChessAi_ab::generateLegalMoves(Node *node, int bestMove)
const int MOVE_PRIORITY_TABLE_SIZE = NineChess::N_RINGS * NineChess::N_SEATS;
int pos = 0;
node->children.reserve(48); // 余量空间 (2%)
// 留足余量空间避免多次重新分配,此动作本身也占用 CPU/内存 开销
node->children.reserve(48);
#ifdef MOVE_PRIORITY_TABLE_SUPPORT
#ifdef RANDOM_MOVE
#else // RANDOM_MOVE
int movePriorityTable[MOVE_PRIORITY_TABLE_SIZE] = {
17, 19, 21, 23, // 星位
@ -220,13 +225,13 @@ void NineChessAi_ab::generateLegalMoves(Node *node, int bestMove)
addNode(node, INF_VALUE, pos, bestMove, chessTemp.context.turn);
}
} else {
addNode(node, 0, pos, bestMove, chessTemp.context.turn); // (24%)
addNode(node, 0, pos, bestMove, chessTemp.context.turn);
}
}
}
break;
}
}
// 对于移子阶段
if (chessTemp.context.stage & NineChess::GAME_MOVING) {
int newPos, oldPos;
@ -265,9 +270,9 @@ void NineChessAi_ab::generateLegalMoves(Node *node, int bestMove)
}
}
break;
// 对于吃子动作
case NineChess::ACTION_CAPTURE:
case NineChess::ACTION_CAPTURE:
if (chessTemp.isAllInMills(opponent)) {
// 全成三的情况
for (int i = MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) {
@ -282,7 +287,7 @@ void NineChessAi_ab::generateLegalMoves(Node *node, int bestMove)
pos = movePriorityTable[i];
if (chessTemp.board_[pos] & opponent) {
if (chessTemp.getRule()->allowRemoveMill || !chessTemp.isInMills(pos)) {
addNode(node, 0, -pos, bestMove, chessTemp.context.turn); // (6%)
addNode(node, 0, -pos, bestMove, chessTemp.context.turn);
}
}
}
@ -322,71 +327,11 @@ void NineChessAi_ab::sortLegalMoves(Node *node)
{
// 这个函数对效率的影响很大,排序好的话,剪枝较早,节省时间,但不能在此函数耗费太多时间
#ifdef AB_RANDOM_SORT_CHILDREN
if (chessTemp.whosTurn() == NineChess::PLAYER1) {
node->children.sort([](Node* n1, Node* n2) {
bool ret = false;
if (n1->value > n2->value) {
ret = true;
} else if (n1->value < n2->value) {
ret = false;
} else if (n1->value == n2->value) {
ret = n1->rand < n2->rand;
}
return ret;
});
} else {
node->children.sort([](Node* n1, Node* n2) {
bool ret = false;
if (n1->value < n2->value) {
ret = true;
} else if (n1->value > n2->value) {
ret = false;
} else if (n1->value == n2->value) {
ret = n1->rand < n2->rand;
}
return ret;
});
}
#else
if (chessTemp.whosTurn() == NineChess::PLAYER1) {
//node->children.sort([](Node *n1, Node *n2) {return n1->value > n2->value; }); // (6%)
std::stable_sort(node->children.begin(), node->children.end(), nodeGreater);
} else {
//node->children.sort([](Node *n1, Node *n2) { return n1->value < n2->value; }); // (6%)
std::stable_sort(node->children.begin(), node->children.end(), nodeLess);
}
#if 0
if (chessTemp.whosTurn() == NineChess::PLAYER1) {
node->children.sort([](Node *n1, Node *n2) {
bool ret = false;
if (n1->value > n2->value) {
ret = true;
} else if (n1->value < n2->value) {
ret = false;
} else if (n1->value == n2->value) {
ret = n1->pruned < n2->pruned;
}
return ret;
});
} else {
node->children.sort([](Node *n1, Node *n2) {
bool ret = false;
if (n1->value < n2->value) {
ret = true;
} else if (n1->value > n2->value) {
ret = false;
} else if (n1->value == n2->value) {
ret = n1->pruned > n2->pruned;
}
return ret;
});
}
#endif
#endif
}
void NineChessAi_ab::deleteTree(Node *node)
@ -410,6 +355,7 @@ void NineChessAi_ab::setChess(const NineChess &chess)
#endif // HASH_MAP_ENABLE
#ifdef BOOK_LEARNING
// TODO: 规则改变时清空学习表
//clearBookHashMap();
//openingBook.clear();
#endif // BOOK_LEARNING
@ -476,7 +422,7 @@ int NineChessAi_ab::evaluate(Node *node)
// 如果形成去子状态每有一个可去的子算100分
case NineChess::ACTION_CAPTURE:
nPiecesNeedRemove = (chessContext->turn == NineChess::PLAYER1) ?
nPiecesNeedRemove = (chessContext->turn == NineChess::PLAYER1) ?
chessContext->nPiecesNeedRemove : -(chessContext->nPiecesNeedRemove);
value += nPiecesNeedRemove * 100;
#ifdef DEBUG_AB_TREE
@ -501,7 +447,7 @@ int NineChessAi_ab::evaluate(Node *node)
// 如果形成去子状态每有一个可去的子算128分
case NineChess::ACTION_CAPTURE:
nPiecesNeedRemove = (chessContext->turn == NineChess::PLAYER1) ?
nPiecesNeedRemove = (chessContext->turn == NineChess::PLAYER1) ?
chessContext->nPiecesNeedRemove : -(chessContext->nPiecesNeedRemove);
value += nPiecesNeedRemove * 128;
#ifdef DEBUG_AB_TREE
@ -563,7 +509,7 @@ int NineChessAi_ab::evaluate(Node *node)
node->result = 1;
#endif
}
break;
default:
@ -583,15 +529,11 @@ int NineChessAi_ab::changeDepth(int originalDepth)
#ifdef GAME_PLACING_DYNAMIC_DEPTH
#ifdef DEAL_WITH_HORIZON_EFFECT
#ifdef HASH_MAP_ENABLE
int depthTable[] = { 8, 12, 12, 13, 13, 12, 11, 10, 10, 9, 9, 8, 1 };
//int depthTable[] = { 2, 11, 11, 11, 11, 10, 9, 8, 8, 8, 7, 7, 1 };
#else
int depthTable[] = { 8, 12, 12, 13, 13, 12, 11, 10, 10, 9, 9, 8, 1 };
#else // HASH_MAP_ENABLE
int depthTable[] = { 2, 11, 11, 11, 11, 10, 9, 8, 8, 8, 7, 7, 1 };
//int depthTable[] = { 8, 12, 12, 13, 13, 12, 11, 10, 10, 9, 9, 9, 1 };
#endif // HASH_MAP_ENABLE
//int depthTable[] = { 2, 12, 12, 12, 12, 11, 10, 9, 9, 8, 8, 7, 1 };
#else
//int depthTable[] = { 2, 12, 12, 12, 12, 11, 10, 9, 8, 8, 8, 7, 1 };
#else // DEAL_WITH_HORIZON_EFFECT
int depthTable[] = { 2, 13, 13, 13, 12, 11, 10, 9, 9, 8, 8, 7, 1 };
#endif // DEAL_WITH_HORIZON_EFFECT
newDepth = depthTable[chessTemp.getPiecesInHandCount_1()];
@ -600,7 +542,7 @@ int NineChessAi_ab::changeDepth(int originalDepth)
#endif // GAME_PLACING_DYNAMIC_DEPTH
}
#ifdef GAME_MOVING_FIXED_DEPTH
#ifdef GAME_MOVING_FIXED_DEPTH
// 走棋阶段将深度调整
if ((chessTemp.context.stage) & (NineChess::GAME_MOVING)) {
newDepth = GAME_MOVING_FIXED_DEPTH;
@ -628,7 +570,7 @@ int NineChessAi_ab::alphaBetaPruning(int depth)
if (chess_.getStage() == NineChess::GAME_PLACING)
{
if (chess_.context.nPiecesInHand_1 < 8) {
// 不是一开始就记录到开局库
// 不是一开始就记录到开局库, 几着之后再记录
openingBook.push_back(chess_.getHash());
} else {
// 暂时在此处清空开局库
@ -640,14 +582,14 @@ int NineChessAi_ab::alphaBetaPruning(int depth)
#ifdef MOVE_PRIORITY_TABLE_SUPPORT
#ifdef RANDOM_MOVE
shuffleMovePriorityTable();
#endif
#endif
#endif // RANDOM_MOVE
#endif // MOVE_PRIORITY_TABLE_SUPPORT
#ifdef IDS_SUPPORT
// 深化迭代
for (int i = 2; i < d; i += 2) {
#ifdef HASH_MAP_ENABLE
clearHashMap();
clearHashMap(); // 每次走子前清空哈希表
#endif
alphaBetaPruning(i, -INF_VALUE, INF_VALUE, rootNode);
}
@ -658,6 +600,7 @@ int NineChessAi_ab::alphaBetaPruning(int depth)
#ifdef HASH_MAP_ENABLE
clearHashMap(); // 每次走子前清空哈希表
#endif
value = alphaBetaPruning(d, -INF_VALUE /* alpha */, INF_VALUE /* beta */, rootNode);
qDebug() << "Total Time: " << time1.elapsed() / 1000.0 << "s\n";
@ -686,7 +629,7 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
bool hitBook = false;
#endif
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
// 哈希值
HashValue hashValue;
memset(&hashValue, 0, sizeof(hashValue));
@ -703,7 +646,7 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
// 检索开局库
if (findBookHash(hash, hashValue)) {
if (chessContext->turn == NineChess::PLAYER2) {
// 是否需对后手扣分
// 是否需对后手扣分 // TODO: 先后手都处理
hitBook = true;
}
}
@ -713,9 +656,6 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
// 检索 hashmap
//hashMapMutex.lock();
// 从地址里一定可以读取出东西found 恒定为 true?
//bool found = findHash(hash, hashValue);
HashType type = hashfEMPTY;
int probeVal = probeHash(hash, depth, alpha, beta, bestMove, type);
@ -726,60 +666,20 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
node->value = probeVal;
if (type != hashfEXACT && type != hashfEMPTY) {
node->pruned = true;
node->pruned = true; // TODO: 是否有用?
}
// // TODO: 有必要?
// if (chessContext->turn == NineChess::PLAYER1)
// node->value += hashValue.depth - depth;
// else
// node->value -= hashValue.depth - depth;
#if 0
// TODO: 有必要针对深度微调 value?
if (chessContext->turn == NineChess::PLAYER1)
node->value += hashValue.depth - depth;
else
node->value -= hashValue.depth - depth;
#endif
return node->value;
}
#if 0
if (node != rootNode &&
hashValue.hash == hash && // 校验放在这里?
hashValue.depth >= depth) { // 大于还是大于或等于?
#ifdef DEBUG_AB_TREE
node->isHash = true;
#endif
// TODO: 处理 Alpha/Beta 确切值
if (hashValue.type == hashfEXACT) {
hashHitCount++;
node->value = hashValue.value;
alpha = hashValue.alpha;
beta = hashValue.beta;
// Why? 对 depth 的调整放在这里合适?
if (chessContext->turn == NineChess::PLAYER1)
node->value += hashValue.depth - depth;
else
node->value -= hashValue.depth - depth;
//hashMapMutex::unlock();
return node->value;
}
if (hashValue.type == hashfALPHA) {
alpha = hashValue.alpha;
}
if (hashValue.type == hashfBETA) {
beta = hashValue.beta;
}
minMax = chessTemp.whosTurn() == NineChess::PLAYER1 ? -INF_VALUE : INF_VALUE;
if (alpha >= beta) {
node->value = hashValue.value;
return node->value;
}
}
#endif
//hashMapMutex.unlock();
#endif /* HASH_MAP_ENABLE */
@ -797,11 +697,11 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
#endif // HASH_MAP_ENABLE
#endif // DEBUG_AB_TREE
// 搜索到叶子节点(决胜局面) TODO: 是否应该先检索哈希?
// 搜索到叶子节点(决胜局面) // TODO: 对哈希进行特殊处理
if (chessContext->stage == NineChess::GAME_OVER) {
// 局面评估
node->value = evaluate(node);
// 为争取速胜value 值 +- 深度
if (node->value > 0)
node->value += depth;
@ -835,7 +735,7 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
if (requiredQuit) {
node->isTimeout = true;
}
#endif
#endif
#ifdef HASH_MAP_ENABLE
// 记录确切的哈希值
@ -848,19 +748,16 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
// 生成子节点树,即生成每个合理的着法
generateLegalMoves(node, bestMove); // (43%)
// 排序子节点树
//sortChildren(node);
// 根据演算模型执行 MiniMax 检索,对先手,搜索 Max, 对后手,搜索 Min
minMax = chessTemp.whosTurn() == NineChess::PLAYER1 ? -INF_VALUE : INF_VALUE;
for (auto child : node->children) {
// 上下文入栈保存,以便后续撤销着法
contextStack.push(chessTemp.context); // (7%)
contextStack.push(chessTemp.context);
// 执行着法
chessTemp.command(child->move); // (13%)
chessTemp.command(child->move);
#ifdef DEAL_WITH_HORIZON_EFFECT
// 克服“水平线效应”: 若遇到吃子,则搜索深度增加
@ -870,13 +767,13 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
else {
epsilon = 0;
}
#endif
#endif // DEAL_WITH_HORIZON_EFFECT
// 递归 Alpha-Beta 剪枝
value = alphaBetaPruning(depth - 1 + epsilon, alpha, beta, child);
// 上下文弹出栈,撤销着法
chessTemp.context = contextStack.top(); // (5%)
chessTemp.context = contextStack.top();
contextStack.pop();
if (chessTemp.whosTurn() == NineChess::PLAYER1) {
@ -887,11 +784,10 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
// α 为走棋一方搜索到的最好值,任何比它小的值对当前结点的走棋方都没有意义
// 如果某个着法的结果小于或等于 α,那么它就是很差的着法,因此可以抛弃
//alpha = std::max(value, alpha);
if (value > alpha) {
#ifdef HASH_MAP_ENABLE
hashf = hashfEXACT; // ????
hashf = hashfEXACT;
#endif
alpha = value;
}
@ -910,12 +806,16 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
// 如果搜索过程中返回 β 或比 β 更好的值,那就够好的了,走棋的一方就没有机会使用这种策略了。
// 如果某个着法的结果大于或等于 β,那么整个结点就作废了,因为对手不希望走到这个局面,而它有别的着法可以避免到达这个局面。
// 因此如果我们找到的评价大于或等于β,就证明了这个结点是不会发生的,因此剩下的合理着法没有必要再搜索。
// TODO: 本意是要删掉这句,忘了删,结果反而棋力没有明显问题,待查
// 如果删掉这句,三有时不会堵并且计算效率较低
// 有了这句之后hashf 不可能等于 hashfBETA
beta = std::min(value, beta);
if (value < beta)
{
#ifdef HASH_MAP_ENABLE
hashf = hashfBETA; // ????
hashf = hashfBETA;
#endif
beta = value;
}
@ -926,19 +826,16 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
if (alpha >= beta) {
node->pruned = true;
#ifdef HASH_MAP_ENABLE
//hashf = hashfBETA; // ????
#endif
break;
}
}
}
node->value = minMax;
node->value = minMax;
#ifdef DEBUG_AB_TREE
node->alpha = alpha;
node->beta = beta;
#endif
#endif
// 删除“孙子”节点,防止层数较深的时候节点树太大
#ifndef DONOT_DELETE_TREE
@ -951,35 +848,12 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
#ifdef IDS_SUPPORT
// 排序子节点树
sortLegalMoves(node); // (13%)
sortLegalMoves(node);
#endif // IDS_SUPPORT
#ifdef HASH_MAP_ENABLE
// 记录不一定确切的哈希值
// 记录不一定确切的哈希值
recordHash(node->value, depth, hashf, hash, node->children[0]->move);
#if 0
if (hashValue.hash != hash) {
// 添加到hashmap
HashValue newHashValue;
newHashValue.alpha = alpha;
newHashValue.beta = beta;
newHashValue.depth = depth;
newHashValue.type = hashf;
newHashValue.hash = hash;
newHashValue.value = node->value;
recordHash(newHashValue);
}
// 更新更深层数据
else {
//hashMapMutex.lock();
if (hashValue.depth < depth) {
hashValue.value = node->value;
hashValue.depth = depth;
}
//hashMapMutex.unlock();
}
#endif
#endif /* HASH_MAP_ENABLE */
#ifdef BOOK_LEARNING
@ -995,7 +869,6 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
const char* NineChessAi_ab::bestMove()
{
vector<Node*> bestMoves;
size_t retIndex = 0;
size_t bestMovesSize = 0;
if ((rootNode->children).size() == 0)
@ -1046,28 +919,14 @@ const char* NineChessAi_ab::bestMove()
nodeCount = 0;
evaluatedNodeCount = 0;
#ifdef RANDOM_BEST_MOVE
time_t time0 = time(0);
if (time0 % 10 == 0) {
retIndex = bestMovesSize > 1 ? 1 : 0;
}
#else
retIndex = 0;
#endif
#ifdef RANDOM_BEST_MOVE
qDebug() << "Return" << retIndex << "of" << bestMovesSize << "results" << "(" << time0 << ")";
#endif
#ifdef HASH_MAP_ENABLE
qDebug() << "Hash hit count:" << hashHitCount;
#endif
return move2string(bestMoves[retIndex]->move);
return move2string(bestMoves[0]->move);
}
const char *NineChessAi_ab::move2string(int move)
{
@ -1092,7 +951,7 @@ int NineChessAi_ab::probeHash(uint64_t hash, int depth, int alpha, int beta, int
{
const int valUNKNOWN = INT32_MIN;
HashValue hashValue;
if (hashmap.find(hash, hashValue) == false) {
return valUNKNOWN;
}
@ -1163,7 +1022,6 @@ bool NineChessAi_ab::findHash(uint64_t hash, HashValue &hashValue)
int NineChessAi_ab::recordHash(const HashValue &hashValue)
{
//hashMapMutex.lock();
//HashMap<HashValue>::insert(hashValue.hash, hashValue);
hashmap.insert(hashValue.hash, hashValue);
//hashMapMutex.unlock();
@ -1177,12 +1035,12 @@ int NineChessAi_ab::recordHash(int value, int depth, HashType type, uint64_t has
//hashMapMutex.lock();
HashValue hashValue;
memset(&hashValue, 0, sizeof(HashValue));
memset(&hashValue, 0, sizeof(HashValue));
if (findHash(hash, hashValue) &&
if (findHash(hash, hashValue) &&
hashValue.type != hashfEMPTY &&
hashValue.depth > depth) {
#ifdef DEBUG
#ifdef DEBUG_MODE
qDebug() << "Skip recordHash coz depth";
#endif
return -1;
@ -1195,7 +1053,7 @@ int NineChessAi_ab::recordHash(int value, int depth, HashType type, uint64_t has
hashValue.bestMove = bestMove;
hashmap.insert(hashValue.hash, hashValue);
//hashMapMutex.unlock();
return 0;

View File

@ -38,7 +38,7 @@ public:
struct Node* parent; // 父节点
size_t id; // 结点编号
bool pruned; // 是否在此处剪枝
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
uint64_t hash; // 哈希值
#endif
#ifdef HASH_MAP_ENABLE
@ -50,7 +50,7 @@ public:
int depth; // 深度
bool evaluated; // 是否评估过局面
int alpha; // 当前搜索结点走棋方搜索到的最好值,任何比它小的值对当前结点的走棋方都没有意义。当函数递归时 Alpha 和 Beta 不但取负数而且要交换位置
int beta; // 表示对手目前的劣势这是对手所能承受的最坏结果Beta 值越大,表示对手劣势越明显,如果当前结点返回 Beta 或比 Beta 更好的值,作为父结点的对方就绝对不会选择这种策略
int beta; // 表示对手目前的劣势这是对手所能承受的最坏结果Beta 值越大,表示对手劣势越明显,如果当前结点返回 Beta 或比 Beta 更好的值,作为父结点的对方就绝对不会选择这种策略
bool isTimeout; // 是否遍历到此结点时因为超时而被迫退出
bool isLeaf; // 是否为叶子结点, 叶子结点是决胜局面
bool visited; // 是否在遍历时访问过
@ -62,26 +62,9 @@ public:
int result; // 终局结果,-1为负0为未到终局1为胜走棋阶段被闷棋则为 -2/2布局阶段闷棋为 -3
struct Node* root; // 根节点
#endif /* DEBUG_AB_TREE */
#if 0
bool operator < (const Node &another) const
{
return this->value < another.value;
}
bool operator > (const Node &another) const
{
return this->value > another.value;
}
bool operator == (const Node &another) const
{
return this->value == another.value;
}
#endif
};
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
// 定义哈希值的类型
enum HashType
{
@ -120,7 +103,7 @@ public:
// 返回最佳走法的命令行
const char *bestMove();
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING))
// 清空哈希表
void clearHashMap();
#endif
@ -190,7 +173,7 @@ private:
NineChess::ChessContext *chessContext;
// hash计算时,各种转换用的模型
// hash 计算时,各种转换用的模型
NineChess chessTempShift;
// 根节点
@ -231,10 +214,6 @@ private:
private:
// 命令行
char cmdline[32];
#ifdef HASH_MAP_ENABLE
//HashMap<struct HashValue> hashmap;
#endif
};
#endif