diff --git a/.gitignore b/.gitignore index b579dcb1..fd8694f8 100644 --- a/.gitignore +++ b/.gitignore @@ -318,3 +318,7 @@ CMakeLists.txt.user* # visual studio code .vscode/ + +# markdown temp files +*.md.Html +*.md.Htm diff --git a/NineChess/ninechess.pro b/NineChess/ninechess.pro index 3e1525c7..bdee9da7 100644 --- a/NineChess/ninechess.pro +++ b/NineChess/ninechess.pro @@ -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 \ diff --git a/NineChess/ninechess.pro.user b/NineChess/ninechess.pro.user index df4126e6..8ed40c40 100644 --- a/NineChess/ninechess.pro.user +++ b/NineChess/ninechess.pro.user @@ -1,6 +1,6 @@ - + EnvironmentId @@ -292,7 +292,7 @@ ninechess.pro false - + E:/My program/QT/NineChess/build-ninechess-Desktop_Qt_5_11_0_MSVC2017_64bit-Debug 3768 false true diff --git a/NineChess/src/ninechess.cpp b/NineChess/src/ninechess.cpp index f3aaaf9b..6eac615d 100644 --- a/NineChess/src/ninechess.cpp +++ b/NineChess/src/ninechess.cpp @@ -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::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; } diff --git a/NineChess/src/ninechess.h b/NineChess/src/ninechess.h index bce65e59..d6d2636d 100644 --- a/NineChess/src/ninechess.h +++ b/NineChess/src/ninechess.h @@ -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代表禁点 - * 0x11~0x1C代表先手第1~12子 - * 0x21~0x2c代表后手第1~12子 - * 判断棋子是先手的用(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 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代表禁点 + * 0x11~0x1C代表先手第1~12子 + * 0x21~0x2c代表后手第1~12子 + * 判断棋子是先手的用(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 millList; + + /* 当前招法,AI会用到,如下表示 + 0x 00 00 + pos1 pos2 + 开局落子:0x00??,??为棋盘上的位置 + 移子:0x__??,__为移动前的位置,??为移动后的位置 + 去子:0xFF??,??为棋盘上去子点的位置 + */ + short move_; + // 当前使用的规则 struct Rule rule; - // 棋盘数据 - struct Data data_; // 选中的棋子在board中的位置 int currentPos; diff --git a/NineChess/src/ninechessai_ab.cpp b/NineChess/src/ninechessai_ab.cpp index 4feaf7ab..f7ca8e46 100644 --- a/NineChess/src/ninechessai_ab.cpp +++ b/NineChess/src/ninechessai_ab.cpp @@ -1,10 +1,16 @@ -#include "NineChessAi_ab.h" +#include "ninechessai_ab.h" +#include +#include 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; +} diff --git a/NineChess/src/ninechessai_ab.h b/NineChess/src/ninechessai_ab.h index fd117729..397266e3 100644 --- a/NineChess/src/ninechessai_ab.h +++ b/NineChess/src/ninechessai_ab.h @@ -1,22 +1,22 @@ -#ifndef NINECHESSAI_AB +#ifndef NINECHESSAI_AB #define NINECHESSAI_AB #include "ninechess.h" #include -// ע⣺NineChess಻̰߳ȫģ -// Բai޸NineChessľ̬Աмǣ +// 注意:NineChess类不是线程安全的! +// 所以不能在ai类中修改NineChess类的静态成员变量,切记! class NineChessAi_ab { public: - // һڵṹ + // 定义一个节点结构体 struct Node{ - int value; // ڵֵ - short move_; // зָͼϱʾΪڵǰ - struct Node * parent; // ڵ - list children; // ӽڵб + int value; // 节点的值 + short move_; // 招法的命令行指令,图上标示为节点前的连线 + struct Node * parent; // 父节点 + list 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 dataCache; + // 局面数据缓存区最大大小 + size_t cacheMaxSize; - // ʶ֦㷨 + // 标识,用于跳出剪枝算法,立即返回 bool requiredQuit; - // ֦㷨IJ + // 剪枝算法的层深 int depth; - // 弫ֵ32λзֵֵ + // 定义极大值,等于32位有符号整形数字的最大值 static const int infinity = 0x7fffffff; }; diff --git a/Readme.md b/Readme.md index 4157a3d4..3553cf5b 100644 --- a/Readme.md +++ b/Readme.md @@ -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。