增加 AI 随机走子的选项

去除 MOVE_PRIORITY_TABLE_SUPPORT 宏, 固定走这个宏的逻辑,
至于是否打乱着法则视 randomMove 而定.
This commit is contained in:
CalciteM Team 2019-09-07 11:21:26 +08:00
parent d8f9f375ab
commit 07e1c12f3a
9 changed files with 82 additions and 54 deletions

View File

@ -292,6 +292,7 @@
<addaction name="actionAnimation_A"/>
<addaction name="separator"/>
<addaction name="actionAutoRestart_A"/>
<addaction name="actionRandomMove_R"/>
</widget>
<widget class="QMenu" name="menu_H">
<property name="title">
@ -1101,6 +1102,17 @@
<string>自动重新开局(&amp;A)</string>
</property>
</action>
<action name="actionRandomMove_R">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>电脑着法随机(&amp;R)</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

View File

@ -81,8 +81,6 @@
//#define DONOT_DELETE_TREE
#define MOVE_PRIORITY_TABLE_SUPPORT
#define SORT_CONSIDER_PRUNED
//#define MESSAGEBOX_ENABLE

View File

@ -200,21 +200,21 @@ struct MillGameAi_ab::Node *MillGameAi_ab::addNode(
return newNode;
}
#ifdef MOVE_PRIORITY_TABLE_SUPPORT
#ifdef RANDOM_MOVE
void MillGameAi_ab::shuffleMovePriorityTable()
{
array<int, 4> movePriorityTable0 = { 17, 19, 21, 23 }; // 中圈四个顶点 (星位)
array<int, 8> movePriorityTable1 = { 25, 27, 29, 31, 9, 11, 13, 15 }; // 外圈和内圈四个顶点
array<int, 4> movePriorityTable2 = { 16, 18, 20, 22 }; // 中圈十字架
array<int, 8> movePriorityTable3 = { 8, 10, 12, 14, 24, 26, 28, 30 }; // 内外圈十字架
array<int, 8> movePriorityTable3 = { 24, 26, 28, 30, 8, 10, 12, 14 }; // 外内圈十字架
uint32_t seed = static_cast<uint32_t>(std::chrono::system_clock::now().time_since_epoch().count());
if (chess_.getRandomMove() == true) {
uint32_t seed = static_cast<uint32_t>(std::chrono::system_clock::now().time_since_epoch().count());
std::shuffle(movePriorityTable0.begin(), movePriorityTable0.end(), std::default_random_engine(seed));
std::shuffle(movePriorityTable1.begin(), movePriorityTable1.end(), std::default_random_engine(seed));
std::shuffle(movePriorityTable2.begin(), movePriorityTable2.end(), std::default_random_engine(seed));
std::shuffle(movePriorityTable3.begin(), movePriorityTable3.end(), std::default_random_engine(seed));
std::shuffle(movePriorityTable0.begin(), movePriorityTable0.end(), std::default_random_engine(seed));
std::shuffle(movePriorityTable1.begin(), movePriorityTable1.end(), std::default_random_engine(seed));
std::shuffle(movePriorityTable2.begin(), movePriorityTable2.end(), std::default_random_engine(seed));
std::shuffle(movePriorityTable3.begin(), movePriorityTable3.end(), std::default_random_engine(seed));
}
for (size_t i = 0; i < 4; i++) {
movePriorityTable[i + 0] = movePriorityTable0[i];
@ -232,8 +232,6 @@ void MillGameAi_ab::shuffleMovePriorityTable()
movePriorityTable[i + 16] = movePriorityTable3[i];
}
}
#endif // #ifdef RANDOM_MOVE
#endif // MOVE_PRIORITY_TABLE_SUPPORT
void MillGameAi_ab::generateLegalMoves(Node *node, move_t bestMove)
{
@ -273,27 +271,6 @@ void MillGameAi_ab::generateLegalMoves(Node *node, move_t bestMove)
node->children.reserve(newCapacity + 2 /* TODO: 未细调故再多留余量2 */);
#ifdef MOVE_PRIORITY_TABLE_SUPPORT
#ifdef RANDOM_MOVE
#else // RANDOM_MOVE
int movePriorityTable[MOVE_PRIORITY_TABLE_SIZE] = {
17, 19, 21, 23, // 星位
25, 27, 29, 31, // 外圈四个顶点
9, 11, 13, 15, // 内圈四个顶点
16, 18, 20, 22, // 中圈十字架
24, 26, 28, 30, // 外圈十字架
8, 10, 12, 14, // 中圈十字架
};
#endif // RANDOM_MOVE
#else // MOVE_PRIORITY_TABLE_SUPPORT
int movePriorityTable[MOVE_PRIORITY_TABLE_SIZE] = {
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
};
#endif // MOVE_PRIORITY_TABLE_SUPPORT
// 如果有子节点,则返回,避免重复建立
if (!node->children.empty()) {
return;
@ -331,12 +308,9 @@ void MillGameAi_ab::generateLegalMoves(Node *node, move_t bestMove)
// 对于移子阶段
if (chessTemp.context.stage & MillGame::GAME_MOVING) {
int newPos, oldPos;
#ifdef MOVE_PRIORITY_TABLE_SUPPORT
// 尽量走理论上较差的位置的棋子
for (int i = MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) {
#else
for (int i = 0; i < MOVE_PRIORITY_TABLE_SIZE; i++) {
#endif // MOVE_PRIORITY_TABLE_SUPPORT
oldPos = movePriorityTable[i];
if (!chessTemp.choose(oldPos)) {
@ -752,11 +726,8 @@ int MillGameAi_ab::alphaBetaPruning(depth_t depth)
}
#endif // THREEFOLD_REPETITION
#ifdef MOVE_PRIORITY_TABLE_SUPPORT
#ifdef RANDOM_MOVE
shuffleMovePriorityTable();
#endif // RANDOM_MOVE
#endif // MOVE_PRIORITY_TABLE_SUPPORT
// 随机打乱着法顺序
shuffleMovePriorityTable();
#ifdef IDS_SUPPORT
// 深化迭代

View File

@ -211,11 +211,7 @@ protected:
depth_t changeDepth(depth_t originalDepth);
// 随机打乱着法搜索顺序
#ifdef MOVE_PRIORITY_TABLE_SUPPORT
#ifdef RANDOM_MOVE
void shuffleMovePriorityTable();
#endif
#endif
#ifdef HASH_MAP_ENABLE
// 查找哈希表
@ -269,9 +265,12 @@ private:
// 标识,用于跳出剪枝算法,立即返回
bool requiredQuit {false};
#ifdef MOVE_PRIORITY_TABLE_SUPPORT
array<int, MillGame::N_RINGS *MillGame::N_SEATS> movePriorityTable {};
#endif // MOVE_PRIORITY_TABLE_SUPPORT
// 着法顺序表, 后续会被打乱
array<int, MillGame::N_RINGS *MillGame::N_SEATS> movePriorityTable {
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
};
// 定义极大值
static const value_t INF_VALUE = 0x1 << 14;

View File

@ -168,6 +168,7 @@ MillGame &MillGame::operator = (const MillGame &chess)
context = chess.context;
currentStep = chess.currentStep;
moveStep = chess.moveStep;
randomMove_ = chess.randomMove_;
board_ = context.board;
currentPos = chess.currentPos;
winner = chess.winner;
@ -211,7 +212,7 @@ MillGame::Player MillGame::getOpponent(MillGame::Player player)
void MillGame::createMoveTable()
{
#ifdef CONST_MOVE_TABLE
#ifdef MOVE_PRIORITY_TABLE_SUPPORT
#if 1
const int moveTable_obliqueLine[MillGame::N_POINTS][MillGame::N_MOVE_DIRECTIONS] = {
/* 0 */ {0, 0, 0, 0},
/* 1 */ {0, 0, 0, 0},
@ -399,7 +400,7 @@ void MillGame::createMoveTable()
/* 38 */ {0, 0, 0, 0},
/* 39 */ {0, 0, 0, 0},
};
#endif /* MOVE_PRIORITY_TABLE_SUPPORT */
#endif
if (currentRule.hasObliqueLines) {
memcpy(moveTable, moveTable_obliqueLine, sizeof(moveTable));
@ -662,10 +663,19 @@ void MillGame::createMillTable()
#endif
}
// 设置配置
bool MillGame::configure(bool randomMove)
{
// 设置是否随机走子
this->randomMove_ = randomMove;
return true;
}
// 设置棋局状态和棋盘数据,用于初始化
bool MillGame::setContext(const struct Rule *rule, step_t maxStepsLedToDraw, int maxTimeLedToLose,
step_t initialStep, int flags, const char *board,
int nPiecesInHand_1, int nPiecesInHand_2, int nPiecesNeedRemove)
step_t initialStep, int flags, const char *board,
int nPiecesInHand_1, int nPiecesInHand_2, int nPiecesNeedRemove)
{
// 有效性判断
if (maxTimeLedToLose < 0) {

View File

@ -303,6 +303,9 @@ public:
// 运算符重载
MillGame &operator=(const MillGame &);
// 设置配置
bool configure(bool randomMove);
// 设置棋局状态和棋盘上下文,用于初始化
bool setContext(const struct Rule *rule,
step_t maxStepsLedToDraw = 0, // 限制步数
@ -361,6 +364,12 @@ public:
return moveStep;
}
// 获取 AI 是否随机走子
bool getRandomMove() const
{
return randomMove_;
}
// 获取局面阶段标识
enum GameStage getStage() const
{
@ -567,6 +576,9 @@ private:
// 从走子阶段开始或上次吃子起的步数
int moveStep {};
// AI 是否随机走子
bool randomMove_ {true};
// 游戏起始时间
time_t startTime {};

View File

@ -54,7 +54,8 @@ GameController::GameController(GameScene & scene, QObject * parent) :
timeID(0),
ruleNo_(-1),
timeLimit(0),
stepsLimit(50)
stepsLimit(50),
randomMove_(true)
{
// 已在view的样式表中添加背景scene中不用添加背景
// 区别在于view中的背景不随视图变换而变换scene中的背景随视图变换而变换
@ -116,6 +117,7 @@ const QMap<int, QStringList> GameController::getActions()
void GameController::gameStart()
{
chess_.configure(randomMove_);
chess_.start();
chessTemp = chess_;
@ -141,6 +143,7 @@ void GameController::gameReset()
}
// 重置游戏
chess_.configure(randomMove_);
chess_.reset();
chessTemp = chess_;
@ -281,6 +284,8 @@ void GameController::setRule(int ruleNo, MillGame::step_t stepLimited /*= -1*/,
void GameController::setEngine1(bool arg)
{
chess_.configure(randomMove_);
isAiPlayer1 = arg;
if (arg) {
ai1.setAi(chess_);
@ -295,6 +300,8 @@ void GameController::setEngine1(bool arg)
void GameController::setEngine2(bool arg)
{
chess_.configure(randomMove_);
isAiPlayer2 = arg;
if (arg) {
ai2.setAi(chess_);
@ -369,6 +376,11 @@ void GameController::setAutoRestart(bool arg)
isAutoRestart = arg;
}
void GameController::setRandomMove(bool arg)
{
randomMove_ = arg;
}
// 上下翻转
void GameController::flip()
{

View File

@ -70,6 +70,11 @@ public:
return stepsLimit;
}
bool getRandomMove()
{
return randomMove_;
}
bool isAnimation()
{
return hasAnimation;
@ -148,6 +153,9 @@ public slots:
// 是否自动开局
void setAutoRestart(bool arg = false);
// AI 是否随机走子
void setRandomMove(bool arg);
// 上下翻转
void flip();
@ -235,6 +243,9 @@ private:
// 是否棋局结束后自动重新开局
bool isAutoRestart;
// AI 是否随机走子
bool randomMove_;
// 定时器ID
int timeID;

View File

@ -209,6 +209,9 @@ void MillGameWindow::initialize()
connect(ui.actionAutoRestart_A, SIGNAL(toggled(bool)),
game, SLOT(setAutoRestart(bool)));
connect(ui.actionRandomMove_R, SIGNAL(toggled(bool)),
game, SLOT(setRandomMove(bool)));
// 视图上下翻转
connect(ui.actionFlip_F, &QAction::triggered,
game, &GameController::flip);