NineChess模型类重构前的最后一次提交,Windows和Linux下都能正确编译。

下一步为AI做准备,改模型为位棋盘形式。
This commit is contained in:
liuweilhy 2018-12-01 00:14:41 +08:00
parent 022ae41475
commit 2e9a690df3
8 changed files with 375 additions and 319 deletions

4
.gitignore vendored
View File

@ -318,3 +318,7 @@ CMakeLists.txt.user*
# visual studio code
.vscode/
# markdown temp files
*.md.Html
*.md.Htm

View File

@ -15,6 +15,8 @@ TEMPLATE = app
CONFIG += C++11 \
warn_off
INCLUDEPATH += src
SOURCES += \
src/main.cpp \
src/boarditem.cpp \
@ -22,7 +24,7 @@ SOURCES += \
src/gamescene.cpp \
src/gameview.cpp \
src/ninechess.cpp \
src/ninechessai_ab.cpp \
src/ninechessai_ab.cpp \
src/ninechesswindow.cpp \
src/pieceitem.cpp \
src/aithread.cpp
@ -34,7 +36,7 @@ HEADERS += \
src/gameview.h \
src/graphicsconst.h \
src/ninechess.h \
src/ninechessai_ab.h \
src/ninechessai_ab.h \
src/ninechesswindow.h \
src/pieceitem.h \
src/manuallistview.h \

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.6.1, 2018-11-29T03:34:51. -->
<!-- Written by QtCreator 4.6.1, 2018-11-30T23:59:04. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
@ -292,7 +292,7 @@
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.ProFile">ninechess.pro</value>
<value type="bool" key="Qt4ProjectManager.Qt4RunConfiguration.UseDyldImageSuffix">false</value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory"></value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory.default"></value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory.default">E:/My program/QT/NineChess/build-ninechess-Desktop_Qt_5_11_0_MSVC2017_64bit-Debug</value>
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>

View File

@ -130,44 +130,44 @@ bool NineChess::setData(const struct Rule *rule, int s, int t, int step, int fla
this->rule.maxSteps = s;
this->rule.maxTime = t;
// 设置步数
data_.step = step;
this->step = step;
// 设置状态
// 局面阶段标识
if (flags & GAME_NOTSTARTED)
data_.phase = GAME_NOTSTARTED;
phase = GAME_NOTSTARTED;
else if (flags & GAME_OPENING)
data_.phase = GAME_OPENING;
phase = GAME_OPENING;
else if (flags & GAME_MID)
data_.phase = GAME_MID;
phase = GAME_MID;
else if (flags & GAME_OVER)
data_.phase = GAME_OVER;
phase = GAME_OVER;
else
return false;
// 轮流状态标识
if (flags & PLAYER1)
data_.turn = PLAYER1;
turn = PLAYER1;
else if (flags & PLAYER2)
data_.turn = PLAYER2;
turn = PLAYER2;
else
return false;
// 动作状态标识
if (flags & ACTION_CHOOSE)
data_.action = ACTION_CHOOSE;
action = ACTION_CHOOSE;
else if (flags & ACTION_PLACE)
data_.action = ACTION_PLACE;
action = ACTION_PLACE;
else if (flags & ACTION_REMOVE)
data_.action = ACTION_REMOVE;
action = ACTION_REMOVE;
else
return false;
// 胜负标识
data_.winner = NOBODY;
winner = NOBODY;
// 当前棋局3×8
if (boardsource == nullptr)
memset(data_.board, 0, sizeof(data_.board));
memset(this->board, 0, sizeof(this->board));
else
memcpy(data_.board, boardsource, sizeof(data_.board));
memcpy(this->board, boardsource, sizeof(this->board));
// 生成招法表
for (int i = 1; i <= RING; i++)
{
@ -257,38 +257,38 @@ bool NineChess::setData(const struct Rule *rule, int s, int t, int step, int fla
}
// 计算盘面子数
data_.player1_Remain = data_.player2_Remain = 0;
player1_Remain = player2_Remain = 0;
for (int i = 1; i < RING + 2; i++)
{
for (int j = 0; j < SEAT; j++)
{
if (data_.board[i*SEAT + j] & '\x10')
data_.player1_Remain++;
else if (data_.board[i*SEAT + j] & '\x20') {
data_.player2_Remain++;
if (board[i*SEAT + j] & '\x10')
player1_Remain++;
else if (board[i*SEAT + j] & '\x20') {
player2_Remain++;
}
}
}
// 设置玩家盘面剩余子数和未放置子数
if (data_.player1_Remain > rule->numOfChess || data_.player2_Remain > rule->numOfChess)
if (player1_Remain > rule->numOfChess || player2_Remain > rule->numOfChess)
return false;
if (p1_InHand < 0 || p2_InHand < 0)
return false;
data_.player1_InHand = rule->numOfChess - data_.player1_Remain;
data_.player2_InHand = rule->numOfChess - data_.player2_Remain;
data_.player1_InHand = p1_InHand < data_.player1_InHand ? p1_InHand : data_.player1_InHand;
data_.player2_InHand = p2_InHand < data_.player2_InHand ? p2_InHand : data_.player2_InHand;
player1_InHand = rule->numOfChess - player1_Remain;
player2_InHand = rule->numOfChess - player2_Remain;
player1_InHand = p1_InHand < player1_InHand ? p1_InHand : player1_InHand;
player2_InHand = p2_InHand < player2_InHand ? p2_InHand : player2_InHand;
// 设置去子状态时的剩余尚待去除子数
if (flags & ACTION_REMOVE) {
if (num_NeedRemove >= 0 && num_NeedRemove < 3)
data_.num_NeedRemove = num_NeedRemove;
this->num_NeedRemove = num_NeedRemove;
}
else
data_.num_NeedRemove = 0;
this->num_NeedRemove = 0;
// 清空成三记录
data_.millList.clear();
millList.clear();
// 不选中棋子
currentPos = 0;
@ -322,53 +322,53 @@ void NineChess::getData(struct Rule &rule, int &step, int &chess, const char *&b
int &p1_InHand, int &p2_InHand, int &num_NeedRemove)
{
rule = this->rule;
step = data_.step;
chess = data_.phase | data_.turn | data_.action | data_.winner;
board = data_.board;
p1_InHand = data_.player1_InHand;
p2_InHand = data_.player2_InHand;
num_NeedRemove = data_.num_NeedRemove;
step = this->step;
chess = phase | turn | action | winner;
board = this->board;
p1_InHand = player1_InHand;
p2_InHand = player2_InHand;
num_NeedRemove = this->num_NeedRemove;
}
const char * NineChess::getBoard()
{
return data_.board;
return board;
}
bool NineChess::reset()
{
if (data_.phase == GAME_NOTSTARTED && player1_MS == player2_MS == 0)
if (phase == GAME_NOTSTARTED && player1_MS == player2_MS == 0)
return true;
// 步数归零
data_.step = 0;
step = 0;
// 局面阶段标识
data_.phase = GAME_NOTSTARTED;
phase = GAME_NOTSTARTED;
// 轮流状态标识
data_.turn = PLAYER1;
turn = PLAYER1;
// 动作状态标识
data_.action = ACTION_PLACE;
action = ACTION_PLACE;
// 胜负标识
data_.winner = NOBODY;
winner = NOBODY;
// 当前棋局3×8
memset(data_.board, 0, sizeof(data_.board));
memset(board, 0, sizeof(board));
// 盘面子数归零
data_.player1_Remain = data_.player2_Remain = 0;
player1_Remain = player2_Remain = 0;
// 设置玩家盘面剩余子数和未放置子数
data_.player1_InHand = data_.player2_InHand = rule.numOfChess;
player1_InHand = player2_InHand = rule.numOfChess;
// 设置去子状态时的剩余尚待去除子数
data_.num_NeedRemove = 0;
num_NeedRemove = 0;
// 清空成三记录
data_.millList.clear();
millList.clear();
// 不选中棋子
currentPos = 0;
@ -401,16 +401,16 @@ bool NineChess::reset()
bool NineChess::start()
{
// 如果游戏已经开始则返回false
if (data_.phase == GAME_OPENING || data_.phase == GAME_MID)
if (phase == GAME_OPENING || phase == GAME_MID)
return false;
// 如果游戏结束,则重置游戏,进入未开始状态
if (data_.phase == GAME_OVER)
if (phase == GAME_OVER)
reset();
// 如果游戏处于未开始状态
if (data_.phase == GAME_NOTSTARTED) {
data_.phase = GAME_OPENING;
if (phase == GAME_NOTSTARTED) {
phase = GAME_OPENING;
// 启动计时器
ftime(&startTimeb);
}
@ -429,18 +429,18 @@ int NineChess::cp2pos(int c, int p)
bool NineChess::place(int c, int p, long time_p /* = -1*/)
{
// 如果局面为“结局”返回false
if (data_.phase == GAME_OVER)
if (phase == GAME_OVER)
return false;
// 如果局面为“未开局”,则开具
if (data_.phase == GAME_NOTSTARTED)
if (phase == GAME_NOTSTARTED)
start();
// 如非“落子”状态返回false
if (data_.action != ACTION_PLACE)
if (action != ACTION_PLACE)
return false;
// 如果落子位置在棋盘外、已有子点或禁点返回false
int pos = cp2pos(c, p);
if (!inBoard[pos] || data_.board[pos])
if (!inBoard[pos] || board[pos])
return false;
// 时间的临时变量
long player_ms = -1;
@ -448,29 +448,28 @@ bool NineChess::place(int c, int p, long time_p /* = -1*/)
// 对于开局落子
char piece = '\x00';
int n = 0;
if (data_.phase == GAME_OPENING) {
if (phase == GAME_OPENING) {
// 先手下
if (data_.turn == PLAYER1)
if (turn == PLAYER1)
{
piece = '\x11' + rule.numOfChess - data_.player1_InHand;
data_.board[pos] = piece;
data_.player1_InHand--;
data_.player1_Remain++;
piece = '\x11' + rule.numOfChess - player1_InHand;
board[pos] = piece;
player1_InHand--;
player1_Remain++;
}
// 后手下
else
{
piece = '\x21' + rule.numOfChess - data_.player2_InHand;
data_.board[pos] = piece;
data_.player2_InHand--;
data_.player2_Remain++;
piece = '\x21' + rule.numOfChess - player2_InHand;
board[pos] = piece;
player2_InHand--;
player2_Remain++;
}
player_ms = update(time_p);
data_.move_ = pos;
sprintf(cmdline, "(%1u,%1u) %02u:%02u.%03u", c, p, player_ms / 60000, player_ms / 1000, player_ms % 1000);
cmdlist.push_back(string(cmdline));
currentPos = pos;
data_.step++;
step++;
// 如果决出胜负
if (win()) {
setTip();
@ -481,19 +480,19 @@ bool NineChess::place(int c, int p, long time_p /* = -1*/)
// 开局阶段未成三
if (n == 0) {
// 如果双方都无未放置的棋子
if (data_.player1_InHand == 0 && data_.player2_InHand == 0) {
if (player1_InHand == 0 && player2_InHand == 0) {
// 进入中局阶段
data_.phase = GAME_MID;
phase = GAME_MID;
// 进入选子状态
data_.action = ACTION_CHOOSE;
action = ACTION_CHOOSE;
// 清除禁点
cleanForbidden();
// 设置轮到谁走
if (rule.isDefensiveMoveFirst) {
data_.turn = PLAYER2;
turn = PLAYER2;
}
else {
data_.turn = PLAYER1;
turn = PLAYER1;
}
// 再决胜负
if (win()) {
@ -510,19 +509,19 @@ bool NineChess::place(int c, int p, long time_p /* = -1*/)
// 如果成三
else {
// 设置去子数目
data_.num_NeedRemove = rule.removeMore ? n : 1;
num_NeedRemove = rule.removeMore ? n : 1;
// 进入去子状态
data_.action = ACTION_REMOVE;
action = ACTION_REMOVE;
}
setTip();
return true;
}
// 对于中局落子
else if (data_.phase == GAME_MID) {
else if (phase == GAME_MID) {
// 如果落子不合法
if ((data_.turn == PLAYER1 && (data_.player1_Remain > rule.numAtLest || !rule.canFly)) ||
(data_.turn == PLAYER2 && (data_.player2_Remain > rule.numAtLest || !rule.canFly))) {
if ((turn == PLAYER1 && (player1_Remain > rule.numAtLest || !rule.canFly)) ||
(turn == PLAYER2 && (player2_Remain > rule.numAtLest || !rule.canFly))) {
int i;
for (i = 0; i < 4; i++) {
if (pos == moveTable[currentPos][i])
@ -534,20 +533,19 @@ bool NineChess::place(int c, int p, long time_p /* = -1*/)
}
// 移子
player_ms = update(time_p);
data_.move_ = (currentPos << 8) | pos;
sprintf(cmdline, "(%1u,%1u)->(%1u,%1u) %02u:%02u.%03u", currentPos / SEAT, currentPos % SEAT + 1,
c, p, player_ms / 60000, player_ms / 1000, player_ms % 1000);
cmdlist.push_back(string(cmdline));
data_.board[pos] = data_.board[currentPos];
data_.board[currentPos] = '\x00';
board[pos] = board[currentPos];
board[currentPos] = '\x00';
currentPos = pos;
data_.step++;
step++;
n = addMills(currentPos);
// 中局阶段未成三
if (n == 0) {
// 进入选子状态
data_.action = ACTION_CHOOSE;
action = ACTION_CHOOSE;
// 设置轮到谁走
changeTurn();
// 如果决出胜负
@ -559,9 +557,9 @@ bool NineChess::place(int c, int p, long time_p /* = -1*/)
// 中局阶段成三
else {
// 设置去子数目
data_.num_NeedRemove = rule.removeMore ? n : 1;
num_NeedRemove = rule.removeMore ? n : 1;
// 进入去子状态
data_.action = ACTION_REMOVE;
action = ACTION_REMOVE;
setTip();
}
setTip();
@ -574,20 +572,20 @@ bool NineChess::place(int c, int p, long time_p /* = -1*/)
bool NineChess::remove(int c, int p, long time_p /* = -1*/)
{
// 如果局面为"未开局"或“结局”返回false
if (data_.phase == GAME_NOTSTARTED || data_.phase == GAME_OVER)
if (phase == GAME_NOTSTARTED || phase == GAME_OVER)
return false;
// 如非“去子”状态返回false
if (data_.action != ACTION_REMOVE)
if (action != ACTION_REMOVE)
return false;
// 如果去子完成返回false
if (data_.num_NeedRemove <= 0)
if (num_NeedRemove <= 0)
return false;
// 时间的临时变量
long player_ms = -1;
int pos = cp2pos(c, p);
// 对手
enum Player opponent = PLAYER2;
if (data_.turn == PLAYER2)
if (turn == PLAYER2)
opponent = PLAYER1;
// 判断去子不是对手棋
if (getWhosPiece(c, p) != opponent)
@ -599,21 +597,20 @@ bool NineChess::remove(int c, int p, long time_p /* = -1*/)
}
// 去子(设置禁点)
if (rule.hasForbidden && data_.phase == GAME_OPENING)
data_.board[pos] = '\x0f';
if (rule.hasForbidden && phase == GAME_OPENING)
board[pos] = '\x0f';
else // 去子
data_.board[pos] = '\x00';
if (data_.turn == PLAYER1)
data_.player2_Remain--;
else if (data_.turn == PLAYER2)
data_.player1_Remain--;
board[pos] = '\x00';
if (turn == PLAYER1)
player2_Remain--;
else if (turn == PLAYER2)
player1_Remain--;
player_ms = update(time_p);
data_.move_ = 0xFF00 | pos;
sprintf(cmdline, "-(%1u,%1u) %02u:%02u.%03u", c, p, player_ms / 60000, player_ms / 1000, player_ms % 1000);
cmdlist.push_back(string(cmdline));
currentPos = 0;
data_.num_NeedRemove--;
data_.step++;
num_NeedRemove--;
step++;
// 去子完成
// 如果决出胜负
@ -622,28 +619,28 @@ bool NineChess::remove(int c, int p, long time_p /* = -1*/)
return true;
}
// 还有其余的子要去吗
if (data_.num_NeedRemove > 0) {
if (num_NeedRemove > 0) {
// 继续去子
return true;
}
// 所有去子都完成了
else {
// 开局阶段
if (data_.phase == GAME_OPENING) {
if (phase == GAME_OPENING) {
// 如果双方都无未放置的棋子
if (data_.player1_InHand == 0 && data_.player2_InHand == 0) {
if (player1_InHand == 0 && player2_InHand == 0) {
// 进入中局阶段
data_.phase = GAME_MID;
phase = GAME_MID;
// 进入选子状态
data_.action = ACTION_CHOOSE;
action = ACTION_CHOOSE;
// 清除禁点
cleanForbidden();
// 设置轮到谁走
if (rule.isDefensiveMoveFirst) {
data_.turn = PLAYER2;
turn = PLAYER2;
}
else {
data_.turn = PLAYER1;
turn = PLAYER1;
}
// 再决胜负
if (win()) {
@ -654,7 +651,7 @@ bool NineChess::remove(int c, int p, long time_p /* = -1*/)
// 如果双方还有子
else {
// 进入落子状态
data_.action = ACTION_PLACE;
action = ACTION_PLACE;
// 设置轮到谁走
changeTurn();
// 如果决出胜负
@ -667,7 +664,7 @@ bool NineChess::remove(int c, int p, long time_p /* = -1*/)
// 中局阶段
else {
// 进入选子状态
data_.action = ACTION_CHOOSE;
action = ACTION_CHOOSE;
// 设置轮到谁走
changeTurn();
// 如果决出胜负
@ -684,20 +681,20 @@ bool NineChess::remove(int c, int p, long time_p /* = -1*/)
bool NineChess::choose(int c, int p)
{
// 如果局面不是"中局”返回false
if (data_.phase != GAME_MID)
if (phase != GAME_MID)
return false;
// 如非“选子”或“落子”状态返回false
if (data_.action != ACTION_CHOOSE && data_.action != ACTION_PLACE)
if (action != ACTION_CHOOSE && action != ACTION_PLACE)
return false;
int pos = cp2pos(c, p);
// 根据先后手,判断可选子
char t ='\0';
if (data_.turn == PLAYER1)
if (turn == PLAYER1)
t = '\x10';
else if (data_.turn == PLAYER2)
else if (turn == PLAYER2)
t = '\x20';
// 判断选子是否可选
if (data_.board[pos] & t) {
if (board[pos] & t) {
// 判断pos处的棋子是否被“闷”
if (isSurrounded(pos)) {
return false;
@ -705,7 +702,7 @@ bool NineChess::choose(int c, int p)
// 选子
currentPos = pos;
// 选子完成,进入落子状态
data_.action = ACTION_PLACE;
action = ACTION_PLACE;
return true;
}
return false;
@ -713,12 +710,12 @@ bool NineChess::choose(int c, int p)
bool NineChess::giveup(Player loser)
{
if (data_.phase == GAME_MID || data_.phase == GAME_OPENING)
if (phase == GAME_MID || phase == GAME_OPENING)
{
if (loser == PLAYER1)
{
data_.phase = GAME_OVER;
data_.winner = PLAYER2;
phase = GAME_OVER;
winner = PLAYER2;
tip = "玩家1投子认负恭喜玩家2获胜";
sprintf(cmdline, "Player1 give up!");
cmdlist.push_back(string(cmdline));
@ -726,8 +723,8 @@ bool NineChess::giveup(Player loser)
}
else if (loser == PLAYER2)
{
data_.phase = GAME_OVER;
data_.winner = PLAYER1;
phase = GAME_OVER;
winner = PLAYER1;
tip = "玩家2投子认负恭喜玩家1获胜";
sprintf(cmdline, "Player2 give up!");
cmdlist.push_back(string(cmdline));
@ -804,11 +801,11 @@ bool NineChess::command(const char *cmd)
inline long NineChess::update(long time_p /*= -1*/)
{
long ret = -1;
long *player_ms = (data_.turn == PLAYER1 ? &(player1_MS) : &(player2_MS));
long playerNext_ms = (data_.turn == PLAYER1 ? player2_MS : player1_MS);
long *player_ms = (turn == PLAYER1 ? &player1_MS : &player2_MS);
long playerNext_ms = (turn == PLAYER1 ? player2_MS : player1_MS);
// 根据局面调整计时器
switch (data_.phase)
switch (phase)
{
case NineChess::GAME_OPENING:
case NineChess::GAME_MID:
@ -850,9 +847,9 @@ inline long NineChess::update(long time_p /*= -1*/)
// 是否分出胜负
bool NineChess::win()
{
if (data_.phase == GAME_OVER)
if (phase == GAME_OVER)
return true;
if (data_.phase == GAME_NOTSTARTED)
if (phase == GAME_NOTSTARTED)
return false;
// 如果有时间限定
@ -861,8 +858,8 @@ bool NineChess::win()
// 如果玩家1超时
if (player1_MS > rule.maxTime * 60000) {
player1_MS = rule.maxTime * 60000;
data_.winner = PLAYER2;
data_.phase = GAME_OVER;
winner = PLAYER2;
phase = GAME_OVER;
tip = "玩家1超时恭喜玩家2获胜";
sprintf(cmdline, "Time over. Player2 win!");
cmdlist.push_back(string(cmdline));
@ -871,8 +868,8 @@ bool NineChess::win()
// 如果玩家2超时
else if (player2_MS > rule.maxTime * 60000) {
player2_MS = rule.maxTime * 60000;
data_.winner = PLAYER1;
data_.phase = GAME_OVER;
winner = PLAYER1;
phase = GAME_OVER;
tip = "玩家2超时恭喜玩家1获胜";
sprintf(cmdline, "Time over. Player1 win!");
cmdlist.push_back(string(cmdline));
@ -882,9 +879,9 @@ bool NineChess::win()
// 如果有步数限定
if (rule.maxSteps > 0) {
if (data_.step > rule.maxSteps) {
data_.winner = DRAW;
data_.phase = GAME_OVER;
if (step > rule.maxSteps) {
winner = DRAW;
phase = GAME_OVER;
sprintf(cmdline, "Steps over. In draw!");
cmdlist.push_back(string(cmdline));
return true;
@ -892,48 +889,48 @@ bool NineChess::win()
}
// 如果玩家1子数小于赛点则玩家2获胜
if (data_.player1_Remain + data_.player1_InHand < rule.numAtLest) {
data_.winner = PLAYER2;
data_.phase = GAME_OVER;
if (player1_Remain + player1_InHand < rule.numAtLest) {
winner = PLAYER2;
phase = GAME_OVER;
sprintf(cmdline, "Player2 win!");
cmdlist.push_back(string(cmdline));
return true;
}
// 如果玩家2子数小于赛点则玩家1获胜
else if (data_.player2_Remain + data_.player2_InHand < rule.numAtLest) {
data_.winner = PLAYER1;
data_.phase = GAME_OVER;
else if (player2_Remain + player2_InHand < rule.numAtLest) {
winner = PLAYER1;
phase = GAME_OVER;
sprintf(cmdline, "Player1 win!");
cmdlist.push_back(string(cmdline));
return true;
}
// 如果摆满了,根据规则判断胜负
else if (data_.player1_Remain + data_.player2_Remain >= SEAT * RING) {
else if (player1_Remain + player2_Remain >= SEAT * RING) {
if (rule.isFullLose) {
data_.winner = PLAYER2;
data_.phase = GAME_OVER;
winner = PLAYER2;
phase = GAME_OVER;
sprintf(cmdline, "Player2 win!");
cmdlist.push_back(string(cmdline));
return true;
}
else
{
data_.winner = DRAW;
data_.phase = GAME_OVER;
winner = DRAW;
phase = GAME_OVER;
sprintf(cmdline, "Full. In draw!");
cmdlist.push_back(string(cmdline));
return true;
}
}
// 如果中局被“闷”
else if (data_.phase == GAME_MID && data_.action == ACTION_CHOOSE && isAllSurrounded(data_.turn)) {
else if (phase == GAME_MID && action == ACTION_CHOOSE && isAllSurrounded(turn)) {
// 规则要求被“闷”判负,则对手获胜
if (rule.isNoWayLose) {
if (data_.turn == PLAYER1)
if (turn == PLAYER1)
{
tip = "玩家1无子可走恭喜玩家2获胜";
data_.winner = PLAYER2;
data_.phase = GAME_OVER;
winner = PLAYER2;
phase = GAME_OVER;
sprintf(cmdline, "Player1 no way to go. Player2 win!");
cmdlist.push_back(string(cmdline));
return true;
@ -941,8 +938,8 @@ bool NineChess::win()
else
{
tip = "玩家2无子可走恭喜玩家1获胜";
data_.winner = PLAYER1;
data_.phase = GAME_OVER;
winner = PLAYER1;
phase = GAME_OVER;
sprintf(cmdline, "Player2 no way to go. Player1 win!");
cmdlist.push_back(string(cmdline));
return true;
@ -963,12 +960,12 @@ int NineChess::isInMills(int pos)
{
int n = 0;
int pos1, pos2;
char m = data_.board[pos] & '\x30';
char m = board[pos] & '\x30';
for (int i = 0; i < 3; i++)
{
pos1 = millTable[pos][i][0];
pos2 = millTable[pos][i][1];
if (m & data_.board[pos1] & data_.board[pos2])
if (m & board[pos1] & board[pos2])
n++;
}
return n;
@ -983,14 +980,14 @@ int NineChess::addMills(int pos)
long long mill = 0;
int n = 0;
int p[3], min, temp;
char m = data_.board[pos] & '\x30';
char m = board[pos] & '\x30';
for (int i = 0; i < 3; i++)
{
p[0] = pos;
p[1] = millTable[pos][i][0];
p[2] = millTable[pos][i][1];
// 如果成三
if (m & data_.board[p[1]] & data_.board[p[2]]) {
if (m & board[p[1]] & board[p[2]]) {
// 排序
for (int j = 0; j < 2; j++) {
min = j;
@ -1005,11 +1002,11 @@ int NineChess::addMills(int pos)
}
}
// 成三
mill = (((long long)(data_.board[p[0]])) << 40)
mill = (((long long)board[p[0]]) << 40)
+ (((long long)p[0]) << 32)
+ (((long long)(data_.board[p[1]])) << 24)
+ (((long long)board[p[1]]) << 24)
+ (((long long)p[1]) << 16)
+ (((long long)(data_.board[p[2]])) << 8)
+ (((long long)board[p[2]]) << 8)
+ (long long)p[2];
// 如果允许相同三连反复去子
@ -1022,15 +1019,15 @@ int NineChess::addMills(int pos)
// 迭代器
list<long long>::iterator itor;
// 遍历
for (itor = data_.millList.begin(); itor != data_.millList.end(); itor++)
for (itor = millList.begin(); itor != millList.end(); itor++)
{
if (mill == *itor)
break;
}
// 如果没找到历史项
if (itor == data_.millList.end()) {
if (itor == millList.end()) {
n++;
data_.millList.push_back(mill);
millList.push_back(mill);
}
}
}
@ -1050,7 +1047,7 @@ bool NineChess::isAllInMills(enum Player player)
for (int i = 1; i <= RING; i++)
for (int j = 0; j < SEAT; j++) {
if (data_.board[i*SEAT + j] & ch) {
if (board[i*SEAT + j] & ch) {
if (!isInMills(i*SEAT + j)) {
return false;
}
@ -1063,13 +1060,13 @@ bool NineChess::isAllInMills(enum Player player)
bool NineChess::isSurrounded(int pos)
{
// 判断pos处的棋子是否被“闷”
if ((data_.turn == PLAYER1 && (data_.player1_Remain > rule.numAtLest || !rule.canFly)) ||
(data_.turn == PLAYER2 && (data_.player2_Remain > rule.numAtLest || !rule.canFly)))
if ((turn == PLAYER1 && (player1_Remain > rule.numAtLest || !rule.canFly)) ||
(turn == PLAYER2 && (player2_Remain > rule.numAtLest || !rule.canFly)))
{
int i, movePos;
for (i = 0; i < 4; i++) {
movePos = moveTable[pos][i];
if (movePos && !data_.board[movePos])
if (movePos && !board[movePos])
break;
}
// 被围住
@ -1089,11 +1086,11 @@ bool NineChess::isAllSurrounded(enum Player ply)
else if (ply == PLAYER2)
t &= '\x20';
// 如果摆满
if (data_.player1_Remain + data_.player2_Remain >= SEAT * RING)
if (player1_Remain + player2_Remain >= SEAT * RING)
return true;
// 判断是否可以飞子
if ((data_.turn == PLAYER1 && (data_.player1_Remain <= rule.numAtLest && rule.canFly)) ||
(data_.turn == PLAYER2 && (data_.player2_Remain <= rule.numAtLest && rule.canFly)))
if ((turn == PLAYER1 && (player1_Remain <= rule.numAtLest && rule.canFly)) ||
(turn == PLAYER2 && (player2_Remain <= rule.numAtLest && rule.canFly)))
{
return false;
}
@ -1103,10 +1100,10 @@ bool NineChess::isAllSurrounded(enum Player ply)
for (int j = 0; j < SEAT; j++)
{
int movePos;
if (t & data_.board[i*SEAT + j]) {
if (t & board[i*SEAT + j]) {
for (int k = 0; k < 4; k++) {
movePos = moveTable[i*SEAT + j][k];
if (movePos && !data_.board[movePos])
if (movePos && !board[movePos])
return false;
}
}
@ -1119,70 +1116,70 @@ void NineChess::cleanForbidden()
{
for (int i = 1; i <= RING; i++)
for (int j = 0; j < SEAT; j++) {
if (data_.board[i*SEAT + j] == '\x0f')
data_.board[i*SEAT + j] = '\x00';
if (board[i*SEAT + j] == '\x0f')
board[i*SEAT + j] = '\x00';
}
}
enum NineChess::Player NineChess::changeTurn()
{
// 设置轮到谁走
return data_.turn = (data_.turn == PLAYER1) ? PLAYER2 : PLAYER1;
return turn = (turn == PLAYER1) ? PLAYER2 : PLAYER1;
}
void NineChess::setTip()
{
switch (data_.phase)
switch (phase)
{
case NineChess::GAME_NOTSTARTED:
tip = "轮到玩家1落子剩余" + std::to_string(data_.player1_InHand) + "";
tip = "轮到玩家1落子剩余" + std::to_string(player1_InHand) + "";
break;
case NineChess::GAME_OPENING:
if (data_.action == ACTION_PLACE) {
if (data_.turn == PLAYER1) {
tip = "轮到玩家1落子剩余" + std::to_string(data_.player1_InHand) + "";
if (action == ACTION_PLACE) {
if (turn == PLAYER1) {
tip = "轮到玩家1落子剩余" + std::to_string(player1_InHand) + "";
}
else if (data_.turn == PLAYER2) {
tip = "轮到玩家2落子剩余" + std::to_string(data_.player2_InHand) + "";
else if (turn == PLAYER2) {
tip = "轮到玩家2落子剩余" + std::to_string(player2_InHand) + "";
}
}
else if (data_.action == ACTION_REMOVE) {
if (data_.turn == PLAYER1) {
tip = "轮到玩家1去子需去" + std::to_string(data_.num_NeedRemove) + "";
else if (action == ACTION_REMOVE) {
if (turn == PLAYER1) {
tip = "轮到玩家1去子需去" + std::to_string(num_NeedRemove) + "";
}
else if (data_.turn == PLAYER2) {
tip = "轮到玩家2去子需去" + std::to_string(data_.num_NeedRemove) + "";
else if (turn == PLAYER2) {
tip = "轮到玩家2去子需去" + std::to_string(num_NeedRemove) + "";
}
}
break;
case NineChess::GAME_MID:
if (data_.action == ACTION_PLACE || data_.action == ACTION_CHOOSE) {
if (data_.turn == PLAYER1) {
if (action == ACTION_PLACE || action == ACTION_CHOOSE) {
if (turn == PLAYER1) {
tip = "轮到玩家1选子移动";
}
else if (data_.turn == PLAYER2) {
else if (turn == PLAYER2) {
tip = "轮到玩家2选子移动";
}
}
else if (data_.action == ACTION_REMOVE) {
if (data_.turn == PLAYER1) {
tip = "轮到玩家1去子需去" + std::to_string(data_.num_NeedRemove) + "";
else if (action == ACTION_REMOVE) {
if (turn == PLAYER1) {
tip = "轮到玩家1去子需去" + std::to_string(num_NeedRemove) + "";
}
else if (data_.turn == PLAYER2) {
tip = "轮到玩家2去子需去" + std::to_string(data_.num_NeedRemove) + "";
else if (turn == PLAYER2) {
tip = "轮到玩家2去子需去" + std::to_string(num_NeedRemove) + "";
}
}
break;
case NineChess::GAME_OVER:
if (data_.winner == DRAW)
if (winner == DRAW)
tip = "超出限定步数,双方平局";
else if (data_.winner == PLAYER1) {
else if (winner == PLAYER1) {
if (tip.find("无子可走") != tip.npos)
tip += "恭喜玩家1获胜";
else
tip = "恭喜玩家1获胜";
}
else if (data_.winner == PLAYER2) {
else if (winner == PLAYER2) {
if (tip.find("无子可走") != tip.npos)
tip += "恭喜玩家2获胜";
else
@ -1197,9 +1194,9 @@ void NineChess::setTip()
enum NineChess::Player NineChess::getWhosPiece(int c, int p)
{
int pos = cp2pos(c, p);
if (data_.board[pos] & '\x10')
if (board[pos] & '\x10')
return PLAYER1;
else if (data_.board[pos] & '\x20')
else if (board[pos] & '\x20')
return PLAYER2;
return NOBODY;
}
@ -1207,7 +1204,7 @@ enum NineChess::Player NineChess::getWhosPiece(int c, int p)
int NineChess::getPieceNum(int c, int p)
{
int pos = cp2pos(c, p);
int n = 0x0f & data_.board[pos];
int n = 0x0f & board[pos];
return n;
}

View File

@ -87,68 +87,6 @@ public:
// 预定义的规则
static const struct Rule RULES[RULENUM];
// 嵌套的数据结构体
// 单独提出来是为了ai计算的时候压栈的数据量少
struct Data
{
// 当前步数
int step;
// 局面阶段标识
enum Phases phase;
// 轮流状态标识
enum Player turn;
// 动作状态标识
enum Actions action;
// 赢家
enum Player winner;
// 玩家1剩余未放置子数
int player1_InHand;
// 玩家2剩余未放置子数
int player2_InHand;
// 玩家1盘面剩余子数
int player1_Remain;
// 玩家1盘面剩余子数
int player2_Remain;
// 尚待去除的子数
int num_NeedRemove;
/* 棋局抽象为一个5×8的char数组上下两行留空
* 0x00
* 0x0F
* 0x110x1C112
* 0x210x2c112
* (board[i] & 0x10)
* (board[i] & 0x20)
*/
char board[(NineChess::RING + 2)*NineChess::SEAT];
/* 本打算用如下的结构体来表示“三连”
struct Mill {
char piece1; // “三连”中最小的棋子
char pos1; // 最小棋子的位置
char piece2; // 次小的棋子
char pos2; // 次小棋子的位置
char piece3; // 最大的棋子
char pos3; // 最大棋子的位置
};
64
0x 00 00 00 00 00 00 00 00
unused unused piece1 pos1 piece2 pos2 piece3 pos3
*/
// “三连列表”
list <long long> millList;
/* 当前招法AI会用到如下表示
0x 00 00
pos1 pos2
0x00????
0x__??__为移动前的位置??
0xFF????
*/
short move_;
};
private:
// 空棋盘点位,用于判断一个棋子位置是否在棋盘上
static const char inBoard[(RING + 2)*SEAT];
@ -191,15 +129,15 @@ public:
// 获取当前点
int getCurrentPos() { return currentPos; }
// 获取当前步数
int getStep() { return data_.step; }
int getStep() { return step; }
// 获取局面阶段标识
enum Phases getPhase() { return data_.phase; }
enum Phases getPhase() { return phase; }
// 获取轮流状态标识
enum Player whosTurn() { return data_.turn; }
enum Player whosTurn() { return turn; }
// 获取动作状态标识
enum Actions getAction() { return data_.action; }
enum Actions getAction() { return action; }
// 判断胜负
enum Player whoWin() { return data_.winner; }
enum Player whoWin() { return winner; }
// 玩家1和玩家2的用时
void getPlayer_TimeMS(int &p1_ms, int &p2_ms);
// 获取棋局的字符提示
@ -218,15 +156,15 @@ public:
void setStartTimeb(timeb stimeb) { startTimeb = stimeb; }
// 玩家1剩余未放置子数
int getPlayer1_InHand() { return data_.player1_InHand; }
int getPlayer1_InHand() { return player1_InHand; }
// 玩家2剩余未放置子数
int getPlayer2_InHand() { return data_.player2_InHand; }
int getPlayer2_InHand() { return player2_InHand; }
// 玩家1盘面剩余子数
int getPlayer1_Remain() { return data_.player1_Remain; }
int getPlayer1_Remain() { return player1_Remain; }
// 玩家1盘面剩余子数
int getPlayer2_Remain() { return data_.player2_Remain; }
int getPlayer2_Remain() { return player2_Remain; }
// 尚待去除的子数
int getNum_NeedRemove() { return data_.num_NeedRemove; }
int getNum_NeedRemove() { return num_NeedRemove; }
// 游戏重置
bool reset();
@ -268,10 +206,65 @@ protected:
void setTip();
private:
// 当前步数
int step;
// 局面阶段标识
enum Phases phase;
// 轮流状态标识
enum Player turn;
// 动作状态标识
enum Actions action;
// 赢家
enum Player winner;
// 玩家1剩余未放置子数
int player1_InHand;
// 玩家2剩余未放置子数
int player2_InHand;
// 玩家1盘面剩余子数
int player1_Remain;
// 玩家1盘面剩余子数
int player2_Remain;
// 尚待去除的子数
int num_NeedRemove;
/* 棋局抽象为一个5×8的char数组上下两行留空
* 0x00
* 0x0F
* 0x110x1C112
* 0x210x2c112
* (board[i] & 0x10)
* (board[i] & 0x20)
*/
char board[(NineChess::RING + 2)*NineChess::SEAT];
/* 本打算用如下的结构体来表示“三连”
struct Mill {
char piece1; // “三连”中最小的棋子
char pos1; // 最小棋子的位置
char piece2; // 次小的棋子
char pos2; // 次小棋子的位置
char piece3; // 最大的棋子
char pos3; // 最大棋子的位置
};
64
0x 00 00 00 00 00 00 00 00
unused unused piece1 pos1 piece2 pos2 piece3 pos3
*/
// “三连列表”
list <long long> millList;
/* 当前招法AI会用到如下表示
0x 00 00
pos1 pos2
0x00????
0x__??__为移动前的位置??
0xFF????
*/
short move_;
// 当前使用的规则
struct Rule rule;
// 棋盘数据
struct Data data_;
// 选中的棋子在board中的位置
int currentPos;

View File

@ -1,10 +1,16 @@
#include "NineChessAi_ab.h"
#include "ninechessai_ab.h"
#include <cmath>
#include <time.h>
NineChessAi_ab::NineChessAi_ab():
rootNode(nullptr),
requiredQuit(false),
depth(5) // 默认5层深度
depth(3) // 默认3层深度
{
rootNode = new Node;
rootNode->value = 0;
rootNode->move_ = 0;
rootNode->parent = nullptr;
}
NineChessAi_ab::~NineChessAi_ab()
@ -14,25 +20,30 @@ NineChessAi_ab::~NineChessAi_ab()
void NineChessAi_ab::buildChildren(Node *node)
{
// 列出所有合法的下一招
;
}
void NineChessAi_ab::sortChildren(Node *node)
{
// 这个函数对效率的影响很大,
// 排序好的话,剪枝较早,节省时间
// 但不能在此函数耗费太多时间
;
// 这个函数对效率的影响很大,排序好的话,剪枝较早,节省时间,但不能在此函数耗费太多时间
// 先赋初值初始值不会影响alpha-beta剪枝
for (auto i : node->children) {
i->value = evaluate(node);
}
// 排序
node->children.sort([](Node *n1, Node *n2) { return n1->value > n2->value; });
}
void NineChessAi_ab::deleteTree(Node *node)
{
if (rootNode) {
for (auto i : rootNode->children) {
// 递归删除节点树
if (node) {
for (auto i : node->children) {
deleteTree(i);
}
rootNode->children.clear();
delete rootNode;
node->children.clear();
delete node;
}
}
@ -45,63 +56,100 @@ void NineChessAi_ab::setChess(const NineChess &chess)
int NineChessAi_ab::evaluate(Node *node)
{
// 初始评估值为0对先手有利则增大对后手有利则减小
// 初始评估值为0对先手有利则增大对后手有利则减小
int value = 0;
// 赋值返回
// 赋值返回
node->value = value;
return value;
}
int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
{
// 评价值
// 评价值
int value;
if (!depth || !(node->children.size())) {
node->value = evaluate(node);
return node->value;
}
// 生成子节点树
// 生成子节点树
buildChildren(node);
// 排序子节点树
// 排序子节点树
sortChildren(node);
// 根据演算模型执行MiniMax检索
// 对先手搜索Max
// 根据演算模型执行MiniMax检索
// 对先手搜索Max
if (chessTemp.whosTurn() == NineChess::PLAYER1) {
for (auto child : node->children) {
value = alphaBetaPruning(depth - 1, alpha, beta, child);
// 取最大值
// 取最大值
if (value > alpha)
alpha = value;
// 剪枝返回
// 剪枝返回
if (alpha >= beta) {
return value;
}
}
// 取最大值
// 取最大值
node->value = alpha;
}
// 对后手搜索Min
// 对后手搜索Min
else {
for (auto child : node->children) {
value = alphaBetaPruning(depth - 1, alpha, beta, child);
// 取最小值
// 取最小值
if (value < beta)
beta = value;
// 剪枝返回
// 剪枝返回
if (alpha >= beta) {
return value;
}
}
// 取最小值
// 取最小值
node->value = beta;
}
// 返回
// 返回
return node->value;
}
void NineChessAi_ab::reverse(const NineChess *node1, NineChess *node2, int i)
{
}
void NineChessAi_ab::turn(const NineChess *node1, NineChess *node2, int i)
{
}
void NineChessAi_ab::rotate(const NineChess *node1, NineChess *node2, int i)
{
}
bool NineChessAi_ab::isInCache(Node * node, int &value)
{
/* NineChess tempData;
for (int i = 0; i < 2; i++) {
reverse(node, &tempData, i);
for (int j = 0; j < 6; j++) {
turn(node, &tempData, j);
int n = chess.rule.hasX ? 8 : 4;
for (int k = 0; k < n; k++) {
rotate(node, &tempData, k);
for (auto i : dataCache) {
if (compare(i, &tempData) == 0) {
value = i.value;
return true;
}
}
}
}
}
*/
return false;
}

View File

@ -1,22 +1,22 @@
#ifndef NINECHESSAI_AB
#ifndef NINECHESSAI_AB
#define NINECHESSAI_AB
#include "ninechess.h"
#include <list>
// 注意NineChess类不是线程安全的
// 所以不能在ai类中修改NineChess类的静态成员变量切记
// 注意NineChess类不是线程安全的
// 所以不能在ai类中修改NineChess类的静态成员变量切记
class NineChessAi_ab
{
public:
// 定义一个节点结构体
// 定义一个节点结构体
struct Node{
int value; // 节点的值
short move_; // 招法的命令行指令,图上标示为节点前的连线
struct Node * parent; // 父节点
list<struct Node *> children; // 子节点列表
int value; // 节点的值
short move_; // 招法的命令行指令,图上标示为节点前的连线
struct Node * parent; // 父节点
list<struct Node *> children; // 子节点列表
};
public:
@ -28,32 +28,44 @@ public:
void quit() { requiredQuit = true; }
protected:
// 建立子节点
// 建立子节点
void buildChildren(Node *node);
// 子节点排序
// 子节点排序
void sortChildren(Node *node);
// 清空节点树
// 清空节点树
void deleteTree(Node *node);
// 评价函数
// 评价函数
int evaluate(Node *node);
// Alpha-Beta剪枝算法
// Alpha-Beta剪枝算法
int alphaBetaPruning(int depth, int alpha, int beta, Node *node);
// 局面逆序
void reverse(const NineChess *node1, NineChess *node2, int i);
// 局面层次翻转
void turn(const NineChess *node1, NineChess *node2, int i);
// 局面旋转
void rotate(const NineChess *node1, NineChess *node2, int i);
// 判断是否在缓存中
bool isInCache(Node * node, int &value);
private:
// 原始模型
// 原始模型
NineChess chess;
// 演算用的模型
// 演算用的模型
NineChess chessTemp;
// 根节点
// 根节点
Node * rootNode;
// 局面数据缓存区
list<NineChess> dataCache;
// 局面数据缓存区最大大小
size_t cacheMaxSize;
// 标识,用于跳出剪枝算法,立即返回
// 标识,用于跳出剪枝算法,立即返回
bool requiredQuit;
// 剪枝算法的层深
// 剪枝算法的层深
int depth;
// 定义极大值等于32位有符号整形数字的最大值
// 定义极大值等于32位有符号整形数字的最大值
static const int infinity = 0x7fffffff;
};

View File

@ -1,7 +1,7 @@
# 九联棋 NineChess
## 古老的游戏
### 莫里斯九子棋
![莫里斯九子棋](./Wiki/莫里斯九子棋.PNG "Optional title")
![莫里斯九子棋](./screenshot/莫里斯九子棋.PNG "Optional title")
九子棋Nine Men's Morris是一个非常古老的智力的游戏。其历史甚至可以追溯到公元前1400多年的古埃及时代。
@ -16,7 +16,7 @@
+ 与莫里斯九子棋类似但一方仅剩3子时不能飞子。
### 打三棋12子棋
![12子棋](./Wiki/12子棋.PNG "Optional title")
![12子棋](./screenshot/12子棋.PNG "Optional title")
1. 双方各12颗子棋盘有斜线
2. 摆棋阶段被提子的位置不能再摆子,直到走棋阶段;
@ -26,7 +26,7 @@
6. 其它规则与成三棋基本相同。
### 九连棋
![九连棋](./Wiki/九连棋.PNG "Optional title")
![九连棋](./screenshot/九连棋.PNG "Optional title")
1. 规则与成三棋基本相同,只是它的棋子有序号;
2. 相同序号、位置的“三连”不能重复提子;
@ -35,7 +35,7 @@
## 应用程序说明
### 用户界面
![GUI](./Wiki/GUI.PNG "Optional title")
![GUI](./screenshot/GUI.PNG "Optional title")
### 系统支持
+ Windows版支持64位Windows 7、8、10系统不支持32位系统及Windows XP。