Commit ebbfb394 by WangChenxi

add ChineseChess

parent f6c221a4
No preview for this file type
No preview for this file type
...@@ -15,7 +15,7 @@ promoted 8 ...@@ -15,7 +15,7 @@ promoted 8
stone 17 stone 17
Attention: all this coding part should be >= 0 Attention: all this coding part should be >= 0
*/ */
//action = from_id * 64^4 + to_id * 64^3 + captured * 64^2 + promoted * 64^1 = stone * 64^0 //action = from_id * 64^4 + to_id * 64^3 + captured * 64^2 + promoted * 64^1 + stone * 64^0
#include <cstdlib> #include <cstdlib>
#include <string> #include <string>
......
...@@ -29,8 +29,8 @@ const ChessCoordId COORD_UNSET = -2; ...@@ -29,8 +29,8 @@ const ChessCoordId COORD_UNSET = -2;
const ChessCoordId COORD_RESIGN = -3; const ChessCoordId COORD_RESIGN = -3;
const ChessStoneColor EMPTY = 0; const ChessStoneColor EMPTY = 0;
const ChessStoneColor BLACK = 1; const ChessStoneColor WHITE= 1;
const ChessStoneColor WHITE = 2; const ChessStoneColor BLACK = 2;
const ChessStoneColor WALL = 3; const ChessStoneColor WALL = 3;
const ChessStoneColor WHITE_PAWN = 4; const ChessStoneColor WHITE_PAWN = 4;
const ChessStoneColor WHITE_KNIGHT = 5; const ChessStoneColor WHITE_KNIGHT = 5;
......
...@@ -895,7 +895,7 @@ bool ChessEnv::IsMoveValid(int &action) const{ ...@@ -895,7 +895,7 @@ bool ChessEnv::IsMoveValid(int &action) const{
ChessFunction::ActionToId(action, from_id, to_id, captured_stone, promoted_stone, piece); ChessFunction::ActionToId(action, from_id, to_id, captured_stone, promoted_stone, piece);
piece = board_state_[from_id]; piece = board_state_[from_id];
captured_stone = board_state_[to_id]; captured_stone = board_state_[to_id];
ChessFunction::IdToAction(from_id, to_id, captured_stone, promoted_stone, piece); ChessFunction::IdToAction(from_id, to_id, captured_stone, promoted_stone, piece, action);
return true; return true;
} }
} }
...@@ -924,9 +924,9 @@ int ChessEnv::CalcResult() const { ...@@ -924,9 +924,9 @@ int ChessEnv::CalcResult() const {
if(kingid == ChessComm::COORD_UNSET){ if(kingid == ChessComm::COORD_UNSET){
if(current_player_ == ChessComm::WHITE){ if(current_player_ == ChessComm::WHITE){
state_ = 1;//BLACK WIN! state_ = 2;//BLACK WIN!
}else if (current_player_ == ChessComm::BLACK){ }else if (current_player_ == ChessComm::BLACK){
state_ = 2;//WHITE WIN! state_ = 1;//WHITE WIN!
} }
} }
...@@ -939,9 +939,9 @@ ChessStoneColor ChessEnv::GetWinner() const { ...@@ -939,9 +939,9 @@ ChessStoneColor ChessEnv::GetWinner() const {
if (result == 3){ if (result == 3){
return CHessComm::EMPTY; return CHessComm::EMPTY;
}else if (result == 1){ }else if (result == 1){
return ChessComm::BLACK;
}else if(result == 2){
return ChessComm::WHITE; return ChessComm::WHITE;
}else if(result == 2){
return ChessComm::BLACK;
} }
} }
......
# ChineseChess
象棋遊戲設計
需求:
1. 人機對打
2. 人人對打
3. 搜尋深度設定(電腦強度)
4. 悔棋,還原
5. 下棋步法名稱顯示(ex:走三進一)
系統分為兩部分:
1. AI設計
2. 界面還有程式輔助設計(ex: UI顯示 以及操作和悔棋還原)
遊戲方式:
1. 由鍵盤輸入指令控制要哪個要動 EX: 1-> 車(0,9) 2-> 馬(1,9) 依此類推,選定後再顯示該單位可走路線(x,y)值,在由選手輸入x y值移動,以,做間隔。
2. 吃掉對方將軍為勝利者
AI設計
建立一顆博弈樹來表示下棋的過程,樹中的每一個節點代表棋盤上的一個局面,對每一個節點根據不同的走法又產生不同的局面(生出新的結點),如此不斷進行下去直到沒有可以選擇的走法,即到達葉子結點(棋局結束)。
該樹包含三種類型的結點
1. 奇數層的中間結點(以及根結點),表示輪到紅方
2. 偶數層的中間結點,表示輪到黑方
3. 葉子結點,表示棋局結束
局面評估
自己定義棋子的價值,考慮棋子位置及子力總合來替局面打分數。
建議用Alpha-Beta搜尋算法
悔棋和還原功能
(建議)悔棋主要任務為:
1. 下棋回合數減一
2. 將當前局面的數據保存到[步法]的queue裡,以供還原
3.[步法]queue中POP出上一回合的棋局數據,覆蓋目前回合。
4. 將戰況的列表框中的目前回合的保存到一個[步法名稱]QUEUE中,以供還原用,然後刪掉列表框中的目前回合走法名稱。
還原則跟悔棋相反:
1. 下棋回合數加一
2. 跟上面相反就對了….
下棋步法顯示功能
簡單來說就是將步法的起點坐標,終點坐標轉成中國象棋規範的走法名稱。
指定棋類為一個class(內含ID 名稱 子力 顏色(哪方) 目前位置(x,y) 存在(是否被吃掉了))
指定每個種類(EX:車馬砲士將兵象)繼承棋類各為class
建議定義一個class作為遊戲平台將各種棋類CLASS包含在內部,而AI引擎定義為另一個CLASS分開做。
Main function裡最好只需要多加
Game.start();
就好
其他自由發揮 只要能做到上述需求即可
#include "Chess.h"
#include "Map.h"
#include "Game.h"
Chess::Chess(string iName, bool icolor, COORD iPos) : Name(iName), Color(icolor), Pos(iPos), alive(true){}
King::King(string iName, bool icolor, COORD iPos) : Chess(iName, icolor, iPos){}
Chariot::Chariot(string iName, bool icolor, COORD iPos) : Chess(iName, icolor, iPos){}
Horse::Horse(string iName, bool icolor, COORD iPos) : Chess(iName, icolor, iPos){}
Cannon::Cannon(string iName, bool icolor, COORD iPos) : Chess(iName, icolor, iPos){}
Elephant::Elephant(string iName, bool icolor, COORD iPos) : Chess(iName, icolor, iPos){}
Adviser::Adviser(string iName, bool icolor, COORD iPos) : Chess(iName, icolor, iPos){}
Pawn::Pawn(string iName, bool icolor, COORD iPos) : Chess(iName, icolor, iPos){}
Chess::~Chess(){}
King::~King(){}
Chariot::~Chariot(){}
Horse::~Horse(){}
Cannon::~Cannon(){}
Elephant::~Elephant(){}
Adviser::~Adviser(){}
Pawn::~Pawn(){}
void Chess::setAlive(bool t)
{
alive = t;
}
void Chess::setPos(COORD p)
{
Pos = p;
}
/***base class***/
bool Chess::isValid(COORD move, const Map& map) const
{
return true;
}
/*******將*******/
bool King::isValid(COORD moveP, const Map& map) const
{
bool color = getColor();
Chess *ch = map.pChess[moveP.X][moveP.Y];
if (ch != NULL && color == ch->getColor())
return false;
COORD path = ComXY(moveP.X - getPos().X, moveP.Y - getPos().Y);
if ((color ? 7 : 0) <= moveP.Y && moveP.Y <= (color ? 9 : 2) && moveP.X >= 3 && moveP.X <= 5 && //
((abs(path.X) == 1 && abs(path.Y) == 0) || (abs(path.Y) == 1 && abs(path.X) == 0))) { // > 一般移動 //
return true; //
} //
else if (getPos().X == moveP.X && //王見王 可吃對方
(moveP.X == (color ? map.bKingPointer()->getPos().X : map.rKingPointer()->getPos().X))&&
(moveP.Y == (color ? map.bKingPointer()->getPos().Y : map.rKingPointer()->getPos().Y)))
{
for (int i = (getPos().Y > moveP.Y ? moveP.Y : getPos().Y) + 1; i < (getPos().Y > moveP.Y ? getPos().Y : moveP.Y); i++)
if (map.pChess[moveP.X][i] != NULL)
return false;
return true;
}
return false;
}
/*******車*******/
bool Chariot::isValid(COORD moveP, const Map& map) const
{
Chess *ch = map.pChess[moveP.X][moveP.Y];
if (ch != NULL&&getColor() == ch->getColor())
return false;
bool pass = false;
if ((getPos().X - moveP.X) != 0 && (getPos().Y == moveP.Y)) //左右前進
{
for (int i = (getPos().X > moveP.X ? moveP.X : getPos().X) + 1;i < (getPos().X > moveP.X ? getPos().X : moveP.X); i++)
if (map.pChess[i][moveP.Y] != NULL)
return false;
return true;
}
else if ((getPos().X == moveP.X) && (getPos().Y - moveP.Y) != 0) //上下前進
{
for (int i = (getPos().Y > moveP.Y ? moveP.Y : getPos().Y) + 1;i < (getPos().Y > moveP.Y ? getPos().Y : moveP.Y); i++)
if (map.pChess[moveP.X][i] != NULL)
return false;
return true;
}
return false;
}
/*******馬*******/
bool Horse::isValid(COORD moveP, const Map& map) const
{
Chess *ch = map.pChess[moveP.X][moveP.Y];
if (ch != NULL&&getColor() == ch->getColor())
return false;
if (abs(getPos().Y - moveP.Y) ==2 ) {//拐馬腳Y
if (map.pChess[getPos().X][getPos().Y - (getPos().Y - moveP.Y)/2] != NULL)
return false;
}
if (abs(getPos().X - moveP.X) == 2) {//拐馬腳X
if (map.pChess[getPos().X - (getPos().X - moveP.X)/2][getPos().Y] != NULL)
return false;
}
if (abs(getPos().Y - moveP.Y) == 2 && abs(getPos().X - moveP.X) == 1 ||
(abs(getPos().Y - moveP.Y) == 1 && abs(getPos().X - moveP.X) == 2)) {
return true;
}
return false;
}
/*******炮*******/
bool Cannon::isValid(COORD moveP, const Map& map) const
{
Chess *ch = map.pChess[moveP.X][moveP.Y];
if (ch != NULL&&getColor() == ch->getColor())
return false;
short count = 0; //看中間有幾個棋子
if (abs(getPos().X - moveP.X) != 0 && abs(getPos().Y - moveP.Y) == 0)
if (getPos().X - moveP.X > 0)
{
for (int i = getPos().X - 1; i > moveP.X; i--)
if (map.pChess[i][getPos().Y] != NULL)
count++;
if ((count == 1 && ch != NULL)
|| count == 0 && ch == NULL)
return true;
}
else
{
for (int i = getPos().X + 1; i < moveP.X; i++)
if (map.pChess[i][getPos().Y] != NULL)
count++;
if ((count == 1 && ch != NULL)
|| count == 0 && ch == NULL)
return true;
}
else if (abs(getPos().X - moveP.X) == 0 && abs(getPos().Y - moveP.Y) != 0)
if (getPos().Y - moveP.Y > 0)
{
for (int i = getPos().Y - 1; i > moveP.Y; i--)
if (map.pChess[getPos().X][i] != NULL)
count++;
if ((count == 1 && ch != NULL)
|| count == 0 && ch == NULL)
return true;
}
else
{
for (int i = getPos().Y + 1; i < moveP.Y; i++)
if (map.pChess[getPos().X][i] != NULL)
count++;
if ((count == 1 && ch != NULL)
|| count == 0 && ch == NULL)
return true;
}
return false;
}
/*******象*******/
bool Elephant::isValid(COORD moveP, const Map& map) const
{
Chess *ch = map.pChess[moveP.X][moveP.Y];
if (ch != NULL&&getColor() == ch->getColor())
return false;
if (map.pChess[(getPos().X + moveP.X) / 2][(getPos().Y + moveP.Y) / 2] == NULL)//中間沒卡到東西
{
if (getColor() == true)
{
if (abs(moveP.X - getPos().X) == 2 && abs(moveP.Y - getPos().Y) == 2 && moveP.Y >= 5 && moveP.Y <= 9)
return true;
}
else
{
if (abs(moveP.X - getPos().X) == 2 && abs(moveP.Y - getPos().Y) == 2 && moveP.Y >= 0 && moveP.Y <= 4)
return true;
}
}
return false;
}
/*******士*******/
bool Adviser::isValid(COORD moveP, const Map& map) const
{
Chess *ch = map.pChess[moveP.X][moveP.Y];
if (ch != NULL&&getColor() == ch->getColor())
return false;
if (moveP.X >= 3 && moveP.X <= 5) {
if (getColor() == true) {
if (abs(moveP.X - getPos().X) == 1 && abs(moveP.Y - getPos().Y) == 1 && moveP.Y >= 7 && moveP.Y <= 9)
return true;
}
else {
if (abs(moveP.X - getPos().X) == 1 && abs(moveP.Y - getPos().Y) == 1 && moveP.Y >= 0 && moveP.Y <= 2)
return true;
}
}
return false;
}
/*******兵*******/
bool Pawn::isValid(COORD moveP, const Map& map) const
{
Chess *ch = map.pChess[moveP.X][moveP.Y];
if (ch != NULL&&getColor() == ch->getColor())
return false;
if (getColor() == true) {//red
if (getPos().Y <= 4) {//已過河
if ((((getPos().Y - moveP.Y) == 1 && abs(getPos().X - moveP.X) == 0) ||
(abs(getPos().Y - moveP.Y) == 0 && abs(getPos().X - moveP.X) == 1)))
return true;
}
else//未過河
if (getPos().Y - moveP.Y == 1 && abs(getPos().X - moveP.X) == 0) {
return true;
}
return false;
}
else {//black
if (getPos().Y >= 5) {//已過河
if ((getPos().Y - moveP.Y == -1 && abs(getPos().X - moveP.X) == 0) ||
(abs(getPos().Y - moveP.Y) == 0 && abs(getPos().X - moveP.X) == 1))
return true;
}
else//未過河
if (getPos().Y - moveP.Y == -1 && abs(getPos().X - moveP.X) == 0) {
return true;
}
return false;
}
}
bool Chess::isDeath() {
return (!alive);
}
\ No newline at end of file
#ifndef CHESS_H
#define CHESS_H
#include <vector>
#include <string>
#include <Windows.h>
using std::string;
class Map;
class Chess
{
public:
Chess() = delete;
~Chess();
Chess(string iName, bool icolor, COORD iPos);
void setName();
void setColor();
void setPos(COORD p);
void setAlive(bool t);
string getName() const { return Name; }
bool getColor() const { return Color; }
COORD getPos() const { return Pos; }
virtual bool isValid(COORD moveP, const Map& map) const; //走法是否合法
bool isDeath();
std::vector<COORD>access;
std::vector<Chess*>enemy;
private:
string Name;
bool Color; //紅為true 黑為false
COORD Pos; //size 9,10
bool alive;
};
class King :public Chess // 將
{
public:
King() = delete;
~King();
King(string iName, bool icolor, COORD iPos);
virtual bool isValid(COORD move, const Map& map)const;
};
class Chariot :public Chess // 車
{
public:
Chariot() = delete;
~Chariot();
Chariot(string iName, bool icolo, COORD iPos);
virtual bool isValid(COORD move, const Map& map)const;
};
class Horse : public Chess // 馬
{
public:
Horse() = delete;
~Horse();
Horse(string iName, bool icolor, COORD iPos);
virtual bool isValid(COORD moveP, const Map& map)const;
};
class Cannon :public Chess // 炮
{
public:
Cannon() = delete;
~Cannon();
Cannon(string iName, bool icolor, COORD iPos);
virtual bool isValid(COORD move, const Map& map)const;
};
class Elephant :public Chess //象
{
public:
Elephant() = delete;
~Elephant();
Elephant(string iName, bool icolor, COORD iPos);
virtual bool isValid(COORD move, const Map& map)const;
};
class Adviser :public Chess //士
{
public:
Adviser() = delete;
~Adviser();
Adviser(string iName, bool icolor, COORD iPos);
virtual bool isValid(COORD moveP, const Map& map)const;
};
class Pawn :public Chess //兵
{
public:
Pawn() = delete;
~Pawn();
Pawn(string iName, bool icolor, COORD iPos);
virtual bool isValid(COORD move, const Map& map)const;
};
#endif
\ No newline at end of file
#include "GUI.h"
#include <Windows.h>
#include <conio.h>
#include <fstream>
static CONSOLE_SCREEN_BUFFER_INFO srInfo;
static const HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
static CONSOLE_CURSOR_INFO crInfo;
GUI::GUI()
{
system("chcp 950");
system("mode con: cols=104 lines=24");
SetConsoleTitle("象棋遊戲");
GetConsoleScreenBufferInfo(hConsole, &srInfo);
GetConsoleCursorInfo(hConsole, &crInfo);
if (hConsole == INVALID_HANDLE_VALUE)
exit(EXIT_FAILURE);
}
GUI::~GUI(){}
void GUI::gotoxy(SHORT x, SHORT y)
{
COORD hwCursorPosition{ x, y };
SetConsoleCursorPosition(hConsole, hwCursorPosition);
}
void GUI::gotoxy(COORD cd)
{
SetConsoleCursorPosition(hConsole, cd);
}
void GUI::setVisible(bool op)
{
crInfo.bVisible = op;
SetConsoleCursorInfo(hConsole, &crInfo);
}
void GUI::dwSize(int size)
{
crInfo.dwSize = size;
SetConsoleCursorInfo(hConsole, &crInfo);
}
void GUI::showTextColor(std::string word, SHORT color)
{
SetConsoleTextAttribute(hConsole, color);
cout << word;
SetConsoleTextAttribute(hConsole, DEFAULT_COLOR);
}
void GUI::setColor(INT color)
{
SetConsoleTextAttribute(hConsole, color);
}
void GUI::displayChessboard(const Map& map)
{
COORD bufferSize = { 34, 21 };
COORD characterBufferSize = { 34, 21 };
COORD characterPosition = { 0, 0 };
SMALL_RECT consoleWriteArea = { CHESS_BOARD_X, CHESS_BOARD_Y, CHESS_BOARD_X + 34 - 1, CHESS_BOARD_Y + 21 - 1 };
CHAR_INFO consoleBuffer[34 * 21];
SetConsoleScreenBufferSize(hConsole, bufferSize);
for (int y = 0; y < (21); ++y) {
for (int x = 0; x < 34; ++x) {
consoleBuffer[x + 34 * y].Attributes = ((y == 0 || y == 20) ? WD_Purple_BG_WHITE : WD_BLACK_BG_WHITE);
consoleBuffer[x + 34 * y].Char.AsciiChar = ChessScreenChar[y][x];
}
}
for (int x = 0; x < ROW_SIZE; x++)
for (int y = 0; y < COLUMN_SIZE; y++)
if (map.pChess[x][y] != NULL) {
consoleBuffer[(x * 4) + 34 * (y * 2 + 1)].Attributes = ((map.pChess[x][y]->getColor() == true) ? CHESS_RED : CHESS_BLACK);
consoleBuffer[(x * 4) + 34 * (y * 2 + 1) + 1].Attributes = ((map.pChess[x][y]->getColor() == true) ? CHESS_RED : CHESS_BLACK);
consoleBuffer[(x * 4) + 34 * (y * 2 + 1)].Char.AsciiChar = map.pChess[x][y]->getName().at(0);
consoleBuffer[(x * 4) + 34 * (y * 2 + 1) + 1].Char.AsciiChar = map.pChess[x][y]->getName().at(1);
}
/* Write our character buffer (a single character currently) to the console buffer */
WriteConsoleOutputA(hConsole, consoleBuffer, characterBufferSize, characterPosition, &consoleWriteArea);
}
void GUI::displayGameInfo(bool isWhosTurn, const Map& map, const Chess *ch)
{
const int cols = 38;
const int lines = 22;
COORD bufferSize = { cols, lines };
COORD characterBufferSize = { cols, lines };
COORD characterPosition = { 0, 0 };
SMALL_RECT consoleWriteArea = { 64, 1, 64 + cols - 1, 1 + lines - 1 };
CHAR_INFO consoleBuffer[cols * lines];
SetConsoleScreenBufferSize(hConsole, bufferSize);
for (int y = 0; y < lines; ++y)
for (int x = 0; x < cols; ++x) {
consoleBuffer[x + cols * y].Attributes = DEFAULT_COLOR;
consoleBuffer[x + cols * y].Char.AsciiChar = gameInfoScreen[y][x];
}
for (int y = 2; y < 21; ++y) {
for (int x = 2; x < 36; ++x) {
if (y == 3) { // 第3行
consoleBuffer[x + cols * y].Attributes = WD_OCEANBLUE_BG_BLACK;
if (x >= 18 && x <= 23) {
consoleBuffer[x + cols * y].Char.AsciiChar = string(isWhosTurn ? "紅色方" : "黑色方").at(x - 18);
consoleBuffer[x + cols * y].Attributes = isWhosTurn ? WD_RED_BG_BLACK : WD_GRAY_BG_BLACK;
}
}
else if (y == 6 && ch != nullptr) { //第5行
consoleBuffer[x + cols * y].Attributes = WD_OCEANBLUE_BG_BLACK;
if (x >= 12 && x <= 23) {
consoleBuffer[x + cols * y].Char.AsciiChar = string(string("您選擇了 ") + string(ch->getName())).at(x - 12);
if (x == 22 || x == 23)
consoleBuffer[x + cols * y].Attributes = isWhosTurn ? WD_RED_BG_BLACK : WD_GRAY_BG_BLACK;
}
}
else if (y == 9 && (map.checkKingToBeKilled(true) || map.checkKingToBeKilled(false))) { // 第7行
if ((isWhosTurn && map.checkKingToBeKilled(true)) || ((!isWhosTurn) && map.checkKingToBeKilled(false))) {
consoleBuffer[x + cols * 8].Attributes = 64;
consoleBuffer[x + cols * 9].Attributes = 64;
consoleBuffer[x + cols * 10].Attributes = 64;
}
if (x >= 9 && x <= 28) {
consoleBuffer[x + cols * y].Attributes = DEFAULT_COLOR;
if (isWhosTurn && map.checkKingToBeKilled(true))
consoleBuffer[x + cols * y].Char.AsciiChar = string("! 紅方 被將軍 !").at(x - 9);
else if ((!isWhosTurn) && map.checkKingToBeKilled(false))
consoleBuffer[x + cols * y].Char.AsciiChar = string("! 黑方 被將軍 !").at(x - 9);
if (x >= 13 && x <= 16)
consoleBuffer[x + cols * y].Attributes = isWhosTurn ? WD_RED_BG_BLACK : WD_GRAY_BG_BLACK;
}
}
}
}
WriteConsoleOutputA(hConsole, consoleBuffer, characterBufferSize, characterPosition, &consoleWriteArea);
}
bool GUI::showConfirm(const string& info)
{
/* A COORD struct for specificying the console's screen buffer dimensions */
COORD bufferSize = { WINDOW_COLS, WINDOW_LINES };
/* Setting up different variables for passing to WriteConsoleOutput */
COORD characterBufferSize = { WINDOW_COLS, WINDOW_LINES };
COORD characterPosition = { 0, 0 };
SMALL_RECT consoleWriteArea = { 0, 0, WINDOW_COLS - 1, WINDOW_LINES - 1 };
/* A CHAR_INFO structure containing data about a single character */
CHAR_INFO consoleBuffer[WINDOW_COLS * WINDOW_LINES];
/* Set the screen's buffer size */
SetConsoleScreenBufferSize(hConsole, bufferSize);
/* Write our character buffer (a single character currently) to the console buffer */
ReadConsoleOutputA(hConsole, consoleBuffer, characterBufferSize, characterPosition, &consoleWriteArea);
setVisible(false);
bool point = false;
setColor(12);
gotoxy(MID_X - 10, MID_Y - 3);
cout << "╔═══════════╗"; gotoxy(MID_X - 10, MID_Y - 2);
cout << "║ ║"; gotoxy(MID_X - 10, MID_Y - 1);
cout << "║ ║"; gotoxy(MID_X - 10, MID_Y);
cout << "║"; showTextColor(info, DEFAULT_COLOR); setColor(12); cout << "║"; gotoxy(MID_X - 10, MID_Y + 1);
cout << "║ ║"; gotoxy(MID_X - 10, MID_Y + 2);
cout << "║ 是 否 ║"; gotoxy(MID_X - 10, MID_Y + 3);
cout << "║ ║"; gotoxy(MID_X - 10, MID_Y + 4);
cout << "╚═══════════╝";
char KB;
while (true)
{
gotoxy(MID_X - 4, MID_Y + 2);
showTextColor("是", !point ? 15 : 240);
gotoxy(MID_X + 6, MID_Y + 2);
showTextColor("否", point ? 15 : 240);
KB = _getch();
switch (KB)
{
case KB_LEFT:
case KB_RIGHT:
point = !point;
break;
case KB_ENTER:
WriteConsoleOutputA(hConsole, consoleBuffer, characterBufferSize, characterPosition, &consoleWriteArea);
return point;
break;
default:
break;
}
}
WriteConsoleOutputA(hConsole, consoleBuffer, characterBufferSize, characterPosition, &consoleWriteArea);
return point;
}
void GUI::displayBattleSituation(const Map& map)
{
const int cols = 26;
const int lines = BATTLE_SITUATION_LINES + 2;
COORD bufferSize = { cols, lines };// col = 26, lines = 16
COORD characterBufferSize = { cols, lines };
COORD characterPosition = { 0, 0 };
SMALL_RECT consoleWriteArea = { 2, 1, (78 + cols) - 1, (1 + lines) - 1 };
CHAR_INFO consoleBuffer[cols * lines];
SetConsoleScreenBufferSize(hConsole, bufferSize);
string wall = "║";
for (int y = 0; y < lines; ++y)
for (int x = 0; x < cols; ++x) {
consoleBuffer[y * cols + x].Char.AsciiChar = '\0';
consoleBuffer[y * cols + x].Attributes = DEFAULT_COLOR;
if (x == 0 || x == (cols-2)) {
consoleBuffer[y * cols + x].Char.AsciiChar = wall.at(0);
consoleBuffer[y * cols + x + 1].Char.AsciiChar = wall.at(1);
consoleBuffer[y * cols + x + 1].Attributes = DEFAULT_COLOR;
x++;
}
}
string title = "╔═ 戰 況 顯 示 ═╗";
string bottom = "╚═══════════╝";
for (int x = 0; x < cols; ++x) {
if (title[x] == '\0')break;
consoleBuffer[x].Char.AsciiChar = title.at(x);
}
const std::string ChineseNum[9] = { "一", "二", "三", "四", "五", "六", "七", "八", "九" };
const std::string ArabicNum[9] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
int counter = 1;
bool straight = false;
int i = 0;
const std::vector<chessStorage> *it = (map.chessStoragePointerConst());
if (it->size() > BATTLE_SITUATION_LINES) {
counter += it->size() - BATTLE_SITUATION_LINES;
i += it->size() - BATTLE_SITUATION_LINES;
}
string line;
int printer = 1;
for (; i != map.chessStoragePointerConst()->size(); i++) {
straight = false;
bool color = it->at(i).moved->getColor();
if (counter >= 100)line += " ";
else if (counter >= 10)line += " ";
else if (counter < 10)line += " ";
line += std::to_string(counter);
line += (color ? " 紅:" : " 黑:");
line += it->at(i).moved->getName() + " ";
line += (color ? ChineseNum[(8 - it->at(i).prePos.X)] : ArabicNum[(it->at(i).prePos.X)]);
if ((color ? (it->at(i).prePos.Y - it->at(i).Pos.Y) : (it->at(i).Pos.Y - it->at(i).prePos.Y)) > 0) {
line += " 進 ";
if ((it->at(i).prePos.X - it->at(i).Pos.X) == 0) {
line += (color ? (ChineseNum[(it->at(i).prePos.Y - it->at(i).Pos.Y) - 1]) : (ArabicNum[(it->at(i).Pos.Y - it->at(i).prePos.Y) - 1]));
straight = true;
}
}
else if ((color ? (it->at(i).Pos.Y - it->at(i).prePos.Y) : -(it->at(i).prePos.Y - it->at(i).Pos.Y)) == 0)
line += " 平 ";
else {
line += " 退 ";
if ((it->at(i).prePos.X - it->at(i).Pos.X) == 0) {
line += (color ? (ChineseNum[(it->at(i).Pos.Y - it->at(i).prePos.Y) - 1]) : (ArabicNum[(it->at(i).prePos.Y - it->at(i).Pos.Y) - 1]));
straight = true;
}
}
if (!straight)
line += (color ? (ChineseNum[(8 - it->at(i).Pos.X)]) : (ArabicNum[(it->at(i).Pos.X)]));
for (int x = 0; x < 26; ++x) {
if (x == (line.size()))break;
consoleBuffer[26 * printer + x + 2].Char.AsciiChar = line.at(x);
}
consoleBuffer[26 * printer + 7].Attributes = (color ? WD_RED_BG_BLACK : WD_GRAY_BG_BLACK);
consoleBuffer[26 * printer + 8].Attributes = (color ? WD_RED_BG_BLACK : WD_GRAY_BG_BLACK);
printer++;
line.clear();
counter++;
}
for (int x = 0; x < cols; ++x) {
if (title[x] == '\0')break;
consoleBuffer[21*cols+x].Char.AsciiChar = bottom.at(x);
}
WriteConsoleOutputA(hConsole, consoleBuffer, characterBufferSize, characterPosition, &consoleWriteArea);
}
void GUI::displayExitScreen()
{
HWND hwnd = GetConsoleWindow();
WINDOWINFO rr;
GetWindowInfo(hwnd, &rr);
COORD window{ rr.rcClient.right - rr.rcClient.left, rr.rcClient.bottom - rr.rcClient.top };
HDC hdc = GetDC(hwnd);
HDC memhdc = CreateCompatibleDC(hdc);
HBITMAP cross = CreateCompatibleBitmap(hdc, window.X, window.Y);//106*8,24*16
SelectObject(memhdc, cross);
int x1 = 0;
int y1 = 0;
int x2 = window.X;
int y2 = window.Y;
int speed = 6;
int density = 4;
int color = 200;
while (1) {
DeleteObject(cross);
cross = CreateCompatibleBitmap(hdc, window.X, window.Y);
SelectObject(memhdc, cross);
int y = 200;
for (int x = x1; x < x2; x += density) {
for (int y = y1; y < y2; y += density) {
color = rand() % 256 + y1;
if (color>255)color = 255;
SetPixel(memhdc, x, y, RGB(color, color, color));
}
}
x1 += 2 * speed, y1 += speed, x2 -= 2 * speed, y2 -= speed;
BitBlt(hdc, 0, 0, window.X, window.Y, memhdc, 0, 0, SRCCOPY);
if (x1 > (window.X / 2))break;
else if (x1 > (window.X / 4)){ density = 2; }
else if (x1 > (window.X / 3)){ density = 1; }
}
}
short GUI::mainMenu()
{
//確定音訊檔有無在同一目錄下
std::ifstream audio;
audio.open("bgaudio.wav");
if (audio.is_open()) {
audio.close();
PlaySound("bgaudio.wav", NULL, SND_ASYNC | SND_FILENAME | SND_LOOP); //Play Sound;
}
system("cls");
class printMenu {
private:
COORD bufferSize;
COORD characterBufferSize;
COORD characterPosition;
SMALL_RECT consoleWriteArea;
CHAR_INFO *consoleBuffer;
public:
printMenu() {
consoleBuffer = new CHAR_INFO[WINDOW_COLS * WINDOW_LINES];
bufferSize = { WINDOW_COLS, WINDOW_LINES };
characterBufferSize = { WINDOW_COLS, WINDOW_LINES };
characterPosition = { 0, 0 };
consoleWriteArea = { 0, 0, WINDOW_COLS - 1, WINDOW_LINES - 1 };
}
void print(int color,int option) {
char* options[5] = { "雙人遊戲", "電腦對戰", "設定難度", " 關於 ", "退出遊戲" };
SetConsoleScreenBufferSize(hConsole, bufferSize);
for (int y = 0; y < WINDOW_LINES; ++y) {
for (int x = 0; x < WINDOW_COLS; ++x) {
consoleBuffer[x + WINDOW_COLS * y].Char.AsciiChar = mainMenuScreen[y][x];
consoleBuffer[x + WINDOW_COLS * y].Attributes = (((y / 2 + x + color) / 20) % 6 + 1);
}}
for (int y = 13; y <= 21; ++y) {
if (y % 2 == 0)continue;
for (int x = 53; x <= 60; ++x) {
consoleBuffer[x + WINDOW_COLS * y].Char.AsciiChar = options[(y - 13) / 2][x - 53];
consoleBuffer[x + WINDOW_COLS * y].Attributes = 7;
if (((y - 13) / 2) + 1 == option)
consoleBuffer[x + WINDOW_COLS * y].Attributes = 112;
}
}
WriteConsoleOutputA(hConsole, consoleBuffer, characterBufferSize, characterPosition, &consoleWriteArea);
}
~printMenu() {
delete[] consoleBuffer;
}
};
printMenu image;
bool stop = false;
static int option = 1;
int const optionsNum = 5;
std::thread t1([&]() {
int color = 1, i = 0;
while (!stop) {
image.print(color, option);
color++;
if (color > 10000)
color = 0;
Sleep(150);
}
});
//std::thread t2([&]() {
setVisible(false);
CHAR Input;
bool end = false;
while (!end) {
Input = _getch();
setVisible(false);
switch (Input) {
case KB_UP:
if (option != 1)
option--;
else
option = optionsNum;
break;
case KB_DOWN:
if (option != optionsNum)
option++;
else
option = 1;
break;
case KB_ENTER:
end = true;
stop = true;
break;
default:
break;
}
}
//});
t1.join();
//t2.join();
//PlaySound(NULL, NULL, NULL);
return option;
}
short GUI::MenuInGame()
{
COORD bufferSize = { WINDOW_COLS, WINDOW_LINES };
COORD characterBufferSize = { WINDOW_COLS, WINDOW_LINES };
COORD characterPosition = { 0, 0 };
SMALL_RECT consoleWriteArea = { 0, 0, WINDOW_COLS - 1, WINDOW_LINES - 1 };
CHAR_INFO consoleBuffer[WINDOW_COLS * WINDOW_LINES];
SetConsoleScreenBufferSize(hConsole, bufferSize);
ReadConsoleOutputA(hConsole, consoleBuffer, characterBufferSize, characterPosition, &consoleWriteArea);
bool decided = false;
string options[4] = { "繼續遊戲", "重新開始", "回主選單", "離開遊戲" };
short option = 1;
setColor(9);
gotoxy(MID_X - 6, MID_Y - 5);
cout << "╔═══════╗"; gotoxy(MID_X - 6, MID_Y - 4);
cout << "║ ║"; gotoxy(MID_X - 6, MID_Y - 3);
cout << "╠═══════╣"; gotoxy(MID_X - 6, MID_Y - 2);
cout << "║ ║"; gotoxy(MID_X - 6, MID_Y - 1);
cout << "╠═══════╣"; gotoxy(MID_X - 6, MID_Y);
cout << "║ ║"; gotoxy(MID_X - 6, MID_Y + 1);
cout << "╠═══════╣"; gotoxy(MID_X - 6, MID_Y + 2);
cout << "║ ║"; gotoxy(MID_X - 6, MID_Y + 3);
cout << "╚═══════╝";
setColor(DEFAULT_COLOR);
CHAR Input;
while (!decided)
{
for (int i = 0; i < 4; i++) {
gotoxy(MID_X - 1, MID_Y - 4 + 2 * i);
if (option == (i + 1))
showTextColor(options[i], 240);
else
cout << options[i];
}
setVisible(false);
Input = _getch();
switch (Input)
{
case KB_UP:
if (option != 1)
option--;
else
option = 4;
break;
case KB_DOWN:
if (option != 4)
option++;
else
option = 1;
break;
case KB_ENTER:
decided = true;
break;
case KB_ESC:
option = 1;
decided = true;
default:
break;
}
}
WriteConsoleOutputA(hConsole, consoleBuffer, characterBufferSize, characterPosition, &consoleWriteArea);
return option;
}
void GUI::displayPossiblePath(Chess* ch, const Map& map)
{
COORD bufferSize = { 34, 21 };
COORD characterBufferSize = { 34, 21 };
COORD characterPosition = { 0, 0 };
SMALL_RECT consoleWriteArea = { CHESS_BOARD_X, CHESS_BOARD_Y, CHESS_BOARD_X + 34 - 1, CHESS_BOARD_Y + 21 - 1 };
CHAR_INFO consoleBuffer[34 * 21];
SetConsoleScreenBufferSize(hConsole, bufferSize);
for (int y = 0; y < (21); ++y) {
for (int x = 0; x < 34; ++x) {
consoleBuffer[x + 34 * y].Attributes = ((y == 0 || y == 20) ? WD_Purple_BG_WHITE : WD_BLACK_BG_WHITE);
consoleBuffer[x + 34 * y].Char.AsciiChar = ChessScreenChar[y][x];
}
}
for (int x = 0; x < ROW_SIZE; x++)
for (int y = 0; y < COLUMN_SIZE; y++)
if (map.pChess[x][y] != NULL) {
consoleBuffer[(x * 4) + 34 * (y * 2 + 1)].Attributes = ((map.pChess[x][y]->getColor() == true) ? CHESS_RED : CHESS_BLACK);
consoleBuffer[(x * 4) + 34 * (y * 2 + 1) + 1].Attributes = ((map.pChess[x][y]->getColor() == true) ? CHESS_RED : CHESS_BLACK);
consoleBuffer[(x * 4) + 34 * (y * 2 + 1)].Char.AsciiChar = map.pChess[x][y]->getName().at(0);
consoleBuffer[(x * 4) + 34 * (y * 2 + 1) + 1].Char.AsciiChar = map.pChess[x][y]->getName().at(1);
}
for (unsigned int i = 0; i < ch->access.size(); i++)
{
int x = ch->access.at(i).X;
int y = ch->access.at(i).Y;
Chess *tempch = map.pChess[ch->access.at(i).X][ch->access.at(i).Y];
consoleBuffer[(x * 4) + 34 * (y * 2 + 1)].Attributes = (tempch == NULL ? 120 : (tempch->getColor() ? 60 : 48));
consoleBuffer[(x * 4) + 34 * (y * 2 + 1) + 1].Attributes = (tempch == NULL ? 120 : (tempch->getColor() ? 60 : 48));
if (tempch != NULL) {
consoleBuffer[(x * 4) + 34 * (y * 2 + 1)].Char.AsciiChar = tempch->getName().at(0);
consoleBuffer[(x * 4) + 34 * (y * 2 + 1) + 1].Char.AsciiChar = tempch->getName().at(1);
}
}
WriteConsoleOutputA(hConsole, consoleBuffer, characterBufferSize, characterPosition, &consoleWriteArea);
}
void GUI::showAlert(const string info, const short time)
{
COORD bufferSize = { WINDOW_COLS, WINDOW_LINES };
COORD characterBufferSize = { WINDOW_COLS, WINDOW_LINES };
COORD characterPosition = { 0, 0 };
SMALL_RECT consoleWriteArea = { 0, 0, WINDOW_COLS - 1, WINDOW_LINES - 1 };
CHAR_INFO consoleBuffer[WINDOW_COLS * WINDOW_LINES];
SetConsoleScreenBufferSize(hConsole, bufferSize);
ReadConsoleOutputA(hConsole, consoleBuffer, characterBufferSize, characterPosition, &consoleWriteArea);
setVisible(false);
setColor(12);
gotoxy(MID_X - 10, MID_Y - 3);
cout << "╔═══════════╗"; gotoxy(MID_X - 10, MID_Y - 2);
cout << "║ ║"; gotoxy(MID_X - 10, MID_Y - 1);
cout << "║ ║"; gotoxy(MID_X - 10, MID_Y);
cout << "║"; showTextColor(info, DEFAULT_COLOR); setColor(12); cout << "║"; gotoxy(MID_X - 10, MID_Y + 1);
cout << "║ ║"; gotoxy(MID_X - 10, MID_Y + 2);
cout << "║ ║"; gotoxy(MID_X - 10, MID_Y + 3);
cout << "║ ║"; gotoxy(MID_X - 10, MID_Y + 4);
cout << "╚═══════════╝";
setColor(DEFAULT_COLOR);
Sleep(time);
WriteConsoleOutputA(hConsole, consoleBuffer, characterBufferSize, characterPosition, &consoleWriteArea);
setVisible(true);
}
void GUI::displayGameScreen(const Map& map, bool isWhosTurn, const Chess* ch)
{
COORD bufferSize{ WINDOW_COLS, WINDOW_LINES };
COORD characterBufferSize{ WINDOW_COLS, WINDOW_LINES };
COORD characterPosition{ 0, 0 };
SMALL_RECT consoleWriteArea{ 0, 0, WINDOW_COLS - 1, WINDOW_LINES - 1 };
CHAR_INFO consoleBuffer[WINDOW_COLS * WINDOW_LINES];
SetConsoleScreenBufferSize(hConsole, bufferSize);
for (int y = 0; y < WINDOW_LINES; ++y) {
for (int x = 0; x < WINDOW_COLS; ++x) {
consoleBuffer[x + WINDOW_COLS * y].Char.AsciiChar = gameScreen[y][x];
consoleBuffer[x + WINDOW_COLS * y].Attributes = DEFAULT_COLOR;
}
}
WriteConsoleOutputA(hConsole, consoleBuffer, characterBufferSize, characterPosition, &consoleWriteArea);
displayChessboard(map);
displayBattleSituation(map);
displayGameInfo(isWhosTurn, map, ch);
}
void GUI::displayAboutScreen()
{
COORD bufferSize{ WINDOW_COLS, WINDOW_LINES };
COORD characterBufferSize{ WINDOW_COLS, WINDOW_LINES };
COORD characterPosition{ 0, 0 };
SMALL_RECT consoleWriteArea{ 0, 0, WINDOW_COLS - 1, WINDOW_LINES - 1 };
CHAR_INFO consoleBuffer[WINDOW_COLS * WINDOW_LINES];
SetConsoleScreenBufferSize(hConsole, bufferSize);
for (int y = 0; y < WINDOW_LINES; ++y) {
for (int x = 0; x < WINDOW_COLS; ++x) {
consoleBuffer[x + WINDOW_COLS * y].Char.AsciiChar = aboutScreen[y][x];
consoleBuffer[x + WINDOW_COLS * y].Attributes = DEFAULT_COLOR;
}
}
WriteConsoleOutputA(hConsole, consoleBuffer, characterBufferSize, characterPosition, &consoleWriteArea);
_getch();
}
int GUI::showDepthInput()
{
int depth;
COORD bufferSize = { WINDOW_COLS, WINDOW_LINES };
COORD characterBufferSize = { WINDOW_COLS, WINDOW_LINES };
COORD characterPosition = { 0, 0 };
SMALL_RECT consoleWriteArea = { 0, 0, WINDOW_COLS - 1, WINDOW_LINES - 1 };
CHAR_INFO consoleBuffer[WINDOW_COLS * WINDOW_LINES];
SetConsoleScreenBufferSize(hConsole, bufferSize);
ReadConsoleOutputA(hConsole, consoleBuffer, characterBufferSize, characterPosition, &consoleWriteArea);
setVisible(true);
setColor(12);
gotoxy(MID_X - 10, MID_Y - 3);
cout << "╔═══════════╗"; gotoxy(MID_X - 10, MID_Y - 2);
cout << "║ ║"; gotoxy(MID_X - 10, MID_Y - 1);
cout << "║ ║"; gotoxy(MID_X - 10, MID_Y);
cout << "║"; showTextColor(" 請輸入電腦難度 (1~9) ", DEFAULT_COLOR); setColor(12); cout << "║"; gotoxy(MID_X - 10, MID_Y + 1);
cout << "║ ║"; gotoxy(MID_X - 10, MID_Y + 2);
cout << "║ ║"; gotoxy(MID_X - 10, MID_Y + 3);
cout << "║ ║"; gotoxy(MID_X - 10, MID_Y + 4);
cout << "╚═══════════╝";
setColor(DEFAULT_COLOR);
gotoxy(MID_X - 5, MID_Y + 2);
depth = _getch();
WriteConsoleOutputA(hConsole, consoleBuffer, characterBufferSize, characterPosition, &consoleWriteArea);
return depth - '0';
}
\ No newline at end of file
#ifndef GUI_H
#define GUI_H
#include <iostream>
#include <string>
#include <iomanip> //setw()
#include <thread>
#include "Map.h"
#pragma comment(lib, "winmm.lib") //PlaySound
using std::cout;
using std::string;
#define WINDOW_COLS 104
#define WINDOW_LINES 24
#define BATTLE_SITUATION_LINES 20
#define KB_UP 72 //鍵盤輸入數值 上
#define KB_DOWN 80 //鍵盤輸入數值 下
#define KB_LEFT 75 //鍵盤輸入數值 左
#define KB_RIGHT 77 //鍵盤輸入數值 右
#define KB_ENTER 13 //鍵盤輸入數值 Enter
#define KB_ESC 27 //鍵盤輸入數值 ESC
#define CHESSMAP_SIZE_ROW 17 //棋盤ROW SIZE
#define CHESSMAP_SIZE_COLUMN 19 //棋盤column SIZE
#define WD_BLACK_BG_WHITE 240 //黑字白色背景
#define WD_RED_BG_WHITE 252 //紅字白色背景
#define CHESS_RED 124 //紅方旗子的顏色
#define CHESS_BLACK 112 //黑方棋子的顏色
#define WD_Purple_BG_WHITE 253 //紫色字白色背景
#define WD_RED_BG_BLACK 12 //紅字黑色背景
#define WD_OCEANBLUE_BG_BLACK 11 //水藍色字黑色背景
#define WD_GRAY_BG_BLACK 8 //灰色字黑色背景
#define DEFAULT_COLOR 7 //預設白字黑色背景
#define SHOW_WHOS_TURN_POS ComXY(40,2) //顯示哪一方下棋的座標
#define SHOW_WHAT_YOU_CHOSE_POS ComXY(40, 4) //顯示你選取哪個棋子的座標
#define CHESS_BOARD_X 29 //棋盤基準點X
#define CHESS_BOARD_Y 1 //棋盤基準點Y
#define KB_44 44 //Keyboard "<"
#define KB_46 46 //Keyboard ">"
#define MID_X srInfo.srWindow.Right/2
#define MID_Y srInfo.srWindow.Bottom/2
/**************棋盤*****************/
const char ChessScreenChar[21][35] = {
"1 2 3 4 5 6 7 8 9",
"╔═╤═╤═╤═╤═╤═╤═╤═╗",
"║ │ │ │╲│╱│ │ │ ║",
"║─┼─┼─┼─┼─┼─┼─┼─║",
"║ │ │ │╱│╲│ │ │ ║",
"║─┼─┼─┼─┼─┼─┼─┼─║",
"║ │ │ │ │ │ │ │ ║",
"║─┼─┼─┼─┼─┼─┼─┼─║",
"║ │ │ │ │ │ │ │ ║",
"║─┴─┴─┴─┴─┴─┴─┴─║" ,
"║  楚河      漢界   ║",
"║─┬─┬─┬─┬─┬─┬─┬─║",
"║ │ │ │ │ │ │ │ ║",
"║─┼─┼─┼─┼─┼─┼─┼─║",
"║ │ │ │ │ │ │ │ ║",
"║─┼─┼─┼─┼─┼─┼─┼─║",
"║ │ │ │╲│╱│ │ │ ║",
"║─┼─┼─┼─┼─┼─┼─┼─║",
"║ │ │ │╱│╲│ │ │ ║",
"╚═╧═╧═╧═╧═╧═╧═╧═╝",
"九 八 七 六 五 四 三 二 一" };
const string ChessScreen[CHESSMAP_SIZE_COLUMN][CHESSMAP_SIZE_ROW] =
{ { "╔", "═", "╤", "═", "╤", "═", "╤", "═", "╤", "═", "╤", "═", "╤", "═", "╤", "═", "╗" },
{ "║", " ", "│", " ", "│", " ", "│", "╲", "│", "╱", "│", " ", "│", " ", "│", " ", "║" },
{ "║", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "║" },
{ "║", " ", "│", " ", "│", " ", "│", "╱", "│", "╲", "│", " ", "│", " ", "│", " ", "║" },
{ "║", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "║" },
{ "║", " ", "│", " ", "│", " ", "│", " ", "│", " ", "│", " ", "│", " ", "│", " ", "║" },
{ "║", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "║" },
{ "║", " ", "│", " ", "│", " ", "│", " ", "│", " ", "│", " ", "│", " ", "│", " ", "║" },
{ "║", "─", "┴", "─", "┴", "─", "┴", "─", "┴", "─", "┴", "─", "┴", "─", "┴", "─", "║" },
{ "║", " ", " ", "楚", "河", " ", " ", " ", " ", " ", " ", "漢", "界", " ", " ", " ", "║" },
{ "║", "─", "┬", "─", "┬", "─", "┬", "─", "┬", "─", "┬", "─", "┬", "─", "┬", "─", "║" },
{ "║", " ", "│", " ", "│", " ", "│", " ", "│", " ", "│", " ", "│", " ", "│", " ", "║" },
{ "║", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "║" },
{ "║", " ", "│", " ", "│", " ", "│", " ", "│", " ", "│", " ", "│", " ", "│", " ", "║" },
{ "║", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "║" },
{ "║", " ", "│", " ", "│", " ", "│", "╲", "│", "╱", "│", " ", "│", " ", "│", " ", "║" },
{ "║", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "┼", "─", "║" },
{ "║", " ", "│", " ", "│", " ", "│", "╱", "│", "╲", "│", " ", "│", " ", "│", " ", "║" },
{ "╚", "═", "╧", "═", "╧", "═", "╧", "═", "╧", "═", "╧", "═", "╧", "═", "╧", "═", "╝" } };
const char mainMenuScreen[WINDOW_LINES][WINDOW_COLS + 1] = {
"╔══════════════════════════════════════════════════╗",
"║ ,o888888o. 8888 8888 8888 b. 88 888888888888 d888888o. 888888888888║",
"║ ,8888 `88. 8888 8888 8888 888o. 88 8888 .88888:' `88. 8888 ║",
"║,888888 `8. 8888 8888 8888 888888o. 88 8888 8888888. Y8 8888 ║",
"║8888888 8888 8888 8888 888888888o. 88 8888 `8888888. 8888 ║",
"║8888888 8888 8888 8888 888888888888o.88 8888 `8888888. 8888 ║",
"║8888888 8888888888888888 8888 88Y8888888888888 888888888888 `8888888. 888888888888║",
"║8888888 8888 8888 8888 88 `Y8888888888 8888 `8888888. 8888 ║",
"║`888888 .8' 8888 8888 8888 88 `Y8888888 8888 8b `8888888. 8888 ║",
"║ `8888 ,88' 8888 8888 8888 88 `Y8888 8888 `8b. ;8888888 8888 ║",
"║ `8888888P' 8888 8888 8888 88 `Yo 888888888888 `Y888888888P' 888888888888║",
"║ ║",
"║ ╔════╗ ║",
"║ ,o888888o. 8888 8888 888888888888 ║ ║ d888888o. d888888o. ║",
"║ ,8888 `88. 8888 8888 8888 ╠════╣ .88888:' `88. .88888:' `88. ║",
"║,888888 `8. 8888 8888 8888 ║ ║ 8888888. Y8 8888888. Y8 ║",
"║8888888 8888 8888 8888 ╠════╣ `8888888. `8888888. ║",
"║8888888 8888 8888 8888 ║ ║ `8888888. `8888888. ║",
"║8888888 8888888888888888 888888888888 ╠════╣ `8888888. `8888888. ║",
"║8888888 8888 8888 8888 ║ ║ `8888888. `8888888. ║",
"║`888888 .8' 8888 8888 8888 ╠════╣ 8b `8888888. 8b `8888888. ║",
"║ `8888 ,88' 8888 8888 8888 ║ ║ `8b. ;8888888 `8b. ;8888888 ║",
"║ `8888888P' 8888 8888 888888888888 ╚════╝ `Y888888888P' `Y888888888P' ║",
"╚══════════════════════════════════════════════════╝" };
const char gameScreen[WINDOW_LINES][WINDOW_COLS+1] = {
"╔══════════════════════════════════════════════════╗",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"╚══════════════════════════════════════════════════╝" };
const char gameInfoScreen[22][39] = { //22*38
"╔═════════════════╗",
"║ ║",
"║ ║",
"║ 現在輪到 下棋 ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"║ ║",
"╠═════════════════╣",
"║ Esc 選單 < 悔棋 > 還原 ║",
"║ ║",
"║ Enter 選取棋子 ║",
"║ ║",
"║ ↑ ║",
"║ ← → 方向鍵控制游標 ║",
"║ ↓ ║",
"╚═════════════════╝" };
const char aboutScreen[WINDOW_LINES][WINDOW_COLS + 1] = {
"                                                    ",
"                                                    ",
"  本遊戲為 103-2 程式設計AA期末專題 第三組 之專題成果                   ",
"                                                    ",
"  第三組 成員:                                           ",
"                                                    ",
"                                                    ",
"         組長 陳冠羽                                     ",
"         組員 袁艾文                                     ",
"            張岱                                      ",
"            林福廷                                     ",
"            曾翊華                                     ",
"            李至曜                                     ",
"       指導助教 莊正陽                                     ",
"                                                    ",
"                                                    ",
"                                                    ",
"                                                    ",
"                                                    ",
"                                                    ",
"                                                    ",
"                                                    ",
"                                                    " };
class GUI {
public:
GUI();
~GUI();
short mainMenu(); // 1=start ; 2=setting ; 3=exit
short MenuInGame(); // 1=resume ; 2=restart ; 3=exit
void setVisible(bool);
void dwSize(int);
//--------------------
void displayGameScreen(const Map& map, bool isWhosTurn, const Chess *ch = nullptr);
void displayChessboard(const Map&);
void displayGameInfo(bool,const Map&, const Chess *ch = nullptr);
void displayBattleSituation(const Map& map);
//--------------------
void displayPossiblePath(Chess* ch, const Map& map);
bool showConfirm(const string&); //<==請填剛好22半形字元
void showAlert(const string, const short);//<==請填剛好22半形字元
int showDepthInput();
void displayExitScreen();
void displayAboutScreen();
void gotoxy(COORD);
void gotoxy(short, short);
void showTextColor(string, SHORT);
private:
void setColor(INT);
};
#endif
\ No newline at end of file
#include "Game.h"
#include "Chess.h"
#include "Map.h"
#include "Player.h"
using std::cout;
using namespace XQWLight;
Game::Game()
{
isWhosTurn = true;
cursorPos = ComXY(0, 0);
gamemode = 1;
init_engine(5); //XQ
init_game(); //XQ
}
bool operator ==(const COORD& a, const COORD& b)
{
if (a.X == b.X&&a.Y == b.Y)
return true;
return false;
}
void Game::makeAccess(Map& imap)
{
imap.rKingPointer()->enemy.clear();
imap.bKingPointer()->enemy.clear();
for (int i = 0; i < ROW_SIZE; i++) //尋找每個棋子
for (int j = 0; j < COLUMN_SIZE; j++) //
if (imap.pChess[i][j] != NULL)
{
imap.pChess[i][j]->access.clear();
for (int k = 0; k < ROW_SIZE; k++) //針對每個棋子做預測路徑
for (int l = 0; l < COLUMN_SIZE; l++) //
if (imap.pChess[i][j]->isValid(ComXY(k, l), imap))
{
imap.pChess[i][j]->access.push_back(ComXY(k, l)); //輸入所有能移動的位置
if (k == imap.rKingPointer()->getPos().X && //被將軍與否
l == imap.rKingPointer()->getPos().Y &&
imap.pChess[i][j]->getColor()==false)
imap.rKingPointer()->enemy.push_back(imap.pChess[i][j]);
else if (k == imap.bKingPointer()->getPos().X &&
l == imap.bKingPointer()->getPos().Y &&
imap.pChess[i][j]->getColor()==true)
imap.bKingPointer()->enemy.push_back(imap.pChess[i][j]);
}
}
}
COORD ComXY(SHORT x, SHORT y)
{
return COORD{ x, y };
}
void Game::start()
{
system("cls"); //清除開始介面畫面
gui.setVisible(true);
gui.dwSize(25);
gui.displayGameScreen(GameMap, isWhosTurn);
playerControl();
reset();
}
void Game::setting() //設定電腦難易度 可以不要
{
int depth;
depth = gui.showDepthInput();
if (depth <= 0 || depth > 20) {
gui.showAlert(" 請輸入1~9的數字 ", 2000);
depth = gui.showDepthInput();
}
string out;
out += " 已設定難度為 ";
out += (depth + '0');
out += " ";
gui.showAlert(out, 2000);
init_engine(depth * 2);
}
void Game::Interface() //開始介面
{
while (true)
{
switch (gui.mainMenu()) {
case 1:
gamemode = 0;
PlaySound(NULL, NULL, NULL);
start();
break;
case 2:
gamemode = 1;
PlaySound(NULL, NULL, NULL);
start();
break;
case 3:
setting();
break;
case 4:
gui.displayAboutScreen();
break;
case 5:
exitGame();
break;
default:
break;
}
}
}
void Game::exitGame()
{
system("cls");
gui.displayExitScreen();
Sleep(200);//Delay 200ms
exit(EXIT_SUCCESS);
}
void Game::playerControl()
{
Player rPlayer(true); //Red Player
Player bPlayer(false); //Black Player
isWhosTurn = true; //Red 先開始
bool isMoveSuccess = false;
bool reChoose = false;
bool go = true; //control possible path
makeAccess(GameMap);
gui.gotoxy(CHESS_BOARD_X + cursorPos.X * 4, CHESS_BOARD_Y + cursorPos.Y * 2 + 1);
gui.displayGameScreen(GameMap, isWhosTurn);
CHAR InputKB = _getch();
while (true)
{
isMoveSuccess = false;
reChoose = false;
switch (InputKB)
{
case KB_UP:
if (cursorPos.Y > 0)
--cursorPos.Y;
break;
case KB_DOWN:
if (cursorPos.Y < 9)
++cursorPos.Y;
break;
case KB_LEFT:
if (cursorPos.X > 0)
--cursorPos.X;
break;
case KB_RIGHT:
if (cursorPos.X < 8)
++cursorPos.X;
break;
case KB_ENTER:
if (GameMap.pChess[cursorPos.X][cursorPos.Y] != NULL)
{
bool color = GameMap.pChess[cursorPos.X][cursorPos.Y]->getColor();
if (color != isWhosTurn)break;
gui.displayGameInfo(isWhosTurn, GameMap, GameMap.pChess[cursorPos.X][cursorPos.Y]);
gui.displayPossiblePath(GameMap.pChess[cursorPos.X][cursorPos.Y], GameMap); //顯示可以走的位置
gui.gotoxy(CHESS_BOARD_X + cursorPos.X * 4, CHESS_BOARD_Y + cursorPos.Y * 2 + 1);
COORD originalPos = cursorPos;
cursorPos = (color ? rPlayer : bPlayer).chooseMovePos(ComXY(cursorPos.X, cursorPos.Y), GameMap, isMoveSuccess, reChoose, gui);
if (reChoose) {
gui.displayChessboard(GameMap);
gui.displayGameInfo(isWhosTurn, GameMap);
}
if (!isMoveSuccess)
continue;
//---------------------------------------------------------
//if (color == true) {
string in;
in += ((originalPos.X + '0'));
in += ((originalPos.Y + '0'));
in += ((cursorPos.X + '0'));
in += ((cursorPos.Y + '0'));
on_human_move(in);
//}
//--------------------------------------------------------------
GameMap.chessStorageForRestorePointer()->clear();
gui.displayChessboard(GameMap);
gui.displayGameInfo(isWhosTurn, GameMap);
isWhosTurn = !isWhosTurn; //交換出棋方
makeAccess(GameMap);
gui.displayBattleSituation(GameMap);
gui.displayGameInfo(isWhosTurn, GameMap);
if (GameMap.bKingPointer()->isDeath()) { Sleep(1000); gui.showAlert(" 紅方勝利 ", 5000); return; }
if (GameMap.rKingPointer()->isDeath()){ Sleep(1000); gui.showAlert(" 黑方勝利 ", 5000); return; }
}
break;
case KB_ESC:
switch (gui.MenuInGame()) {
case 1://resume
break;
case 2://restart
if (gui.showConfirm(" 確定重新開始 ? ")) { //22 chars
reset();
gui.displayGameScreen(GameMap, isWhosTurn);
}
break;
case 3://back to main menu
if (gui.showConfirm(" 確定放棄目前戰局 ? "))
return;
break;
case 4://exit
if (gui.showConfirm(" 確定離開 ? "))
exitGame();
default:
break;
}
break;
case KB_44: //悔棋
if (gamemode == 1) {
gui.showAlert(" 電腦不想給你悔棋ㄏㄏ ", 2000);
break;
}
if (gui.showConfirm(" 確定悔棋 ? "))
{
if (GameMap.regret())
{
makeAccess(GameMap);
gui.displayGameScreen(GameMap, isWhosTurn);
}
else
gui.showAlert(" 沒有步數可以悔棋了 ! ", 2500);
}
break;
case KB_46: //還原
if (gamemode == 1) {
gui.showAlert(" 電腦不想給你悔棋ㄏㄏ ", 2000);
break;
}
if (gui.showConfirm(" 確定還原 ? "))
{
if (GameMap.restore())
{
makeAccess(GameMap);
gui.displayGameScreen(GameMap, isWhosTurn);
}
else
gui.showAlert(" 沒有步數可以還原了 ! ", 2500);
}
break;
default:
break;
}
if (gamemode == 1 && isWhosTurn == false) {
if (GameMap.rKingPointer()->enemy.size() > 0) {
bPlayer.move(GameMap.rKingPointer()->enemy.at(0)->getPos(), GameMap.rKingPointer()->getPos(), GameMap);
}
else {
string out;
out = generate_move();
bPlayer.move(ComXY(out.at(0) - '0', out.at(1) - '0'), ComXY(out.at(2) - '0', out.at(3) - '0'), GameMap);
}
isWhosTurn = true;
makeAccess(GameMap);
gui.displayChessboard(GameMap);
gui.displayBattleSituation(GameMap);
gui.displayGameInfo(isWhosTurn, GameMap);
if (GameMap.bKingPointer()->isDeath()) { Sleep(1000); gui.showAlert(" 紅方勝利 ", 5000); return; }
if (GameMap.rKingPointer()->isDeath()){ Sleep(1000); gui.showAlert(" 黑方勝利 ", 5000); return; }
}
gui.gotoxy(CHESS_BOARD_X + cursorPos.X * 4, CHESS_BOARD_Y + cursorPos.Y * 2 + 1);//回到游標原本的位置
gui.setVisible(true);
InputKB = _getch();
}
}
void Game::reset()
{
init_game();
GameMap.reset();
isWhosTurn = true;
cursorPos = ComXY(0, 0);
makeAccess(GameMap);
}
void Game::setGamemode(int mode)
{
if (mode >= 0 && mode <= 1)
gamemode = mode;
}
\ No newline at end of file
#ifndef GAME_H
#define GAME_H
#include <Windows.h>
#include <iostream>
#include <string>
#include "Map.h"
#include "GUI.h"
#include "XQWLight.h"
#include <conio.h> // _getch()
COORD ComXY(SHORT x, SHORT y); //將x, y轉成 sturctor COORD
bool operator ==(const COORD& a, const COORD& b);
class Game
{
public:
Game();
void Interface(); //顯示遊戲介面
private:
void reset();
void setting(); //進入設定畫面
void start(); //執行遊戲
void exitGame(); //離開遊戲
void playerControl(); //進入玩家控制
void makeAccess(Map& imap); //將每顆棋子能走的路徑push_back到棋子的vector中
void setGamemode(int mode);
int gamemode;//0=multiplayer, 1=singleplayer
COORD cursorPos;
Map GameMap;
bool isWhosTurn;
GUI gui;
};
#endif
\ No newline at end of file
#include "Map.h"
#include "Game.h"
Chess* Map::bKingPointer() const
{
return bKing;
}
Chess* Map::rKingPointer() const
{
return rKing;
}
std::vector<chessStorage>* Map::chessStoragePointer()
{
return &storage;
}
std::vector<chessStorage>* Map::chessStorageForRestorePointer()
{
return &storageForRestore;
}
const std::vector<chessStorage>* Map::chessStoragePointerConst() const
{
return &storage;
}
const std::vector<chessStorage>* Map::chessStorageForRestorePointerConst() const
{
return &storageForRestore;
}
Map::Map()
{
for (int i = 0; i < ROW_SIZE; i++)
for (int j = 0; j < COLUMN_SIZE; j++)
pChess[i][j] = NULL;
bKing = new King("將", false, ComXY(4, 0)); pChess[4][0] = bKing;
rKing = new King("帥", true, ComXY(4, 9)); pChess[4][9] = rKing;
bAdviser1 = new Adviser("士", false, ComXY(5, 0)); pChess[5][0] = bAdviser1;
bAdviser2 = new Adviser("士", false, ComXY(3, 0)); pChess[3][0] = bAdviser2;
rAdviser1 = new Adviser("仕", true, ComXY(5, 9)); pChess[5][9] = rAdviser1;
rAdviser2 = new Adviser("仕", true, ComXY(3, 9)); pChess[3][9] = rAdviser2;
bElephant1 = new Elephant("象", false, ComXY(6, 0)); pChess[6][0] = bElephant1;
bElephant2 = new Elephant("象", false, ComXY(2, 0)); pChess[2][0] = bElephant2;
rElephant1 = new Elephant("相", true, ComXY(6, 9)); pChess[6][9] = rElephant1;
rElephant2 = new Elephant("相", true, ComXY(2, 9)); pChess[2][9] = rElephant2;
bHorse1 = new Horse("馬", false, ComXY(1, 0)); pChess[1][0] = bHorse1;
bHorse2 = new Horse("馬", false, ComXY(7, 0)); pChess[7][0] = bHorse2;
rHorse1 = new Horse("傌", true, ComXY(1, 9)); pChess[1][9] = rHorse1;
rHorse2 = new Horse("傌", true, ComXY(7, 9)); pChess[7][9] = rHorse2;
bChariot1 = new Chariot("車", false, ComXY(0, 0)); pChess[0][0] = bChariot1;
bChariot2 = new Chariot("車", false, ComXY(8, 0)); pChess[8][0] = bChariot2;
rChariot1 = new Chariot("車", true, ComXY(0, 9)); pChess[0][9] = rChariot1;
rChariot2 = new Chariot("車", true, ComXY(8, 9)); pChess[8][9] = rChariot2;
bPawn1 = new Pawn("卒", false, ComXY(0, 3)); pChess[0][3] = bPawn1;
bPawn2 = new Pawn("卒", false, ComXY(2, 3)); pChess[2][3] = bPawn2;
bPawn3 = new Pawn("卒", false, ComXY(4, 3)); pChess[4][3] = bPawn3;
bPawn4 = new Pawn("卒", false, ComXY(6, 3)); pChess[6][3] = bPawn4;
bPawn5 = new Pawn("卒", false, ComXY(8, 3)); pChess[8][3] = bPawn5;
rPawn1 = new Pawn("兵", true, ComXY(0, 6)); pChess[0][6] = rPawn1;
rPawn2 = new Pawn("兵", true, ComXY(2, 6)); pChess[2][6] = rPawn2;
rPawn3 = new Pawn("兵", true, ComXY(4, 6)); pChess[4][6] = rPawn3;
rPawn4 = new Pawn("兵", true, ComXY(6, 6)); pChess[6][6] = rPawn4;
rPawn5 = new Pawn("兵", true, ComXY(8, 6)); pChess[8][6] = rPawn5;
bCannon1 = new Cannon("包", false, ComXY(1, 2)); pChess[1][2] = bCannon1;
bCannon2 = new Cannon("包", false, ComXY(7, 2)); pChess[7][2] = bCannon2;
rCannon1 = new Cannon("炮", true, ComXY(1, 7)); pChess[1][7] = rCannon1;
rCannon2 = new Cannon("炮", true, ComXY(7, 7)); pChess[7][7] = rCannon2;
}
Map::~Map()
{
delete bKing;
delete rKing;
delete bAdviser1;
delete bAdviser2;
delete rAdviser1;
delete rAdviser2;
delete bElephant1;
delete bElephant2;
delete rElephant1;
delete rElephant2;
delete bHorse1;
delete bHorse2;
delete rHorse1;
delete rHorse2;
delete bChariot1;
delete bChariot2;
delete rChariot1;
delete rChariot2;
delete bPawn1;
delete bPawn2;
delete bPawn3;
delete bPawn4;
delete bPawn5;
delete rPawn1;
delete rPawn2;
delete rPawn3;
delete rPawn4;
delete rPawn5;
delete bCannon1;
delete bCannon2;
delete rCannon1;
delete rCannon2;
}
short Map::checkKingToBeKilled(bool color) const
{
if ((color ? rKing : bKing)->enemy.size() != 0)
return true;
return false;
}
bool Map::regret()
{
if (storage.size() >= 2) {
for (int i = 0; i < 2; i++){
chessStorage temp = storage.back();
storageForRestore.push_back(temp); //悔棋動作存入還原區
storage.pop_back(); //刪除最後一筆資料
pChess[temp.moved->getPos().X][temp.moved->getPos().Y] = NULL;
temp.moved->setPos(temp.prePos);
pChess[temp.prePos.X][temp.prePos.Y] = temp.moved;
if (temp.dead != NULL)
{
temp.dead->setPos(temp.Pos);
pChess[temp.Pos.X][temp.Pos.Y] = temp.dead;
pChess[temp.Pos.X][temp.Pos.Y]->setAlive(true);
}
}
return true;
}
return false;
}
bool Map::restore()
{
if (storageForRestore.size() >= 2) {
for (int i = 0; i < 2; i++) {
chessStorage temp = storageForRestore.back();
storage.push_back(temp); //還原動作存入
storageForRestore.pop_back(); //刪除還原區最後一筆資料
temp.moved->setPos(temp.Pos);
pChess[temp.prePos.X][temp.prePos.Y] = NULL;
pChess[temp.Pos.X][temp.Pos.Y] = temp.moved;
if (temp.dead != NULL)
temp.dead->setAlive(false);
}
return true;
}
return false;
}
void Map::reset() {
delete bKing;
delete rKing;
delete bAdviser1;
delete bAdviser2;
delete rAdviser1;
delete rAdviser2;
delete bElephant1;
delete bElephant2;
delete rElephant1;
delete rElephant2;
delete bHorse1;
delete bHorse2;
delete rHorse1;
delete rHorse2;
delete bChariot1;
delete bChariot2;
delete rChariot1;
delete rChariot2;
delete bPawn1;
delete bPawn2;
delete bPawn3;
delete bPawn4;
delete bPawn5;
delete rPawn1;
delete rPawn2;
delete rPawn3;
delete rPawn4;
delete rPawn5;
delete bCannon1;
delete bCannon2;
delete rCannon1;
delete rCannon2;
for (int i = 0; i < ROW_SIZE; i++)
for (int j = 0; j < COLUMN_SIZE; j++)
pChess[i][j] = NULL;
bKing = new King("將", false, ComXY(4, 0)); pChess[4][0] = bKing;
rKing = new King("帥", true, ComXY(4, 9)); pChess[4][9] = rKing;
bAdviser1 = new Adviser("士", false, ComXY(5, 0)); pChess[5][0] = bAdviser1;
bAdviser2 = new Adviser("士", false, ComXY(3, 0)); pChess[3][0] = bAdviser2;
rAdviser1 = new Adviser("仕", true, ComXY(5, 9)); pChess[5][9] = rAdviser1;
rAdviser2 = new Adviser("仕", true, ComXY(3, 9)); pChess[3][9] = rAdviser2;
bElephant1 = new Elephant("象", false, ComXY(6, 0)); pChess[6][0] = bElephant1;
bElephant2 = new Elephant("象", false, ComXY(2, 0)); pChess[2][0] = bElephant2;
rElephant1 = new Elephant("相", true, ComXY(6, 9)); pChess[6][9] = rElephant1;
rElephant2 = new Elephant("相", true, ComXY(2, 9)); pChess[2][9] = rElephant2;
bHorse1 = new Horse("馬", false, ComXY(1, 0)); pChess[1][0] = bHorse1;
bHorse2 = new Horse("馬", false, ComXY(7, 0)); pChess[7][0] = bHorse2;
rHorse1 = new Horse("傌", true, ComXY(1, 9)); pChess[1][9] = rHorse1;
rHorse2 = new Horse("傌", true, ComXY(7, 9)); pChess[7][9] = rHorse2;
bChariot1 = new Chariot("車", false, ComXY(0, 0)); pChess[0][0] = bChariot1;
bChariot2 = new Chariot("車", false, ComXY(8, 0)); pChess[8][0] = bChariot2;
rChariot1 = new Chariot("車", true, ComXY(0, 9)); pChess[0][9] = rChariot1;
rChariot2 = new Chariot("車", true, ComXY(8, 9)); pChess[8][9] = rChariot2;
bPawn1 = new Pawn("卒", false, ComXY(0, 3)); pChess[0][3] = bPawn1;
bPawn2 = new Pawn("卒", false, ComXY(2, 3)); pChess[2][3] = bPawn2;
bPawn3 = new Pawn("卒", false, ComXY(4, 3)); pChess[4][3] = bPawn3;
bPawn4 = new Pawn("卒", false, ComXY(6, 3)); pChess[6][3] = bPawn4;
bPawn5 = new Pawn("卒", false, ComXY(8, 3)); pChess[8][3] = bPawn5;
rPawn1 = new Pawn("兵", true, ComXY(0, 6)); pChess[0][6] = rPawn1;
rPawn2 = new Pawn("兵", true, ComXY(2, 6)); pChess[2][6] = rPawn2;
rPawn3 = new Pawn("兵", true, ComXY(4, 6)); pChess[4][6] = rPawn3;
rPawn4 = new Pawn("兵", true, ComXY(6, 6)); pChess[6][6] = rPawn4;
rPawn5 = new Pawn("兵", true, ComXY(8, 6)); pChess[8][6] = rPawn5;
bCannon1 = new Cannon("包", false, ComXY(1, 2)); pChess[1][2] = bCannon1;
bCannon2 = new Cannon("包", false, ComXY(7, 2)); pChess[7][2] = bCannon2;
rCannon1 = new Cannon("炮", true, ComXY(1, 7)); pChess[1][7] = rCannon1;
rCannon2 = new Cannon("炮", true, ComXY(7, 7)); pChess[7][7] = rCannon2;
storage.clear();
storageForRestore.clear();
}
\ No newline at end of file
#ifndef MAP_H
#define MAP_H
#include "Chess.h"
#include <vector>
#define ROW_SIZE 9
#define COLUMN_SIZE 10
struct chessStorage
{
COORD prePos;
COORD Pos;
Chess *moved;
Chess *dead;
};
class Map
{
public:
Map();
~Map();
short checkKingToBeKilled(bool color) const;
bool regret();
bool restore();
void reset();
Chess* bKingPointer() const;
Chess* rKingPointer() const;
const std::vector<chessStorage>* chessStoragePointerConst() const;
const std::vector<chessStorage>* chessStorageForRestorePointerConst() const;
std::vector<chessStorage>* chessStoragePointer();
std::vector<chessStorage>* chessStorageForRestorePointer();
Chess* pChess[ROW_SIZE][COLUMN_SIZE];
private:
std::vector<chessStorage> storage;
std::vector<chessStorage> storageForRestore;
Chess* bKing;
Chess* rKing;
Chess* bAdviser1;
Chess* bAdviser2;
Chess* rAdviser1;
Chess* rAdviser2;
Chess* bElephant1;
Chess* bElephant2;
Chess* rElephant1;
Chess* rElephant2;
Chess* bHorse1;
Chess* bHorse2;
Chess* rHorse1;
Chess* rHorse2;
Chess* bChariot1;
Chess* bChariot2;
Chess* rChariot1;
Chess* rChariot2;
Chess* bPawn1;
Chess* bPawn2;
Chess* bPawn3;
Chess* bPawn4;
Chess* bPawn5;
Chess* rPawn1;
Chess* rPawn2;
Chess* rPawn3;
Chess* rPawn4;
Chess* rPawn5;
Chess* bCannon1;
Chess* bCannon2;
Chess* rCannon1;
Chess* rCannon2;
};
#endif
#ifndef PLAYER_H
#define PLAYER_H
#include "Map.h"
#include "GUI.h"
#include "XQWLight.h"
using namespace XQWLight;
class Player
{
public:
Player()=delete;
Player(bool icolor);
SHORT move(COORD cursorP, COORD moveP, Map& map);
COORD chooseMovePos(COORD cursorPos, Map& map, bool& isMoveSuc, bool& reChoose, GUI& gui);
private:
bool color; //true = false ¤
};
#endif
\ No newline at end of file
/**
* XiangQi Wizard Light - A Very Simple Chinese Chess Program
* Designed by Morning Yellow, Version: 0.6, Last Modified: Mar. 2008
* Copyright (C) 2004-2008 www.elephantbase.net
*
* 象棋小巫师 0.6 的目标:
* 一、实现开局库;
* 二、实现PVS(主要变例搜索);
* 三、把根节点的搜索单独处理,增加搜索的随机性;
* 四、克服由长将引起的置换表的不稳定性。
*/
/////////////////////////////////////////////////////////////////////////////
// Name: XQWLight.cpp
// Created: 10/11/2008
//
// Original Name: XQWL06.CPP (of the package 'xqwlight_win32.7z')
//
// Description: This is 'XQWLight' Engine to interface with HOXChess.
// XQWLight is an open-source (?) Xiangqi AI Engine
// written by Huang Chen at www.elephantbase.net
//
// (Original Chinese URL)
// http://www.elephantbase.net/computer/stepbystep1.htm
//
// (Translated English URL using Goold Translate)
// http://74.125.93.104/translate_c?hl=en&langpair=
// zh-CN|en&u=http://www.elephantbase.net/computer/stepbystep1.htm&
// usg=ALkJrhj7W0v3J1P-xmbufsWzYq7uKciL1w
/////////////////////////////////////////////////////////////////////////////
#include "XQWLight.h"
#include <time.h>
//#include <windows.h>
//#include "resource.h"
#include <sstream> // ostringstream
#include <cstring>
#include <cstdlib>
/////////////////////////////////////////////////////////////////////////////
/////// START of HPHAN's changes /////////////
// *** Typedef (to avoid having to include WinDef.h) ***
typedef unsigned char BYTE;
typedef int BOOL;
typedef unsigned short WORD;
/* Not work on Ubuntu: typedef unsigned long DWORD; */
typedef unsigned int DWORD;
#ifndef FALSE
# define FALSE 0
#endif
#ifndef TRUE
# define TRUE 1
#endif
// *** Additional variables ***
static int s_search_depth = 7; // Search Depth
static int s_search_time = 1; // In seconds (search-time)
static const char* s_opening_book = "../plugins/BOOK.DAT";
/////// END of HPHAN's changes /////////////
///////////////////////////////////////////////////////////////////////////////
const int SQUARE_SIZE = 56;
const int BOARD_EDGE = 8;
const int BOARD_WIDTH = BOARD_EDGE + SQUARE_SIZE * 9 + BOARD_EDGE;
const int BOARD_HEIGHT = BOARD_EDGE + SQUARE_SIZE * 10 + BOARD_EDGE;
// 棋盘范围
const int RANK_TOP = 3;
const int RANK_BOTTOM = 12;
const int FILE_LEFT = 3;
const int FILE_RIGHT = 11;
// 棋子编号
const int PIECE_KING = 0;
const int PIECE_ADVISOR = 1;
const int PIECE_BISHOP = 2;
const int PIECE_KNIGHT = 3;
const int PIECE_ROOK = 4;
const int PIECE_CANNON = 5;
const int PIECE_PAWN = 6;
// 其他常数
const int MAX_GEN_MOVES = 128; // 最大的生成走法数
const int MAX_MOVES = 256; // 最大的历史走法数
const int LIMIT_DEPTH = 64; // 最大的搜索深度
const int MATE_VALUE = 10000; // 最高分值,即将死的分值
const int BAN_VALUE = MATE_VALUE - 100; // 长将判负的分值,低于该值将不写入置换表
const int WIN_VALUE = MATE_VALUE - 200; // 搜索出胜负的分值界限,超出此值就说明已经搜索出杀棋了
const int DRAW_VALUE = 20; // 和棋时返回的分数(取负值)
const int ADVANCED_VALUE = 3; // 先行权分值
const int RANDOM_MASK = 7; // 随机性分值
const int NULL_MARGIN = 400; // 空步裁剪的子力边界
const int NULL_DEPTH = 2; // 空步裁剪的裁剪深度
const int HASH_SIZE = 1 << 20; // 置换表大小
const int HASH_ALPHA = 1; // ALPHA节点的置换表项
const int HASH_BETA = 2; // BETA节点的置换表项
const int HASH_PV = 3; // PV节点的置换表项
const int BOOK_SIZE = 16384; // 开局库大小
// 判断棋子是否在棋盘中的数组
static const char ccInBoard[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
// 判断棋子是否在九宫的数组
static const char ccInFort[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
// 判断步长是否符合特定走法的数组,1=帅(将),2=仕(士),3=相(象)
static const char ccLegalSpan[512] = {
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 2, 1, 2, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 2, 1, 2, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0
};
// 根据步长判断马是否蹩腿的数组
static const char ccKnightPin[512] = {
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, -16, 0, -16, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0
};
// 帅(将)的步长
static const char ccKingDelta[4] = { -16, -1, 1, 16 };
// 仕(士)的步长
static const char ccAdvisorDelta[4] = { -17, -15, 15, 17 };
// 马的步长,以帅(将)的步长作为马腿
static const char ccKnightDelta[4][2] = { { -33, -31 }, { -18, 14 }, { -14, 18 }, { 31, 33 } };
// 马被将军的步长,以仕(士)的步长作为马腿
static const char ccKnightCheckDelta[4][2] = { { -33, -18 }, { -31, -14 }, { 14, 31 }, { 18, 33 } };
// 棋盘初始设置
static const BYTE cucpcStartup[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 20, 19, 18, 17, 16, 17, 18, 19, 20, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 21, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0,
0, 0, 0, 22, 0, 22, 0, 22, 0, 22, 0, 22, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 14, 0, 14, 0, 14, 0, 14, 0, 14, 0, 0, 0, 0,
0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 12, 11, 10, 9, 8, 9, 10, 11, 12, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
// 子力位置价值表
static const BYTE cucvlPiecePos[7][256] = {
{ // 帅(将)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 11, 15, 11, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, { // 仕(士)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 20, 0, 20, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 20, 0, 20, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, { // 相(象)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 20, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 18, 0, 0, 0, 23, 0, 0, 0, 18, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 20, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, { // 马
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 90, 90, 90, 96, 90, 96, 90, 90, 90, 0, 0, 0, 0,
0, 0, 0, 90, 96, 103, 97, 94, 97, 103, 96, 90, 0, 0, 0, 0,
0, 0, 0, 92, 98, 99, 103, 99, 103, 99, 98, 92, 0, 0, 0, 0,
0, 0, 0, 93, 108, 100, 107, 100, 107, 100, 108, 93, 0, 0, 0, 0,
0, 0, 0, 90, 100, 99, 103, 104, 103, 99, 100, 90, 0, 0, 0, 0,
0, 0, 0, 90, 98, 101, 102, 103, 102, 101, 98, 90, 0, 0, 0, 0,
0, 0, 0, 92, 94, 98, 95, 98, 95, 98, 94, 92, 0, 0, 0, 0,
0, 0, 0, 93, 92, 94, 95, 92, 95, 94, 92, 93, 0, 0, 0, 0,
0, 0, 0, 85, 90, 92, 93, 78, 93, 92, 90, 85, 0, 0, 0, 0,
0, 0, 0, 88, 85, 90, 88, 90, 88, 90, 85, 88, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, { // 车
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 206, 208, 207, 213, 214, 213, 207, 208, 206, 0, 0, 0, 0,
0, 0, 0, 206, 212, 209, 216, 233, 216, 209, 212, 206, 0, 0, 0, 0,
0, 0, 0, 206, 208, 207, 214, 216, 214, 207, 208, 206, 0, 0, 0, 0,
0, 0, 0, 206, 213, 213, 216, 216, 216, 213, 213, 206, 0, 0, 0, 0,
0, 0, 0, 208, 211, 211, 214, 215, 214, 211, 211, 208, 0, 0, 0, 0,
0, 0, 0, 208, 212, 212, 214, 215, 214, 212, 212, 208, 0, 0, 0, 0,
0, 0, 0, 204, 209, 204, 212, 214, 212, 204, 209, 204, 0, 0, 0, 0,
0, 0, 0, 198, 208, 204, 212, 212, 212, 204, 208, 198, 0, 0, 0, 0,
0, 0, 0, 200, 208, 206, 212, 200, 212, 206, 208, 200, 0, 0, 0, 0,
0, 0, 0, 194, 206, 204, 212, 200, 212, 204, 206, 194, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, { // 炮
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 100, 100, 96, 91, 90, 91, 96, 100, 100, 0, 0, 0, 0,
0, 0, 0, 98, 98, 96, 92, 89, 92, 96, 98, 98, 0, 0, 0, 0,
0, 0, 0, 97, 97, 96, 91, 92, 91, 96, 97, 97, 0, 0, 0, 0,
0, 0, 0, 96, 99, 99, 98, 100, 98, 99, 99, 96, 0, 0, 0, 0,
0, 0, 0, 96, 96, 96, 96, 100, 96, 96, 96, 96, 0, 0, 0, 0,
0, 0, 0, 95, 96, 99, 96, 100, 96, 99, 96, 95, 0, 0, 0, 0,
0, 0, 0, 96, 96, 96, 96, 96, 96, 96, 96, 96, 0, 0, 0, 0,
0, 0, 0, 97, 96, 100, 99, 101, 99, 100, 96, 97, 0, 0, 0, 0,
0, 0, 0, 96, 97, 98, 98, 98, 98, 98, 97, 96, 0, 0, 0, 0,
0, 0, 0, 96, 96, 97, 99, 99, 99, 97, 96, 96, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, { // 兵(卒)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 9, 9, 9, 11, 13, 11, 9, 9, 9, 0, 0, 0, 0,
0, 0, 0, 19, 24, 34, 42, 44, 42, 34, 24, 19, 0, 0, 0, 0,
0, 0, 0, 19, 24, 32, 37, 37, 37, 32, 24, 19, 0, 0, 0, 0,
0, 0, 0, 19, 23, 27, 29, 30, 29, 27, 23, 19, 0, 0, 0, 0,
0, 0, 0, 14, 18, 20, 27, 29, 27, 20, 18, 14, 0, 0, 0, 0,
0, 0, 0, 7, 0, 13, 0, 16, 0, 13, 0, 7, 0, 0, 0, 0,
0, 0, 0, 7, 0, 7, 0, 15, 0, 7, 0, 7, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}
};
// 判断棋子是否在棋盘中
inline BOOL IN_BOARD(int sq) {
return ccInBoard[sq] != 0;
}
// 判断棋子是否在九宫中
inline BOOL IN_FORT(int sq) {
return ccInFort[sq] != 0;
}
// 获得格子的横坐标
inline int RANK_Y(int sq) {
return sq >> 4;
}
// 获得格子的纵坐标
inline int FILE_X(int sq) {
return sq & 15;
}
// 根据纵坐标和横坐标获得格子
inline int COORD_XY(int x, int y) {
return x + (y << 4);
}
// 翻转格子
inline int SQUARE_FLIP(int sq) {
return 254 - sq;
}
// 纵坐标水平镜像
inline int FILE_FLIP(int x) {
return 14 - x;
}
// 横坐标垂直镜像
inline int RANK_FLIP(int y) {
return 15 - y;
}
// 格子水平镜像
inline int MIRROR_SQUARE(int sq) {
return COORD_XY(FILE_FLIP(FILE_X(sq)), RANK_Y(sq));
}
// 格子水平镜像
inline int SQUARE_FORWARD(int sq, int sd) {
return sq - 16 + (sd << 5);
}
// 走法是否符合帅(将)的步长
inline BOOL KING_SPAN(int sqSrc, int sqDst) {
return ccLegalSpan[sqDst - sqSrc + 256] == 1;
}
// 走法是否符合仕(士)的步长
inline BOOL ADVISOR_SPAN(int sqSrc, int sqDst) {
return ccLegalSpan[sqDst - sqSrc + 256] == 2;
}
// 走法是否符合相(象)的步长
inline BOOL BISHOP_SPAN(int sqSrc, int sqDst) {
return ccLegalSpan[sqDst - sqSrc + 256] == 3;
}
// 相(象)眼的位置
inline int BISHOP_PIN(int sqSrc, int sqDst) {
return (sqSrc + sqDst) >> 1;
}
// 马腿的位置
inline int KNIGHT_PIN(int sqSrc, int sqDst) {
return sqSrc + ccKnightPin[sqDst - sqSrc + 256];
}
// 是否未过河
inline BOOL HOME_HALF(int sq, int sd) {
return (sq & 0x80) != (sd << 7);
}
// 是否已过河
inline BOOL AWAY_HALF(int sq, int sd) {
return (sq & 0x80) == (sd << 7);
}
// 是否在河的同一边
inline BOOL SAME_HALF(int sqSrc, int sqDst) {
return ((sqSrc ^ sqDst) & 0x80) == 0;
}
// 是否在同一行
inline BOOL SAME_RANK(int sqSrc, int sqDst) {
return ((sqSrc ^ sqDst) & 0xf0) == 0;
}
// 是否在同一列
inline BOOL SAME_FILE(int sqSrc, int sqDst) {
return ((sqSrc ^ sqDst) & 0x0f) == 0;
}
// 获得红黑标记(红子是8,黑子是16)
inline int SIDE_TAG(int sd) {
return 8 + (sd << 3);
}
// 获得对方红黑标记
inline int OPP_SIDE_TAG(int sd) {
return 16 - (sd << 3);
}
// 获得走法的起点
inline int SRC(int mv) {
return mv & 255;
}
// 获得走法的终点
inline int DST(int mv) {
return mv >> 8;
}
// 根据起点和终点获得走法
inline int MOVE(int sqSrc, int sqDst) {
return sqSrc + sqDst * 256;
}
// 走法水平镜像
inline int MIRROR_MOVE(int mv) {
return MOVE(MIRROR_SQUARE(SRC(mv)), MIRROR_SQUARE(DST(mv)));
}
// RC4密码流生成器
struct RC4Struct {
BYTE s[256];
int x, y;
void InitZero(void); // 用空密钥初始化密码流生成器
BYTE NextByte(void) { // 生成密码流的下一个字节
BYTE uc;
x = (x + 1) & 255;
y = (y + s[x]) & 255;
uc = s[x];
s[x] = s[y];
s[y] = uc;
return s[(s[x] + s[y]) & 255];
}
DWORD NextLong(void) { // 生成密码流的下四个字节
BYTE uc0, uc1, uc2, uc3;
uc0 = NextByte();
uc1 = NextByte();
uc2 = NextByte();
uc3 = NextByte();
return uc0 + (uc1 << 8) + (uc2 << 16) + (uc3 << 24);
}
};
// 用空密钥初始化密码流生成器
void RC4Struct::InitZero(void) {
int i, j;
BYTE uc;
x = y = j = 0;
for (i = 0; i < 256; i++) {
s[i] = i;
}
for (i = 0; i < 256; i++) {
j = (j + s[i]) & 255;
uc = s[i];
s[i] = s[j];
s[j] = uc;
}
}
// Zobrist结构
struct ZobristStruct {
DWORD dwKey, dwLock0, dwLock1;
void InitZero(void) { // 用零填充Zobrist
dwKey = dwLock0 = dwLock1 = 0;
}
void InitRC4(RC4Struct &rc4) { // 用密码流填充Zobrist
dwKey = rc4.NextLong();
dwLock0 = rc4.NextLong();
dwLock1 = rc4.NextLong();
}
void Xor(const ZobristStruct &zobr) { // 执行XOR操作
dwKey ^= zobr.dwKey;
dwLock0 ^= zobr.dwLock0;
dwLock1 ^= zobr.dwLock1;
}
void Xor(const ZobristStruct &zobr1, const ZobristStruct &zobr2) {
dwKey ^= zobr1.dwKey ^ zobr2.dwKey;
dwLock0 ^= zobr1.dwLock0 ^ zobr2.dwLock0;
dwLock1 ^= zobr1.dwLock1 ^ zobr2.dwLock1;
}
};
// Zobrist表
static struct {
ZobristStruct Player;
ZobristStruct Table[14][256];
} Zobrist;
// 初始化Zobrist表
static void InitZobrist(void) {
int i, j;
RC4Struct rc4;
rc4.InitZero();
Zobrist.Player.InitRC4(rc4);
for (i = 0; i < 14; i++) {
for (j = 0; j < 256; j++) {
Zobrist.Table[i][j].InitRC4(rc4);
}
}
}
// 历史走法信息(占4字节)
struct MoveStruct {
WORD wmv;
BYTE ucpcCaptured, ucbCheck;
DWORD dwKey;
void Set(int mv, int pcCaptured, BOOL bCheck, DWORD dwKey_) {
wmv = mv;
ucpcCaptured = pcCaptured;
ucbCheck = bCheck;
dwKey = dwKey_;
}
}; // mvs
// 局面结构
struct PositionStruct {
int sdPlayer; // 轮到谁走,0=红方,1=黑方
BYTE ucpcSquares[256]; // 棋盘上的棋子
int vlWhite, vlBlack; // 红、黑双方的子力价值
int nDistance, nMoveNum; // 距离根节点的步数,历史走法数
MoveStruct mvsList[MAX_MOVES]; // 历史走法信息列表
ZobristStruct zobr; // Zobrist
void ClearBoard(void) { // 清空棋盘
sdPlayer = vlWhite = vlBlack = nDistance = 0;
memset(ucpcSquares, 0, 256);
zobr.InitZero();
}
void SetIrrev(void) { // 清空(初始化)历史走法信息
mvsList[0].Set(0, 0, Checked(), zobr.dwKey);
nMoveNum = 1;
}
void Startup(unsigned char board[10][9]); // 初始化棋盘
void ChangeSide(void) { // 交换走子方
sdPlayer = 1 - sdPlayer;
zobr.Xor(Zobrist.Player);
}
void AddPiece(int sq, int pc) { // 在棋盘上放一枚棋子
ucpcSquares[sq] = pc;
// 红方加分,黑方(注意"cucvlPiecePos"取值要颠倒)减分
if (pc < 16) {
vlWhite += cucvlPiecePos[pc - 8][sq];
zobr.Xor(Zobrist.Table[pc - 8][sq]);
}
else {
vlBlack += cucvlPiecePos[pc - 16][SQUARE_FLIP(sq)];
zobr.Xor(Zobrist.Table[pc - 9][sq]);
}
}
void DelPiece(int sq, int pc) { // 从棋盘上拿走一枚棋子
ucpcSquares[sq] = 0;
// 红方减分,黑方(注意"cucvlPiecePos"取值要颠倒)加分
if (pc < 16) {
vlWhite -= cucvlPiecePos[pc - 8][sq];
zobr.Xor(Zobrist.Table[pc - 8][sq]);
}
else {
vlBlack -= cucvlPiecePos[pc - 16][SQUARE_FLIP(sq)];
zobr.Xor(Zobrist.Table[pc - 9][sq]);
}
}
int Evaluate(void) const { // 局面评价函数
return (sdPlayer == 0 ? vlWhite - vlBlack : vlBlack - vlWhite) + ADVANCED_VALUE;
}
BOOL InCheck(void) const { // 是否被将军
return mvsList[nMoveNum - 1].ucbCheck;
}
BOOL Captured(void) const { // 上一步是否吃子
return mvsList[nMoveNum - 1].ucpcCaptured != 0;
}
int MovePiece(int mv); // 搬一步棋的棋子
void UndoMovePiece(int mv, int pcCaptured); // 撤消搬一步棋的棋子
BOOL MakeMove(int mv); // 走一步棋
void UndoMakeMove(void) { // 撤消走一步棋
nDistance--;
nMoveNum--;
ChangeSide();
UndoMovePiece(mvsList[nMoveNum].wmv, mvsList[nMoveNum].ucpcCaptured);
}
void NullMove(void) { // 走一步空步
DWORD dwKey;
dwKey = zobr.dwKey;
ChangeSide();
mvsList[nMoveNum].Set(0, 0, FALSE, dwKey);
nMoveNum++;
nDistance++;
}
void UndoNullMove(void) { // 撤消走一步空步
nDistance--;
nMoveNum--;
ChangeSide();
}
// 生成所有走法,如果"bCapture"为"TRUE"则只生成吃子走法
int GenerateMoves(int *mvs, BOOL bCapture = FALSE) const;
BOOL LegalMove(int mv) const; // 判断走法是否合理
BOOL Checked(void) const; // 判断是否被将军
BOOL IsMate(void); // 判断是否被杀
int DrawValue(void) const { // 和棋分值
return (nDistance & 1) == 0 ? -DRAW_VALUE : DRAW_VALUE;
}
int RepStatus(int nRecur = 1) const; // 检测重复局面
int RepValue(int nRepStatus) const { // 重复局面分值
int vlReturn;
vlReturn = ((nRepStatus & 2) == 0 ? 0 : nDistance - BAN_VALUE) +
((nRepStatus & 4) == 0 ? 0 : BAN_VALUE - nDistance);
return vlReturn == 0 ? DrawValue() : vlReturn;
}
BOOL NullOkay(void) const { // 判断是否允许空步裁剪
return (sdPlayer == 0 ? vlWhite : vlBlack) > NULL_MARGIN;
}
void Mirror(PositionStruct &posMirror) const; // 对局面镜像
};
// 初始化棋盘
void PositionStruct::Startup(unsigned char board[10][9]) {
int sq;
ClearBoard();
if (board != NULL)
{
for (int i = 0; i<10; i++)
for (int j = 0; j <9; j++){
if (board[i][j] > 0){
sq = (3 + i) * 16 + 3 + j;
AddPiece(sq, board[i][j]);
}
}
}
else
{
int pc;
for (sq = 0; sq < 256; sq++) {
pc = cucpcStartup[sq];
if (pc != 0) {
AddPiece(sq, pc);
}
}
}
SetIrrev();
}
// 搬一步棋的棋子
int PositionStruct::MovePiece(int mv) {
int sqSrc, sqDst, pc, pcCaptured;
sqSrc = SRC(mv);
sqDst = DST(mv);
pcCaptured = ucpcSquares[sqDst];
if (pcCaptured != 0) {
DelPiece(sqDst, pcCaptured);
}
pc = ucpcSquares[sqSrc];
DelPiece(sqSrc, pc);
AddPiece(sqDst, pc);
return pcCaptured;
}
// 撤消搬一步棋的棋子
void PositionStruct::UndoMovePiece(int mv, int pcCaptured) {
int sqSrc, sqDst, pc;
sqSrc = SRC(mv);
sqDst = DST(mv);
pc = ucpcSquares[sqDst];
DelPiece(sqDst, pc);
AddPiece(sqSrc, pc);
if (pcCaptured != 0) {
AddPiece(sqDst, pcCaptured);
}
}
// 走一步棋
BOOL PositionStruct::MakeMove(int mv) {
int pcCaptured;
DWORD dwKey;
dwKey = zobr.dwKey;
pcCaptured = MovePiece(mv);
if (Checked()) {
UndoMovePiece(mv, pcCaptured);
return FALSE;
}
ChangeSide();
mvsList[nMoveNum].Set(mv, pcCaptured, Checked(), dwKey);
nMoveNum++;
nDistance++;
return TRUE;
}
// "GenerateMoves"参数
const BOOL GEN_CAPTURE = TRUE;
// 生成所有走法,如果"bCapture"为"TRUE"则只生成吃子走法
int PositionStruct::GenerateMoves(int *mvs, BOOL bCapture) const {
int i, j, nGenMoves, nDelta, sqSrc, sqDst;
int pcSelfSide, pcOppSide, pcSrc, pcDst;
// 生成所有走法,需要经过以下几个步骤:
nGenMoves = 0;
pcSelfSide = SIDE_TAG(sdPlayer);
pcOppSide = OPP_SIDE_TAG(sdPlayer);
for (sqSrc = 0; sqSrc < 256; sqSrc++) {
// 1. 找到一个本方棋子,再做以下判断:
pcSrc = ucpcSquares[sqSrc];
if ((pcSrc & pcSelfSide) == 0) {
continue;
}
// 2. 根据棋子确定走法
switch (pcSrc - pcSelfSide) {
case PIECE_KING:
for (i = 0; i < 4; i++) {
sqDst = sqSrc + ccKingDelta[i];
if (!IN_FORT(sqDst)) {
continue;
}
pcDst = ucpcSquares[sqDst];
if (bCapture ? (pcDst & pcOppSide) != 0 : (pcDst & pcSelfSide) == 0) {
mvs[nGenMoves] = MOVE(sqSrc, sqDst);
nGenMoves++;
}
}
break;
case PIECE_ADVISOR:
for (i = 0; i < 4; i++) {
sqDst = sqSrc + ccAdvisorDelta[i];
if (!IN_FORT(sqDst)) {
continue;
}
pcDst = ucpcSquares[sqDst];
if (bCapture ? (pcDst & pcOppSide) != 0 : (pcDst & pcSelfSide) == 0) {
mvs[nGenMoves] = MOVE(sqSrc, sqDst);
nGenMoves++;
}
}
break;
case PIECE_BISHOP:
for (i = 0; i < 4; i++) {
sqDst = sqSrc + ccAdvisorDelta[i];
if (!(IN_BOARD(sqDst) && HOME_HALF(sqDst, sdPlayer) && ucpcSquares[sqDst] == 0)) {
continue;
}
sqDst += ccAdvisorDelta[i];
pcDst = ucpcSquares[sqDst];
if (bCapture ? (pcDst & pcOppSide) != 0 : (pcDst & pcSelfSide) == 0) {
mvs[nGenMoves] = MOVE(sqSrc, sqDst);
nGenMoves++;
}
}
break;
case PIECE_KNIGHT:
for (i = 0; i < 4; i++) {
sqDst = sqSrc + ccKingDelta[i];
if (ucpcSquares[sqDst] != 0) {
continue;
}
for (j = 0; j < 2; j++) {
sqDst = sqSrc + ccKnightDelta[i][j];
if (!IN_BOARD(sqDst)) {
continue;
}
pcDst = ucpcSquares[sqDst];
if (bCapture ? (pcDst & pcOppSide) != 0 : (pcDst & pcSelfSide) == 0) {
mvs[nGenMoves] = MOVE(sqSrc, sqDst);
nGenMoves++;
}
}
}
break;
case PIECE_ROOK:
for (i = 0; i < 4; i++) {
nDelta = ccKingDelta[i];
sqDst = sqSrc + nDelta;
while (IN_BOARD(sqDst)) {
pcDst = ucpcSquares[sqDst];
if (pcDst == 0) {
if (!bCapture) {
mvs[nGenMoves] = MOVE(sqSrc, sqDst);
nGenMoves++;
}
}
else {
if ((pcDst & pcOppSide) != 0) {
mvs[nGenMoves] = MOVE(sqSrc, sqDst);
nGenMoves++;
}
break;
}
sqDst += nDelta;
}
}
break;
case PIECE_CANNON:
for (i = 0; i < 4; i++) {
nDelta = ccKingDelta[i];
sqDst = sqSrc + nDelta;
while (IN_BOARD(sqDst)) {
pcDst = ucpcSquares[sqDst];
if (pcDst == 0) {
if (!bCapture) {
mvs[nGenMoves] = MOVE(sqSrc, sqDst);
nGenMoves++;
}
}
else {
break;
}
sqDst += nDelta;
}
sqDst += nDelta;
while (IN_BOARD(sqDst)) {
pcDst = ucpcSquares[sqDst];
if (pcDst != 0) {
if ((pcDst & pcOppSide) != 0) {
mvs[nGenMoves] = MOVE(sqSrc, sqDst);
nGenMoves++;
}
break;
}
sqDst += nDelta;
}
}
break;
case PIECE_PAWN:
sqDst = SQUARE_FORWARD(sqSrc, sdPlayer);
if (IN_BOARD(sqDst)) {
pcDst = ucpcSquares[sqDst];
if (bCapture ? (pcDst & pcOppSide) != 0 : (pcDst & pcSelfSide) == 0) {
mvs[nGenMoves] = MOVE(sqSrc, sqDst);
nGenMoves++;
}
}
if (AWAY_HALF(sqSrc, sdPlayer)) {
for (nDelta = -1; nDelta <= 1; nDelta += 2) {
sqDst = sqSrc + nDelta;
if (IN_BOARD(sqDst)) {
pcDst = ucpcSquares[sqDst];
if (bCapture ? (pcDst & pcOppSide) != 0 : (pcDst & pcSelfSide) == 0) {
mvs[nGenMoves] = MOVE(sqSrc, sqDst);
nGenMoves++;
}
}
}
}
break;
}
}
return nGenMoves;
}
// 判断走法是否合理
BOOL PositionStruct::LegalMove(int mv) const {
int sqSrc, sqDst, sqPin;
int pcSelfSide, pcSrc, pcDst, nDelta;
// 判断走法是否合法,需要经过以下的判断过程:
// 1. 判断起始格是否有自己的棋子
sqSrc = SRC(mv);
pcSrc = ucpcSquares[sqSrc];
pcSelfSide = SIDE_TAG(sdPlayer);
if ((pcSrc & pcSelfSide) == 0) {
return FALSE;
}
// 2. 判断目标格是否有自己的棋子
sqDst = DST(mv);
pcDst = ucpcSquares[sqDst];
if ((pcDst & pcSelfSide) != 0) {
return FALSE;
}
// 3. 根据棋子的类型检查走法是否合理
switch (pcSrc - pcSelfSide) {
case PIECE_KING:
return IN_FORT(sqDst) && KING_SPAN(sqSrc, sqDst);
case PIECE_ADVISOR:
return IN_FORT(sqDst) && ADVISOR_SPAN(sqSrc, sqDst);
case PIECE_BISHOP:
return SAME_HALF(sqSrc, sqDst) && BISHOP_SPAN(sqSrc, sqDst) &&
ucpcSquares[BISHOP_PIN(sqSrc, sqDst)] == 0;
case PIECE_KNIGHT:
sqPin = KNIGHT_PIN(sqSrc, sqDst);
return sqPin != sqSrc && ucpcSquares[sqPin] == 0;
case PIECE_ROOK:
case PIECE_CANNON:
if (SAME_RANK(sqSrc, sqDst)) {
nDelta = (sqDst < sqSrc ? -1 : 1);
}
else if (SAME_FILE(sqSrc, sqDst)) {
nDelta = (sqDst < sqSrc ? -16 : 16);
}
else {
return FALSE;
}
sqPin = sqSrc + nDelta;
while (sqPin != sqDst && ucpcSquares[sqPin] == 0) {
sqPin += nDelta;
}
if (sqPin == sqDst) {
return pcDst == 0 || pcSrc - pcSelfSide == PIECE_ROOK;
}
else if (pcDst != 0 && pcSrc - pcSelfSide == PIECE_CANNON) {
sqPin += nDelta;
while (sqPin != sqDst && ucpcSquares[sqPin] == 0) {
sqPin += nDelta;
}
return sqPin == sqDst;
}
else {
return FALSE;
}
case PIECE_PAWN:
if (AWAY_HALF(sqDst, sdPlayer) && (sqDst == sqSrc - 1 || sqDst == sqSrc + 1)) {
return TRUE;
}
return sqDst == SQUARE_FORWARD(sqSrc, sdPlayer);
default:
return FALSE;
}
}
// 判断是否被将军
BOOL PositionStruct::Checked() const {
int i, j, sqSrc, sqDst;
int pcSelfSide, pcOppSide, pcDst, nDelta;
pcSelfSide = SIDE_TAG(sdPlayer);
pcOppSide = OPP_SIDE_TAG(sdPlayer);
// 找到棋盘上的帅(将),再做以下判断:
for (sqSrc = 0; sqSrc < 256; sqSrc++) {
if (ucpcSquares[sqSrc] != pcSelfSide + PIECE_KING) {
continue;
}
// 1. 判断是否被对方的兵(卒)将军
if (ucpcSquares[SQUARE_FORWARD(sqSrc, sdPlayer)] == pcOppSide + PIECE_PAWN) {
return TRUE;
}
for (nDelta = -1; nDelta <= 1; nDelta += 2) {
if (ucpcSquares[sqSrc + nDelta] == pcOppSide + PIECE_PAWN) {
return TRUE;
}
}
// 2. 判断是否被对方的马将军(以仕(士)的步长当作马腿)
for (i = 0; i < 4; i++) {
if (ucpcSquares[sqSrc + ccAdvisorDelta[i]] != 0) {
continue;
}
for (j = 0; j < 2; j++) {
pcDst = ucpcSquares[sqSrc + ccKnightCheckDelta[i][j]];
if (pcDst == pcOppSide + PIECE_KNIGHT) {
return TRUE;
}
}
}
// 3. 判断是否被对方的车或炮将军(包括将帅对脸)
for (i = 0; i < 4; i++) {
nDelta = ccKingDelta[i];
sqDst = sqSrc + nDelta;
while (IN_BOARD(sqDst)) {
pcDst = ucpcSquares[sqDst];
if (pcDst != 0) {
if (pcDst == pcOppSide + PIECE_ROOK || pcDst == pcOppSide + PIECE_KING) {
return TRUE;
}
break;
}
sqDst += nDelta;
}
sqDst += nDelta;
while (IN_BOARD(sqDst)) {
int pcDst = ucpcSquares[sqDst];
if (pcDst != 0) {
if (pcDst == pcOppSide + PIECE_CANNON) {
return TRUE;
}
break;
}
sqDst += nDelta;
}
}
return FALSE;
}
return FALSE;
}
// 判断是否被杀
BOOL PositionStruct::IsMate(void) {
int i, nGenMoveNum, pcCaptured;
int mvs[MAX_GEN_MOVES];
nGenMoveNum = GenerateMoves(mvs);
for (i = 0; i < nGenMoveNum; i++) {
pcCaptured = MovePiece(mvs[i]);
if (!Checked()) {
UndoMovePiece(mvs[i], pcCaptured);
return FALSE;
}
else {
UndoMovePiece(mvs[i], pcCaptured);
}
}
return TRUE;
}
// 检测重复局面
int PositionStruct::RepStatus(int nRecur) const {
BOOL bSelfSide, bPerpCheck, bOppPerpCheck;
const MoveStruct *lpmvs;
bSelfSide = FALSE;
bPerpCheck = bOppPerpCheck = TRUE;
lpmvs = mvsList + nMoveNum - 1;
while (lpmvs->wmv != 0 && lpmvs->ucpcCaptured == 0) {
if (bSelfSide) {
bPerpCheck = bPerpCheck && lpmvs->ucbCheck;
if (lpmvs->dwKey == zobr.dwKey) {
nRecur--;
if (nRecur == 0) {
return 1 + (bPerpCheck ? 2 : 0) + (bOppPerpCheck ? 4 : 0);
}
}
}
else {
bOppPerpCheck = bOppPerpCheck && lpmvs->ucbCheck;
}
bSelfSide = !bSelfSide;
lpmvs--;
}
return 0;
}
// 对局面镜像
void PositionStruct::Mirror(PositionStruct &posMirror) const {
int sq, pc;
posMirror.ClearBoard();
for (sq = 0; sq < 256; sq++) {
pc = ucpcSquares[sq];
if (pc != 0) {
posMirror.AddPiece(MIRROR_SQUARE(sq), pc);
}
}
if (sdPlayer == 1) {
posMirror.ChangeSide();
}
posMirror.SetIrrev();
}
static PositionStruct pos; // 局面实例
// 与图形界面有关的全局变量
#if 0
static struct {
HINSTANCE hInst; // 应用程序句柄实例
HWND hWnd; // 主窗口句柄
HDC hdc, hdcTmp; // 设备句柄,只在"ClickSquare"过程中有效
HBITMAP bmpBoard, bmpSelected, bmpPieces[24]; // 资源图片句柄
int sqSelected, mvLast; // 选中的格子,上一步棋
BOOL bFlipped, bGameOver; // 是否翻转棋盘,是否游戏结束(不让继续玩下去)
} Xqwl;
#endif
// 置换表项结构
struct HashItem {
BYTE ucDepth, ucFlag;
short svl;
WORD wmv, wReserved;
DWORD dwLock0, dwLock1;
};
// 开局库项结构
struct BookItem {
DWORD dwLock;
WORD wmv, wvl;
};
// 与搜索有关的全局变量
static struct {
int mvResult; // 电脑走的棋
int nHistoryTable[65536]; // 历史表
int mvKillers[LIMIT_DEPTH][2]; // 杀手走法表
HashItem HashTable[HASH_SIZE]; // 置换表
int nBookSize; // 开局库大小
BookItem BookTable[BOOK_SIZE]; // 开局库
} Search;
// 装入开局库
#include <fstream> // file I/O
#include <iomanip>
static void LoadBook(void) {
/* CREDITS:
* How to read in an entire binary file
* http://www.cplusplus.com/doc/tutorial/files.html
*/
using namespace std;
ifstream fp_in; // declarations of streams fp_in and fp_out
fp_in.open(s_opening_book, ios::in | ios::binary | ios::ate);
// open and read til END
if (!fp_in.is_open()) {
return;
}
ifstream::pos_type size = fp_in.tellg();
Search.nBookSize = size / sizeof(BookItem);
if (Search.nBookSize > BOOK_SIZE) {
Search.nBookSize = BOOK_SIZE;
}
fp_in.seekg(0, ios::beg);
fp_in.read((char*)Search.BookTable, Search.nBookSize * sizeof(BookItem));
fp_in.close(); // close the streams
// printf("%s: Success opening book Size = [%d (of %u)].\n",
// __FUNCTION__, Search.nBookSize, sizeof(BookItem));
}
static int CompareBook(const void *lpbk1, const void *lpbk2) {
DWORD dw1, dw2;
dw1 = ((BookItem *)lpbk1)->dwLock;
dw2 = ((BookItem *)lpbk2)->dwLock;
return dw1 > dw2 ? 1 : dw1 < dw2 ? -1 : 0;
}
// 搜索开局库
static int SearchBook(void) {
int i, vl, nBookMoves, mv;
int mvs[MAX_GEN_MOVES], vls[MAX_GEN_MOVES];
BOOL bMirror;
BookItem bkToSearch, *lpbk;
PositionStruct posMirror;
// 搜索开局库的过程有以下几个步骤
// 1. 如果没有开局库,则立即返回
if (Search.nBookSize == 0) {
return 0;
}
// 2. 搜索当前局面
bMirror = FALSE;
bkToSearch.dwLock = pos.zobr.dwLock1;
lpbk = (BookItem *)bsearch(&bkToSearch, Search.BookTable, Search.nBookSize, sizeof(BookItem), CompareBook);
// 3. 如果没有找到,那么搜索当前局面的镜像局面
if (lpbk == NULL) {
bMirror = TRUE;
pos.Mirror(posMirror);
bkToSearch.dwLock = posMirror.zobr.dwLock1;
lpbk = (BookItem *)bsearch(&bkToSearch, Search.BookTable, Search.nBookSize, sizeof(BookItem), CompareBook);
}
// 4. 如果镜像局面也没找到,则立即返回
if (lpbk == NULL) {
return 0;
}
// 5. 如果找到,则向前查第一个开局库项
while (lpbk >= Search.BookTable && lpbk->dwLock == bkToSearch.dwLock) {
lpbk--;
}
lpbk++;
// 6. 把走法和分值写入到"mvs"和"vls"数组中
vl = nBookMoves = 0;
while (lpbk < Search.BookTable + Search.nBookSize && lpbk->dwLock == bkToSearch.dwLock) {
mv = (bMirror ? MIRROR_MOVE(lpbk->wmv) : lpbk->wmv);
if (pos.LegalMove(mv)) {
mvs[nBookMoves] = mv;
vls[nBookMoves] = lpbk->wvl;
vl += vls[nBookMoves];
nBookMoves++;
if (nBookMoves == MAX_GEN_MOVES) {
break; // 防止"BOOK.DAT"中含有异常数据
}
}
lpbk++;
}
if (vl == 0) {
return 0; // 防止"BOOK.DAT"中含有异常数据
}
// 7. 根据权重随机选择一个走法
vl = rand() % vl;
for (i = 0; i < nBookMoves; i++) {
vl -= vls[i];
if (vl < 0) {
break;
}
}
return mvs[i];
}
// 提取置换表项
static int ProbeHash(int vlAlpha, int vlBeta, int nDepth, int &mv) {
BOOL bMate; // 杀棋标志:如果是杀棋,那么不需要满足深度条件
HashItem hsh;
hsh = Search.HashTable[pos.zobr.dwKey & (HASH_SIZE - 1)];
if (hsh.dwLock0 != pos.zobr.dwLock0 || hsh.dwLock1 != pos.zobr.dwLock1) {
mv = 0;
return -MATE_VALUE;
}
mv = hsh.wmv;
bMate = FALSE;
if (hsh.svl > WIN_VALUE) {
if (hsh.svl < BAN_VALUE) {
return -MATE_VALUE; // 可能导致搜索的不稳定性,立刻退出,但最佳着法可能拿到
}
hsh.svl -= pos.nDistance;
bMate = TRUE;
}
else if (hsh.svl < -WIN_VALUE) {
if (hsh.svl > -BAN_VALUE) {
return -MATE_VALUE; // 同上
}
hsh.svl += pos.nDistance;
bMate = TRUE;
}
if (hsh.ucDepth >= nDepth || bMate) {
if (hsh.ucFlag == HASH_BETA) {
return (hsh.svl >= vlBeta ? hsh.svl : -MATE_VALUE);
}
else if (hsh.ucFlag == HASH_ALPHA) {
return (hsh.svl <= vlAlpha ? hsh.svl : -MATE_VALUE);
}
return hsh.svl;
}
return -MATE_VALUE;
};
// 保存置换表项
static void RecordHash(int nFlag, int vl, int nDepth, int mv) {
HashItem hsh;
hsh = Search.HashTable[pos.zobr.dwKey & (HASH_SIZE - 1)];
if (hsh.ucDepth > nDepth) {
return;
}
hsh.ucFlag = nFlag;
hsh.ucDepth = nDepth;
if (vl > WIN_VALUE) {
if (mv == 0 && vl <= BAN_VALUE) {
return; // 可能导致搜索的不稳定性,并且没有最佳着法,立刻退出
}
hsh.svl = vl + pos.nDistance;
}
else if (vl < -WIN_VALUE) {
if (mv == 0 && vl >= -BAN_VALUE) {
return; // 同上
}
hsh.svl = vl - pos.nDistance;
}
else {
hsh.svl = vl;
}
hsh.wmv = mv;
hsh.dwLock0 = pos.zobr.dwLock0;
hsh.dwLock1 = pos.zobr.dwLock1;
Search.HashTable[pos.zobr.dwKey & (HASH_SIZE - 1)] = hsh;
};
// MVV/LVA每种子力的价值
static BYTE cucMvvLva[24] = {
0, 0, 0, 0, 0, 0, 0, 0,
5, 1, 1, 3, 4, 3, 2, 0,
5, 1, 1, 3, 4, 3, 2, 0
};
// 求MVV/LVA值
inline int MvvLva(int mv) {
return (cucMvvLva[pos.ucpcSquares[DST(mv)]] << 3) - cucMvvLva[pos.ucpcSquares[SRC(mv)]];
}
// "qsort"按MVV/LVA值排序的比较函数
static int CompareMvvLva(const void *lpmv1, const void *lpmv2) {
return MvvLva(*(int *)lpmv2) - MvvLva(*(int *)lpmv1);
}
// "qsort"按历史表排序的比较函数
static int CompareHistory(const void *lpmv1, const void *lpmv2) {
return Search.nHistoryTable[*(int *)lpmv2] - Search.nHistoryTable[*(int *)lpmv1];
}
// 走法排序阶段
const int PHASE_HASH = 0;
const int PHASE_KILLER_1 = 1;
const int PHASE_KILLER_2 = 2;
const int PHASE_GEN_MOVES = 3;
const int PHASE_REST = 4;
// 走法排序结构
struct SortStruct {
int mvHash, mvKiller1, mvKiller2; // 置换表走法和两个杀手走法
int nPhase, nIndex, nGenMoves; // 当前阶段,当前采用第几个走法,总共有几个走法
int mvs[MAX_GEN_MOVES]; // 所有的走法
void Init(int mvHash_) { // 初始化,设定置换表走法和两个杀手走法
mvHash = mvHash_;
mvKiller1 = Search.mvKillers[pos.nDistance][0];
mvKiller2 = Search.mvKillers[pos.nDistance][1];
nPhase = PHASE_HASH;
}
int Next(void); // 得到下一个走法
};
// 得到下一个走法
int SortStruct::Next(void) {
int mv;
switch (nPhase) {
// "nPhase"表示着法启发的若干阶段,依次为:
// 0. 置换表着法启发,完成后立即进入下一阶段;
case PHASE_HASH:
nPhase = PHASE_KILLER_1;
if (mvHash != 0) {
return mvHash;
}
// 技巧:这里没有"break",表示"switch"的上一个"case"执行完后紧接着做下一个"case",下同
// 1. 杀手着法启发(第一个杀手着法),完成后立即进入下一阶段;
case PHASE_KILLER_1:
nPhase = PHASE_KILLER_2;
if (mvKiller1 != mvHash && mvKiller1 != 0 && pos.LegalMove(mvKiller1)) {
return mvKiller1;
}
// 2. 杀手着法启发(第二个杀手着法),完成后立即进入下一阶段;
case PHASE_KILLER_2:
nPhase = PHASE_GEN_MOVES;
if (mvKiller2 != mvHash && mvKiller2 != 0 && pos.LegalMove(mvKiller2)) {
return mvKiller2;
}
// 3. 生成所有着法,完成后立即进入下一阶段;
case PHASE_GEN_MOVES:
nPhase = PHASE_REST;
nGenMoves = pos.GenerateMoves(mvs);
qsort(mvs, nGenMoves, sizeof(int), CompareHistory);
nIndex = 0;
// 4. 对剩余着法做历史表启发;
case PHASE_REST:
while (nIndex < nGenMoves) {
mv = mvs[nIndex];
nIndex++;
if (mv != mvHash && mv != mvKiller1 && mv != mvKiller2) {
return mv;
}
}
// 5. 没有着法了,返回零。
default:
return 0;
}
}
// 对最佳走法的处理
inline void SetBestMove(int mv, int nDepth) {
int *lpmvKillers;
Search.nHistoryTable[mv] += nDepth * nDepth;
lpmvKillers = Search.mvKillers[pos.nDistance];
if (lpmvKillers[0] != mv) {
lpmvKillers[1] = lpmvKillers[0];
lpmvKillers[0] = mv;
}
}
// 静态(Quiescence)搜索过程
static int SearchQuiesc(int vlAlpha, int vlBeta) {
int i, nGenMoves;
int vl, vlBest;
int mvs[MAX_GEN_MOVES];
// 一个静态搜索分为以下几个阶段
// 1. 检查重复局面
vl = pos.RepStatus();
if (vl != 0) {
return pos.RepValue(vl);
}
// 2. 到达极限深度就返回局面评价
if (pos.nDistance == LIMIT_DEPTH) {
return pos.Evaluate();
}
// 3. 初始化最佳值
vlBest = -MATE_VALUE; // 这样可以知道,是否一个走法都没走过(杀棋)
if (pos.InCheck()) {
// 4. 如果被将军,则生成全部走法
nGenMoves = pos.GenerateMoves(mvs);
qsort(mvs, nGenMoves, sizeof(int), CompareHistory);
}
else {
// 5. 如果不被将军,先做局面评价
vl = pos.Evaluate();
if (vl > vlBest) {
vlBest = vl;
if (vl >= vlBeta) {
return vl;
}
if (vl > vlAlpha) {
vlAlpha = vl;
}
}
// 6. 如果局面评价没有截断,再生成吃子走法
nGenMoves = pos.GenerateMoves(mvs, GEN_CAPTURE);
qsort(mvs, nGenMoves, sizeof(int), CompareMvvLva);
}
// 7. 逐一走这些走法,并进行递归
for (i = 0; i < nGenMoves; i++) {
if (pos.MakeMove(mvs[i])) {
vl = -SearchQuiesc(-vlBeta, -vlAlpha);
pos.UndoMakeMove();
// 8. 进行Alpha-Beta大小判断和截断
if (vl > vlBest) { // 找到最佳值(但不能确定是Alpha、PV还是Beta走法)
vlBest = vl; // "vlBest"就是目前要返回的最佳值,可能超出Alpha-Beta边界
if (vl >= vlBeta) { // 找到一个Beta走法
return vl; // Beta截断
}
if (vl > vlAlpha) { // 找到一个PV走法
vlAlpha = vl; // 缩小Alpha-Beta边界
}
}
}
}
// 9. 所有走法都搜索完了,返回最佳值
return vlBest == -MATE_VALUE ? pos.nDistance - MATE_VALUE : vlBest;
}
// "SearchFull"的参数
const BOOL NO_NULL = TRUE;
// 超出边界(Fail-Soft)的Alpha-Beta搜索过程
static int SearchFull(int vlAlpha, int vlBeta, int nDepth, BOOL bNoNull = FALSE) {
int nHashFlag, vl, vlBest;
int mv, mvBest, mvHash, nNewDepth;
SortStruct Sort;
// 一个Alpha-Beta完全搜索分为以下几个阶段
// 1. 到达水平线,则调用静态搜索(注意:由于空步裁剪,深度可能小于零)
if (nDepth <= 0) {
return SearchQuiesc(vlAlpha, vlBeta);
}
// 1-1. 检查重复局面(注意:不要在根节点检查,否则就没有走法了)
vl = pos.RepStatus();
if (vl != 0) {
return pos.RepValue(vl);
}
// 1-2. 到达极限深度就返回局面评价
if (pos.nDistance == LIMIT_DEPTH) {
return pos.Evaluate();
}
// 1-3. 尝试置换表裁剪,并得到置换表走法
vl = ProbeHash(vlAlpha, vlBeta, nDepth, mvHash);
if (vl > -MATE_VALUE) {
return vl;
}
// 1-4. 尝试空步裁剪(根节点的Beta值是"MATE_VALUE",所以不可能发生空步裁剪)
if (!bNoNull && !pos.InCheck() && pos.NullOkay()) {
pos.NullMove();
vl = -SearchFull(-vlBeta, 1 - vlBeta, nDepth - NULL_DEPTH - 1, NO_NULL);
pos.UndoNullMove();
if (vl >= vlBeta) {
return vl;
}
}
// 2. 初始化最佳值和最佳走法
nHashFlag = HASH_ALPHA;
vlBest = -MATE_VALUE; // 这样可以知道,是否一个走法都没走过(杀棋)
mvBest = 0; // 这样可以知道,是否搜索到了Beta走法或PV走法,以便保存到历史表
// 3. 初始化走法排序结构
Sort.Init(mvHash);
// 4. 逐一走这些走法,并进行递归
while ((mv = Sort.Next()) != 0) {
if (pos.MakeMove(mv)) {
// 将军延伸
nNewDepth = pos.InCheck() ? nDepth : nDepth - 1;
// PVS
if (vlBest == -MATE_VALUE) {
vl = -SearchFull(-vlBeta, -vlAlpha, nNewDepth);
}
else {
vl = -SearchFull(-vlAlpha - 1, -vlAlpha, nNewDepth);
if (vl > vlAlpha && vl < vlBeta) {
vl = -SearchFull(-vlBeta, -vlAlpha, nNewDepth);
}
}
pos.UndoMakeMove();
// 5. 进行Alpha-Beta大小判断和截断
if (vl > vlBest) { // 找到最佳值(但不能确定是Alpha、PV还是Beta走法)
vlBest = vl; // "vlBest"就是目前要返回的最佳值,可能超出Alpha-Beta边界
if (vl >= vlBeta) { // 找到一个Beta走法
nHashFlag = HASH_BETA;
mvBest = mv; // Beta走法要保存到历史表
break; // Beta截断
}
if (vl > vlAlpha) { // 找到一个PV走法
nHashFlag = HASH_PV;
mvBest = mv; // PV走法要保存到历史表
vlAlpha = vl; // 缩小Alpha-Beta边界
}
}
}
}
// 5. 所有走法都搜索完了,把最佳走法(不能是Alpha走法)保存到历史表,返回最佳值
if (vlBest == -MATE_VALUE) {
// 如果是杀棋,就根据杀棋步数给出评价
return pos.nDistance - MATE_VALUE;
}
// 记录到置换表
RecordHash(nHashFlag, vlBest, nDepth, mvBest);
if (mvBest != 0) {
// 如果不是Alpha走法,就将最佳走法保存到历史表
SetBestMove(mvBest, nDepth);
}
return vlBest;
}
// 根节点的Alpha-Beta搜索过程
static int SearchRoot(int nDepth) {
int vl, vlBest, mv, nNewDepth;
SortStruct Sort;
vlBest = -MATE_VALUE;
Sort.Init(Search.mvResult);
while ((mv = Sort.Next()) != 0) {
if (pos.MakeMove(mv)) {
nNewDepth = pos.InCheck() ? nDepth : nDepth - 1;
if (vlBest == -MATE_VALUE) {
vl = -SearchFull(-MATE_VALUE, MATE_VALUE, nNewDepth, NO_NULL);
}
else {
vl = -SearchFull(-vlBest - 1, -vlBest, nNewDepth);
if (vl > vlBest) {
vl = -SearchFull(-MATE_VALUE, -vlBest, nNewDepth, NO_NULL);
}
}
pos.UndoMakeMove();
if (vl > vlBest) {
vlBest = vl;
Search.mvResult = mv;
if (vlBest > -WIN_VALUE && vlBest < WIN_VALUE) {
vlBest += (rand() & RANDOM_MASK) - (rand() & RANDOM_MASK);
}
}
}
}
RecordHash(HASH_PV, vlBest, nDepth, Search.mvResult);
SetBestMove(Search.mvResult, nDepth);
return vlBest;
}
// 迭代加深搜索过程
static void SearchMain(void) {
int i, t, vl, nGenMoves;
int mvs[MAX_GEN_MOVES];
// 初始化
memset(Search.nHistoryTable, 0, 65536 * sizeof(int)); // 清空历史表
memset(Search.mvKillers, 0, LIMIT_DEPTH * 2 * sizeof(int)); // 清空杀手走法表
memset(Search.HashTable, 0, HASH_SIZE * sizeof(HashItem)); // 清空置换表
t = clock(); // 初始化定时器
pos.nDistance = 0; // 初始步数
// 搜索开局库
Search.mvResult = SearchBook();
if (Search.mvResult != 0) {
pos.MakeMove(Search.mvResult);
if (pos.RepStatus(3) == 0) {
pos.UndoMakeMove();
return;
}
pos.UndoMakeMove();
}
// 检查是否只有唯一走法
vl = 0;
nGenMoves = pos.GenerateMoves(mvs);
for (i = 0; i < nGenMoves; i++) {
if (pos.MakeMove(mvs[i])) {
pos.UndoMakeMove();
Search.mvResult = mvs[i];
vl++;
}
}
if (vl == 1) {
return;
}
// 迭代加深过程
for (i = 1; i <= s_search_depth; i++) {
vl = SearchRoot(i);
// 搜索到杀棋,就终止搜索
if (vl > WIN_VALUE || vl < -WIN_VALUE) {
break;
}
// 超过一秒,就终止搜索
//if (clock() - t > CLOCKS_PER_SEC) {
//float elapse = ((float) clock() - t) / CLOCKS_PER_SEC;
//printf("%s: Search depth DONE = [%d]. elapse=[%.02f]\n", __FUNCTION__, i, elapse);
if (clock() - t >(CLOCKS_PER_SEC * s_search_time)) {
break;
}
//printf("%s: Search depth START = [%d].\n", __FUNCTION__, i+1);
}
//printf("%s: Search depth = *** [%d].\n", __FUNCTION__, i);
}
// 初始化棋局
static void Startup(unsigned char board[10][9]) {
pos.Startup(board);
//Xqwl.sqSelected = Xqwl.mvLast = 0;
//Xqwl.bGameOver = FALSE;
}
/////////////////////////////////////////////////////////////
////////////////// HPHAN Code addition //////////////////////
void
XQWLight::init_engine(int searchDepth)
{
if (searchDepth < LIMIT_DEPTH)
{
s_search_depth = searchDepth;
}
}
void
XQWLight::init_game(unsigned char board[10][9] /* = NULL */,
const char side /* = 'w' */)
{
srand((DWORD)time(NULL));
InitZobrist();
//Xqwl.hInst = hInstance;
LoadBook();
//Xqwl.bFlipped = FALSE;
Startup(board);
if (side == 'b')
{
pos.ChangeSide();
}
}
std::string
XQWLight::generate_move()
{
SearchMain();
std::string stdMove = _xqwlight2hox(Search.mvResult);
pos.MakeMove(Search.mvResult);
return stdMove;
}
void
XQWLight::on_human_move(const std::string& sMove)
{
const std::string stdMove = sMove;
unsigned int nMove = _hox2xqwlight(stdMove);
Search.mvResult = nMove;
pos.MakeMove(Search.mvResult);
}
void
XQWLight::set_search_time(int nSeconds)
{
s_search_time = nSeconds;
}
unsigned int
XQWLight::_hox2xqwlight(const std::string& sMove)
{
unsigned int sx = sMove[0] - '0';
unsigned int sy = sMove[1] - '0';
unsigned int dx = sMove[2] - '0';
unsigned int dy = sMove[3] - '0';
unsigned int src = (3 + sx) + (3 + sy) * 16;
unsigned int dst = (3 + dx) + (3 + dy) * 16;
return src | (dst << 8);
}
std::string
XQWLight::_xqwlight2hox(unsigned int move)
{
unsigned int src = move & 255;
unsigned int dst = move >> 8;
unsigned int sx = (src % 16) - 3;
unsigned int sy = (src / 16) - 3;
unsigned int dx = (dst % 16) - 3;
unsigned int dy = (dst / 16) - 3;
std::ostringstream ostr;
ostr << sx << sy << dx << dy;
return ostr.str();
}
/************************* END OF FILE ***************************************/
\ No newline at end of file
/***************************************************************************
* Copyright 2007-2009 Huy Phan *
* *
* This file is part of HOXChess. *
* *
* HOXChess is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* HOXChess is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with HOXChess. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
/////////////////////////////////////////////////////////////////////////////
// Name: XQWLight.h
// Created: 10/11/2008
//
// Description: This is 'XQWLight' Engine to interface with HOXChess.
// XQWLight is an open-source (?) Xiangqi AI Engine
// written by Huang Chen at www.elephantbase.net
//
// (Original Chinese URL)
// http://www.elephantbase.net/computer/stepbystep1.htm
//
// (Translated English URL using Goold Translate)
// http://74.125.93.104/translate_c?hl=en&langpair=
// zh-CN|en&u=http://www.elephantbase.net/computer/stepbystep1.htm&
// usg=ALkJrhj7W0v3J1P-xmbufsWzYq7uKciL1w
/////////////////////////////////////////////////////////////////////////////
#ifndef __INCLUDED_XQWLIGHT_HOX_ENGINE_H__
#define __INCLUDED_XQWLIGHT_HOX_ENGINE_H__
#include <string>
namespace XQWLight
{
/* PUBLIC API */
void init_engine( int searchDepth );
void init_game( unsigned char board[10][9] = NULL,
const char side = 'w' );
std::string generate_move();
void on_human_move( const std::string& sMove );
void set_search_time( int nSeconds );
/* Only approximately... */
/* PRIVATE API (declared here for documentation purpose) */
unsigned int _hox2xqwlight( const std::string& sMove );
std::string _xqwlight2hox( unsigned int move );
} // namespace XQWLight
#endif /* __INCLUDED_XQWLIGHT_HOX_ENGINE_H__ */
#include "Game.h"
#include <Windows.h>
#include "mmsystem.h"
int main()
{
Game chessGame;
chessGame.Interface();
return 0;
}
\ No newline at end of file
#include "Player.h"
#include "Game.h"
Player::Player(bool icolor) :color(icolor){}
SHORT Player::move(COORD cursorP, COORD movePos, Map& map)
{
if (map.pChess[cursorP.X][cursorP.Y] == NULL)
system("pause"); // BUG 想走的棋子位置是空的
if (map.pChess[cursorP.X][cursorP.Y]->getColor() != color)
system("pause"); // BUG 動對方的棋子
chessStorage tempStorage; //悔棋還原功能的struct暫存
bool Valid = false;
for (unsigned int i = 0; i < map.pChess[cursorP.X][cursorP.Y]->access.size(); i++) //
{ //
if (map.pChess[cursorP.X][cursorP.Y]->access.at(i) == movePos) // >在access 中確定此路徑可行
Valid = true; //
} //
if (map.pChess[movePos.X][movePos.Y] != NULL)
{
if (map.pChess[movePos.X][movePos.Y]->getColor() == map.pChess[cursorP.X][cursorP.Y]->getColor())//change the chess you chose !
return -1;
else
if (Valid) //eat
{
tempStorage.dead = map.pChess[movePos.X][movePos.Y]; //
tempStorage.moved = map.pChess[cursorP.X][cursorP.Y]; //
tempStorage.prePos = ComXY(cursorP.X, cursorP.Y); // >儲存
tempStorage.Pos = ComXY(movePos.X, movePos.Y); //
map.chessStoragePointer()->push_back(tempStorage); //
map.pChess[movePos.X][movePos.Y]->setAlive(false);
map.pChess[movePos.X][movePos.Y] = map.pChess[cursorP.X][cursorP.Y]; //覆蓋pointer
map.pChess[cursorP.X][cursorP.Y] = NULL; //刪除棋子原本位置的pointer
map.pChess[movePos.X][movePos.Y]->setPos(movePos); //更改Chess private裡的位置
return 1;
}
}
else if (Valid) //move
{
tempStorage.dead = NULL; //
tempStorage.moved = map.pChess[cursorP.X][cursorP.Y]; //
tempStorage.prePos = ComXY(cursorP.X, cursorP.Y); // >儲存
tempStorage.Pos = ComXY(movePos.X, movePos.Y); //
map.chessStoragePointer()->push_back(tempStorage); //
map.pChess[movePos.X][movePos.Y] = map.pChess[cursorP.X][cursorP.Y]; //覆蓋pointer
map.pChess[cursorP.X][cursorP.Y] = NULL; //刪除棋子原本位置的pointer
map.pChess[movePos.X][movePos.Y] ->setPos(movePos); //更改Chess private裡的位置
return 1;
}
return 0;
}
COORD Player::chooseMovePos(COORD cursorPos, Map& map, bool& isMoveSuc, bool& reChoose, GUI& gui)
{
CHAR InputKB = _getch();
COORD originalCursorPos = cursorPos;
bool isChooseSuccess = false;
short moveVar;
while (true)
{
switch (InputKB)
{
case KB_UP:
if (cursorPos.Y > 0)
--cursorPos.Y;
break;
case KB_DOWN:
if (cursorPos.Y < 9)
++cursorPos.Y;
break;
case KB_LEFT:
if (cursorPos.X > 0)
--cursorPos.X;
break;
case KB_RIGHT:
if (cursorPos.X < 8)
++cursorPos.X;
break;
case KB_ENTER:
moveVar = move(originalCursorPos, ComXY(cursorPos.X, cursorPos.Y), map);
if (moveVar == 1) //move sucessfully
{
isChooseSuccess = true;
isMoveSuc = true;
return ComXY(cursorPos.X, cursorPos.Y);
}
else if (moveVar == -1) //change the chess you chose
return ComXY(cursorPos.X, cursorPos.Y);
else if (moveVar == 0)
{
reChoose = true;
return ComXY(cursorPos.X, cursorPos.Y);
}
break;
default:
break;
}
gui.gotoxy(CHESS_BOARD_X + cursorPos.X * 4, CHESS_BOARD_Y + cursorPos.Y * 2 + 1);
InputKB = _getch();
}
}
\ No newline at end of file
/*
Base class for environments of RL.
Following features:
Discrete and fix-size action space.
Used as:
Experiment environment;
Environment in Agents' mind
*/
//EnCode for move(action)::
/*
from_id 90
to_id 90
stone 19
Attention: all this coding part should be >= 0
*/
//action = stone * 90^2 + from_id * 90^1 + to_id * 90^0
#include <cstdlib>
#include <string>
class ChineseChess: public BaseEnv {
public:
ChineseChess();
~ChineseChess();
virtual int Move(int action) = 0;
virtual bool IsFinish() = 0;
virtual void GetResult(float& res) = 0;
virtual void GetFeature(BaseFeature& feature) = 0;
virtual void GetActionNum() = 0;
virtual void GetInputDim(vector<int>& input_dim) = 0;
virtual void GetLegalAction(vector<int>& action) = 0;
virtual bool IsLegal(int action) = 0;
virtual std::string action2str(int action) = 0;
//virtual void TransformFeatures(BaseFeature& feature, int transform_mode) = 0;
//virtual void TransformPolicy(int transform_mode) = 0; // this is reverse mode of TransformFeature
protected:
virtual void Init() = 0;
};
\ No newline at end of file
#include "chinesechess_comm.h"
#include <cstring>
#include <mutex>
// #include <glog/logging.h>
#define x first
#define y second
using namespace std;
using namespace ChineseChessComm;
ChineseChessHashValuePair g_hash_weight[BORDER_SIZE_HEIGHT][BORDER_SIZE_LENGTH];
ChineseChessCoordId g_log2_table[67];
uint64_t g_zobrist_board_hash_weight[4][CHINESECHESSBOARD_SIZE];
uint64_t g_zobrist_player_hash_weight[4];
namespace ChineseChessFunction {
bool InBoard(const ChineseChessCoordId id) {
return 0 <= id && id < CHINESECHESSBOARD_SIZE;
}
bool InBoard(const ChineseChessCoordId x, const ChineseChessCoordId y) {
return 0 <= x && x < BORDER_SIZE_HEIGHT
&& 0 <= y && y < BORDER_SIZE_LENGTH;
}
bool IsUnset(const ChineseChessCoordId id) {
return COORD_UNSET == id;
}
bool IsUnset(const ChineseChessCoordId x, const ChineseChessCoordId y) {
return COORD_UNSET == CoordToId(x, y);
}
bool IsResign(const ChineseChessCoordId id) {
return COORD_RESIGN == id;
}
bool IsResign(const ChineseChessCoordId x, const ChineseChessCoordId y) {
return COORD_RESIGN == CoordToId(x, y);
}
void IdToCoord(const ChineseChessCoordId id, ChineseChessCoordId &x, ChineseChessCoordId &y) {
if (COORD_RESIGN == id) {
x = y = COORD_RESIGN;
} else if (!InBoard(id)) {
x = y = COORD_UNSET;
} else {
x = id / BORDER_SIZE_LENGTH;
y = id % BORDER_SIZE_LENGTH;
}
}
ChineseChessCoordId CoordToId(const ChineseChessCoordId x, const ChineseChessCoordId y) {
if (COORD_RESIGN == x && COORD_RESIGN == y) {
return COORD_RESIGN;
}
if (!InBoard(x, y)) {
return COORD_UNSET;
}
return x * BORDER_SIZE_LENGTH + y;
}
void StrToCoord(const string &str, ChineseChessCoordId &x, ChineseChessCoordId &y) {
// CHECK_EQ(str.length(), 2) << "string[" << str << "] length not equal to 2";
x = str[0] - 'a';
y = str[1] - 'a';
if (!InBoard(x, y)) {
x = y = COORD_UNSET;
}
}
string CoordToStr(const ChineseChessCoordId x, const ChineseChessCoordId y) {
char buffer[3];
if (!InBoard(x, y)) {
buffer[0] = buffer[1] = 'z';
} else {
buffer[0] = x + 'a';
buffer[1] = y + 'a';
}
return string(buffer, 2);
}
std::string IdToStr(const ChineseChessCoordId id) {
ChineseChessCoordId x, y;
IdToCoord(id, x, y);
return CoordToStr(x, y);
}
ChineseChessCoordId StrToId(const std::string &str) {
ChineseChessCoordId x, y;
StrToCoord(str, x, y);
return CoordToId(x, y);
}
void ActionToId(const int &action, const ChineseChessStoneColor &stone, const ChineseChessCoordId &from_id, const ChineseChessCoordId &to_id){
int code[3];
int i;
if(action == COORD_RESIGN){
from_id = to_id = COORD_RESIGN;
stone = EMPTY;
}else if(action == COORD_UNSET){
from_id = to_id = COORD_UNSET;
stone = EMPTY;
}else{
for(i = 0; i < 3; i++){
code[i] = 0;
}
for(i = 0; i < 5; i++){
code[i] = action % 90;
action = action / 90;
}
stone = code[2];
from_id = code[1];
to_id = code[0];
}
}
void IdToAction(const ChineseChessStoneColor &stone, const ChineseChessCoordId &from_id, const ChineseChessCoordId &to_id, const int &action){
int code[3];
int i, j;
for(i = 0; i < 3; i++){
code[i] = 1;
}
if(from_id == COORD_RESIGN || to_id == COORD_RESIGN){
action = COORD_RESIGN;
}else if(!InBoard(from_id) || !InBoard(to_id)){
action = COORD_UNSET;
}else if(stone >= 0 && from_id >= 0 && to_id >= 0){
for(i = 0; i < 3; i++){
for(j = i; j > 0; j--){
code[i] *= 90;
}
}
action = stone * code[2] + from_id * code[1] + to_id * code[0];
}
}
once_flag CreateGlobalVariables_once;
void CreateGlobalVariables() {
call_once(
CreateGlobalVariables_once,
[]() {
CreateHashWeights();
CreateQuickLog2Table();
CreateZobristHash();
}
);
}
void CreateHashWeights() {
g_hash_weight[0][0] = ChineseChessHashValuePair(1, 1);
for (ChineseChessCoordId i = 1; i < CHINESECHESSBOARD_SIZE; ++i) {
g_hash_weight[i / BORDER_SIZE_LENGTH][i % BORDER_SIZE_LENGTH] =
ChineseChessHashValuePair(g_hash_weight[(i - 1) / BORDER_SIZE_LENGTH][(i - 1) % BORDER_SIZE_LENGTH].x * g_hash_unit.x,
g_hash_weight[(i - 1) / BORDER_SIZE_LENGTH][(i - 1) % BORDER_SIZE_LENGTH].y * g_hash_unit.y);
}
}
void CreateQuickLog2Table() {
memset(g_log2_table, -1, sizeof(g_log2_table));
int tmp = 1;
for (ChineseChessCoordId i = 0; i < 64; ++i) {
g_log2_table[tmp] = i;
tmp *= 2;
tmp %= 67;
}
}
#if defined(_WIN32) || defined(_WIN64)
static int rand_r(unsigned int *seed)
{
unsigned int next = *seed;
int result;
next *= 1103515245;
next += 12345;
result = (unsigned int)(next / 65536) % 2048;
next *= 1103515245;
next += 12345;
result <<= 10;
result ^= (unsigned int)(next / 65536) % 1024;
next *= 1103515245;
next += 12345;
result <<= 10;
result ^= (unsigned int)(next / 65536) % 1024;
*seed = next;
return result;
}
#endif
void CreateZobristHash() {
uint32_t seed = 0xdeadbeaf;
for (int i = 0; i < 4; ++i) {
g_zobrist_player_hash_weight[i] = (uint64_t) rand_r(&seed) << 32 | rand_r(&seed);
for (int j = 0; j < CHINESECHESSBOARD_SIZE; ++j) {
g_zobrist_board_hash_weight[i][j] = (uint64_t) rand_r(&seed) << 32 | rand_r(&seed);
}
}
}
} // namespace ChineseChessFunction
#undef y
#undef x
#pragma once
#include <inttypes.h>
#include <string>
#include <vector>
//ChineseChess Board Id:
// 0 1 2 3 4 5 6 7 8
// 9 10 11 12 13 14 15 15 17
// 18 19 20 21 22 23 24 25 26
// 27 28 29 30 31 32 33 34 35
// 36 37 38 39 40 41 42 43 44
// .... ....
// 45 46 47 48 49 50 51 52 53
// 54 55 56 57 58 59 60 61 62
// 63 64 65 66 67 68 69 70 71
// 72 73 74 75 76 77 78 79 80
// 81 82 83 84 85 86 87 88 89
//End
// Return code of functions should be "int"
typedef uint8_t ChineseChessStoneColor; // Stone color
typedef int16_t ChineseChessCoordId; // Stone IDs or coordinates
typedef int16_t ChineseChessSize; // Counts of visit times, used blocks, .. or other count
namespace ChineseChessComm {
const ChineseChessCoordId BORDER_SIZE_LENGTH = 9; // sxk_modify
const ChineseChessCoordId BORDER_SIZE_HEIGHT = 10;
const ChineseChessCoordId CHINESECHESSBOARD_SIZE = BORDER_SIZE_LENGTH * BORDER_SIZE_HEIGHT;
const ChineseChessCoordId COORD_UNSET = -2;
//const ChineseChessCoordId COORD_PASS = -1;
const ChineseChessCoordId COORD_RESIGN = -3;
const ChineseChessStoneColor EMPTY = 0;
const ChineseChessStoneColor RED = 1;
const ChineseChessStoneColor BLACK = 2;
const ChineseChessStoneColor WALL = 3;
const ChineseChessStoneColor RED_PAWN = 4;
const ChineseChessStoneColor RED_CANNON = 5;
const ChineseChessStoneColor RED_HORSE = 6;
const ChineseChessStoneColor RED_CHARIOT = 7;
const ChineseChessStoneColor RED_ELEPHANT = 8;
const ChineseChessStoneColor RED_ADVISER = 9;
const ChineseChessStoneColor RED_KING = 10;
const ChineseChessStoneColor BLACK_PAWN = 11;
const ChineseChessStoneColor BLACK_CANNON = 12;
const ChineseChessStoneColor BLACK_HORSE = 13;
const ChineseChessStoneColor BLACK_CHARIOT = 14;
const ChineseChessStoneColor BLACK_ELEPHANT = 15;
const ChineseChessStoneColor BLACK_ADVISER = 16;
const ChineseChessStoneColor BLACK_KING = 17;
const ChineseChessStoneColor COLOR_UNKNOWN = -1;
const char *const COLOR_STRING[] = { "Empty", "Red", "Black", "Wall", "Red Pawn", "Red Cannon", "Red Horse", "Red Chariot", "Red Elephant", "Red Adviser", "Red King", "Black Pawn", "Black Cannon", "Black Horse", "Black Chariot", "Black Elephant", "Black Adviser", "Black King" };
/*
const ChineseChessCoordId N = -9;
const ChineseChessCoordId S = 9;
const ChineseChessCoordId E = 1;
const ChineseChessCoordId W = -1;
const ChineseChessCoordId NW = -10;
const ChineseChessCoordId SW = 8;
const ChineseChessCoordId NE = -8;
const ChineseChessCoordId SE = 10;
const ChineseChessCoordId NNW = -19;
const ChineseChessCoordId NNE = -17;
const ChineseChessCoordId NWW = -11;
const ChineseChessCoordId NEE = -7;
const ChineseChessCoordId SSW = 17;
const ChineseChessCoordId SSE = 19;
const ChineseChessCoordId SWW = 7;
const ChineseChessCoordId SEE = 11;
*/
} // namespace ChineseChessComm
namespace ChineseChessFeature {
const int SIZE_HISTORYEACHSIDE = 16;
const int SIZE_PLAYERCOLOR = 1;
const int STARTPOS_HISTORYEACHSIDE = 0;
const int STARTPOS_PLAYERCOLOR = STARTPOS_HISTORYEACHSIDE + SIZE_HISTORYEACHSIDE;
const int FEATURE_COUNT = STARTPOS_PLAYERCOLOR + SIZE_PLAYERCOLOR;
} // namespace ChineseChessFeature
namespace ChineseChessFunction {
extern bool InBoard(const ChineseChessCoordId id);
extern bool InBoard(const ChineseChessCoordId x, const ChineseChessCoordId y);
extern bool IsUnset(const ChineseChessCoordId id);
extern bool IsUnset(const ChineseChessCoordId x, const ChineseChessCoordId y);
extern bool IsResign(const ChineseChessCoordId id);
extern bool IsResign(const ChineseChessCoordId x, const ChineseChessCoordId y);
extern void IdToCoord(const ChineseChessCoordId id, ChineseChessCoordId &x, ChineseChessCoordId &y);
extern ChineseChessCoordId CoordToId(const ChineseChessCoordId x, const ChineseChessCoordId y);
extern void StrToCoord(const std::string &str, ChineseChessCoordId &x, ChineseChessCoordId &y);
extern std::string CoordToStr(const ChineseChessCoordId x, const ChineseChessCoordId y);
extern std::string IdToStr(const ChineseChessCoordId id);
extern ChineseChessCoordId StrToId(const std::string &str);
extern void ActionToId(const int &action, const ChineseChessStone &stone, const ChineseChessCoordId &from_id, const ChineseChessCoordId &to_id);
extern void IdToAction(const ChineseChessStone &stone, const ChineseChessCoordId &from_id, const ChineseChessCoordId &to_id, const int &action);
extern void CreateGlobalVariables();
extern void CreateHashWeights();
extern void CreateQuickLog2Table();
extern void CreateZobristHash();
} // namespace ChineseChessFunction
//typedef std::pair<ChineseChessCoordId, ChineseChessCoordId> ChineseChessPosition;
typedef std::pair<uint64_t, uint64_t> ChineseChessHashValuePair;
extern ChineseChessHashValuePair g_hash_weight[ChineseChessComm::BORDER_SIZE_HEIGHT][ChineseChessComm::BORDER_SIZE_LENGTH];
const ChineseChessHashValuePair g_hash_unit(3, 7);
extern uint64_t g_zobrist_board_hash_weight[4][ChineseChessComm::CHINESECHESSBOARD_SIZE];
extern uint64_t g_zobrist_player_hash_weight[4];
extern ChineseChessCoordId g_log2_table[67];
#define FOR_EACHCOORD(id) for (ChineseChessCoordId id = 0; id < ChineseChessComm::CHINESECHESSBOARD_SIZE; ++id)
#include "chinesechess_env.h"
#include <algorithm>
#include <cmath>
using namespace std;
using namespace ChineseChessComm;
using namespace ChineseChessFunction;
using namespace ChineseChessFeature;
ChineseChessEnv::ChineseChessEnv() {
CreateGlobalVariables();
FOR_EACHCOORD(i) {
stones_[i].self_id = i;
}
action_list_.clear();
feature_history_list_.clear();
memset(board_state_, 0, sizeof(board_state_));
memset(move_count_, 0, sizeof(move_count_));
current_player_ = RED;
last_action_ = COORD_UNSET;
is_resign_ = false;
state_ = 0;
board_hash_states_.clear();
zobrist_hash_value_ = 0;
}
//ChineseChessEnv::ChineseChessEnv(const ChineseChessEnv &ge) : ChineseChessEnv() {
// CopyFrom(ge);
// }
ChineseChessEnv::~ChineseChessEnv() {
}
// void ChineseChessEnv::CopyFrom(const ChineseChessEnv &src) {
// *this = src;
// }
void ChineseChessEnv::GetSensibleMove() {
// Add new feature plane
string plane;
// red
{
plane = "";
FOR_EACHCOORD(id) {
if (board_state_[id] == RED) {
plane += "1";
} else {
plane += "0";
}
}
feature_history_list_.push_back(plane);
}
// black
{
plane = "";
FOR_EACHCOORD(id) {
if (board_state_[id] == BLACK) {
plane += "1";
} else {
plane += "0";
}
}
feature_history_list_.push_back(plane);
}
Find_Legal_Moves();
}
int ChineseChessState::Move(int action) {
ChineseChessCoordId from_id, to_id;
ChineseChessStoneColor piece;
Find_Legal_Moves();
if (!IsLegal(action)) {
return -1;
}
if(IsResign(action)){
is_resign_ = true;
return 0;
}
last_action_ = action;
{
zobrist_hash_value_ ^= g_zobrist_player_hash_weight[Self()];
zobrist_hash_value_ ^= g_zobrist_player_hash_weight[Opponent()];
zobrist_hash_value_ ^= g_zobrist_board_hash_weight[Self()][action];
}
ChineseChessFunction::ActionToId(action, piece, from_id, to_id);
++move_count_[action];
board_state_[to_id] = board_state_[from_id];
board_state_[from_id] = ChineseChessComm::EMPTY;
HandOff();
GetSensibleMove();
return 0;
}
void ChineseChessEnv::Find_Legal_Moves() const{
action_list_.clear();
int action;
ChineseChessCoordId i, j, k, l;
ChineseChessCoordId i_x, i_y, j_x, j_y , k_x, k_y, l_x, l_y;
bool is_king_facetoface = false, is_mystone = false, is_chariotpass = false, is_horsepass = false;
int face_tag = 0;
for(i = 0; i < ChineseChessComm::CHINESECHESSBOARD_SIZE; ++i){ //i:from_id
ChineseChessStoneColor piece = board_state_[i];
for(j = 0; j < ChineseChessComm::CHINESECHESSBOARD_SIZE; j++){ //j:to_id
if(((board_state_[j] == ChineseChessComm::RED_PAWN || board_state_[j] == ChineseChessComm::RED_CANNON || board_state_[j] == ChineseChessComm::RED_HORSE || board_state_[j] == ChineseChessComm::RED_CHARIOT ||
board_state_[j] == ChineseChessComm::RED_ELEPHANT || board_state_[j] == ChineseChessComm::RED_ADVISER || board_state_[j] == ChineseChessComm::RED_KING) && current_player_ == ChineseChessComm::RED) ||
((board_state_[j] == ChineseChessComm::BLACK_PAWN || board_state_[j] == ChineseChessComm::BLACK_CANNON || board_state_[j] == ChineseChessComm::BLACK_HORSE || board_state_[j] == ChineseChessComm::BLACK_CHARIOT ||
board_state_[j] == ChineseChessComm::BLACK_ELEPHANT || board_state_[j] == ChineseChessComm::BLACK_ADVISER || board_state_[j] == ChineseChessComm::BLACK_KING) && current_player_ == ChineseChessComm::BLACK)){
is_mystone = true;
}
bool color = (current_player_ == ChineseChessComm::RED) ? true : false;
switch (piece){
case ChineseChessComm::RED_KING : case ChineseChessComm::BLACK_KING : // King
{
for(k = 0; k < ChineseChessComm::CHINESECHESSBOARD_SIZE; k++){ //k:another_king_id
if(piece == ChineseChessComm::RED_KING){
if(board_state_[k] == ChineseChessComm::BLACK_KING){
break;
}
}else{
if(board_state_[k] == ChineseChessComm::RED_KING){
break;
}
}
}
i_x = i_y = j_x = j_y = k_x = k_y = l_x = l_y = ChineseChessComm::COORD_UNSET;
ChineseChessFunction::IdToCoord(i, i_x, i_y);
ChineseChessFunction::IdToCoord(j, j_x, j_y);
if(k != ChineseChessComm::CHINESECHESSBOARD_SIZE){
ChineseChessFunction::IdToCoord(k, k_x, k_y);
}
if ((color ? 7 : 0) <= j_x && j_x <= (color ? 9 : 2) && j_y >= 3 && j_y <= 5 &&
((abs((i_x - j_x)) == 1 && abs((i_y - j_y)) == 0) || (abs((i_y - j_y)) == 1 && abs((i_x - j_x)) == 0))) {
if (i_x == j_x && (j_y == k_y)){
face_tag = 0;
l_y = j_y;
for (int l_x = (i_x > k_x ? k_x : i_x) + 1; l_x < (i_x > k_x ? i_x : k_x); l_x++){
ChineseChessFunction::CoordToId(l_x, l_y, l);
if (board_state_[l] != ChineseChessComm::EMPTY){
face_tag = 1;
}
}
if(face_tag == 0){
is_king_facetoface = true;
}
}
if(!is_mystone && !is_king_facetoface){
ChineseChessFunction::IdToAction(piece, i, j, action);
action_list_.push_back(action);
}
}
}
break;
case ChineseChessComm::RED_CHARIOT : case ChineseChessComm::BLACK_CHARIOT : // Chariot
{
i_x = i_y = j_x = j_y = k_x = k_y = l_x = l_y = ChineseChessComm::COORD_UNSET;
ChineseChessFunction::IdToCoord(i, i_x, i_y);
ChineseChessFunction::IdToCoord(j, j_x, j_y);
if ((i_x == j_x) && (i_y - j_y) != 0){ //横向前進
for (l_y = (i_y > j_y ? j_y : i_y) + 1; l_y < (i_y > j_y ? i_y : j_y); l_y++){
l_x = j_x;
ChineseChessFunction::CoordToId(l_x, l_y, l);
if (board_state_[l] != ChineseChessComm::EMPTY){
is_chariotpass = true;
}
}
}else if ((i_x - j_x) != 0 && (i_y == j_y)){ //纵向前進
for (l_x = (i_x > j_x ? j_x : i_x) + 1; l_x < (i_x > j_x ? i_x : j_x); l_x++){
l_y = j_y;
ChineseChessFunction::CoordToId(l_x, l_y, l);
if (board_state_[l] != ChineseChessComm::EMPTY){
is_chariotpass = true;
}
}
}
if(!is_chariotpass && !is_mystone){
ChineseChessFunction::IdToAction(piece, i, j, action);
action_list_.push_back(action);
}
}
break;
case ChineseChessComm::RED_HORSE : case ChineseChessComm::BLACK_HORSE : // Horse
{
i_x = i_y = j_x = j_y = k_x = k_y = l_x = l_y = ChineseChessComm::COORD_UNSET;
ChineseChessFunction::IdToCoord(i, i_x, i_y);
ChineseChessFunction::IdToCoord(j, j_x, j_y);
if (abs(i_y - j_y) == 2) {//拐馬腳Y,横向
l_x = i_x;
l_y = i_y - (i_y - j_y)/2;
ChineseChessFunction::CoordToId(l_x, l_y, l);
if (board_state_[l] != ChineseChessComm::EMPTY){
is_horsepass = true;
}
}
if (abs(i_x - j_x) == 2) {//拐馬腳X,纵向
l_y = i_y;
l_x = i_x - (i_x - j_x)/2;
ChineseChessFunction::CoordToId(l_x, l_y, l);
if (board_state_[l] != ChineseChessComm::EMPTY){
is_horsepass = true;
}
}
if (abs(i_y - j_y) == 2 && abs(i_x - j_x) == 1 ||
(abs(i_y - j_y) == 1 && abs(i_x - j_x) == 2)) {
if(!is_mystone && !is_horsepass){
ChineseChessFunction::IdToAction(piece, i, j, action);
action_list_.push_back(action);
}
}
}
break;
case ChineseChessComm::RED_CANNON : case ChineseChessComm::BLACK_CANNON : // Cannon
{
i_x = i_y = j_x = j_y = k_x = k_y = l_x = l_y = ChineseChessComm::COORD_UNSET;
ChineseChessFunction::IdToCoord(i, i_x, i_y);
ChineseChessFunction::IdToCoord(j, j_x, j_y);
int count = 0;
if (abs(i_x - j_x) != 0 && abs(i_y - j_y) == 0){ //纵向
if (i_x - j_x > 0){
for (l_x = i_x - 1; l_x > j_x; l_x--){
l_y = i_y;
ChineseChessFunction::CoordToId(l_x, l_y, l);
if (board_state_[l] != ChineseChessComm::EMPTY){
count++;
}
}
if ((count == 1 && (board_state_[j] != ChineseChessComm::EMPTY && !is_mystone)) || (count == 0 && board_state_[j] == ChineseChessComm::EMPTY)){
ChineseChessFunction::IdToAction(piece, i, j, action);
action_list_.push_back(action);
}
}else{
for (l_x = i_x + 1; l_x < j_x; l_x++){
l_y = i_y;
ChineseChessFunction::CoordToId(l_x, l_y, l);
if (board_state_[l] != ChineseChessComm::EMPTY){
count++;
}
}
if ((count == 1 && (board_state_[j] != ChineseChessComm::EMPTY && !is_mystone)) || (count == 0 && board_state_[j] == ChineseChessComm::EMPTY)){
ChineseChessFunction::IdToAction(piece, i, j, action);
action_list_.push_back(action);
}
}
}else if (abs(i_x - j_x) == 0 && abs(i_y - j_y) != 0){ //横向
if (i_y - j_y > 0){
for (l_y = i_y - 1; l_y > j_y; l_y){
l_x = i_x;
ChineseChessFunction::CoordToId(l_x, l_y, l);
if (board_state_[l] != ChineseChessComm::EMPTY){
count++;
}
}
if ((count == 1 && (board_state_[j] != ChineseChessComm::EMPTY && !is_mystone)) || (count == 0 && board_state_[j] == ChineseChessComm::EMPTY)){
ChineseChessFunction::IdToAction(piece, i, j, action);
action_list_.push_back(action);
}
}else{
for (l_y = i_y + 1; l_y < j_y; l_y++){
l_x = i_x;
ChineseChessFunction::CoordToId(l_x, l_y, l);
if (board_state_[l] != ChineseChessComm::EMPTY){
count++;
}
}
if ((count == 1 && (board_state_[j] != ChineseChessComm::EMPTY && !is_mystone)) || (count == 0 && board_state_[j] == ChineseChessComm::EMPTY)){
ChineseChessFunction::IdToAction(piece, i, j, action);
action_list_.push_back(action);
}
}
}
}
break;
case ChineseChessComm::RED_ELEPHANT : case ChineseChessComm::BLACK_ELEPHANT : // Elephant
{
i_x = i_y = j_x = j_y = k_x = k_y = l_x = l_y = ChineseChessComm::COORD_UNSET;
ChineseChessFunction::IdToCoord(i, i_x, i_y);
ChineseChessFunction::IdToCoord(j, j_x, j_y);
l_x = (i_x + j_x)/2;
l_y = (i_y + j_y)/2;
ChineseChessFunction::CoordToId(l_x, l_y, l);
if(!is_mystone){
if (board_state_[l] == ChineseChessComm::EMPTY){ //中間沒卡到東西
if (current_player_ == ChineseChessComm::RED){
if (abs(j_x - i_x) == 2 && abs(j_y - i_y) == 2 && j_x >= 5 && j_x <= 9){
ChineseChessFunction::IdToAction(piece, i, j, action);
action_list_.push_back(action);
}
}else{
if (abs(j_x - i_x) == 2 && abs(j_y - i_y) == 2 && j_x>= 0 && j_x <= 4){
ChineseChessFunction::IdToAction(piece, i, j, action);
action_list_.push_back(action);
}
}
}
}
}
break;
case ChineseChessComm::RED_ADVISER : case ChineseChessComm::BLACK_ADVISER : // Adviser
{
i_x = i_y = j_x = j_y = k_x = k_y = l_x = l_y = ChineseChessComm::COORD_UNSET;
ChineseChessFunction::IdToCoord(i, i_x, i_y);
ChineseChessFunction::IdToCoord(j, j_x, j_y);
if(!is_mystone){
if (j_y >= 3 && j_y <= 5) {
if (current_player_ == ChineseChessComm::RED) {
if (abs(j_x - i_x) == 1 && abs(j_y - i_y) == 1 && j_x >= 7 && j_x <= 9){
ChineseChessFunction::IdToAction(piece, i, j, action);
action_list_.push_back(action);
}
}else {
if (abs(j_x - i_x) == 1 && abs(j_y - i_y) == 1 && j_x >= 0 && j_x <= 2){
ChineseChessFunction::IdToAction(piece, i, j, action);
action_list_.push_back(action);
}
}
}
}
}
break;
case ChineseChessComm::RED_PAWN : case ChineseChessComm::BLACK_PAWN : // Pawn
{
i_x = i_y = j_x = j_y = k_x = k_y = l_x = l_y = ChineseChessComm::COORD_UNSET;
ChineseChessFunction::IdToCoord(i, i_x, i_y);
ChineseChessFunction::IdToCoord(j, j_x, j_y);
if(!is_mystone){
if (current_player_ == ChineseChessComm::RED) {//red
if (i_x <= 4) {//已過河
if ((((i_x - j_x) == 1 && abs(i_y - j_y) == 0) ||
(abs(i_x - j_x) == 0 && abs(i_y - j_y) == 1))){
ChineseChessFunction::IdToAction(piece, i, j, action);
action_list_.push_back(action);
}
}else{//未過河
if (i_x - j_x == 1 && abs(i_y - j_y) == 0) {
ChineseChessFunction::IdToAction(piece, i, j, action);
action_list_.push_back(action);
}
}
}else {//black
if (i_x >= 5) {//已過河
if ((i_x - j_x == -1 && abs(i_y - j_y) == 0) ||
(abs(i_x - j_x) == 0 && abs(i_y - j_y) == 1)){
ChineseChessFunction::IdToAction(piece, i, j, action);
action_list_.push_back(action);
}
}else{//未過河
if (i_x - j_x == -1 && abs(i_y - j_y) == 0) {
ChineseChessFunction::IdToAction(piece, i, j, action);
action_list_.push_back(action);
}
}
}
}
}
break;
default : // Invalid, wrong color, or empty
continue;
}
}
}
}
bool ChineseChessEnv::IsMoveValid(int &action) const{
ChineseChessCoordId from_id, to_id;
ChineseChessStoneColor piece;
for (unsigned int i = 0; i < action_list_.size(); ++i){
if (action_list_[i] == action){
return true;
}
}
return false;
}
int ChineseChessEnv::CalcResult() const {
ChineseChessStoneColor king;
CSquare kingid = ChineseChessComm::COORD_UNSET;
if (current_player_ == ChineseChessComm::RED){
king = ChineseChessComm::RED_KING;
}
else if(current_player_ == ChineseChessComm::BLACK){
king = ChineseChessComm::BLACK_KING;
}
FOR_EACHCOORD(id){
if(board_state_[id] == king){
kingid = id;
break;
}
}
if(kingid == ChineseChessComm::COORD_UNSET){
if(current_player_ == ChineseChessComm::RED){
state_ = 2;//BLACK WIN!
}else if (current_player_ == ChineseChessComm::BLACK){
state_ = 1;//RED WIN!
}
}
return state_;
}
ChineseChessStoneColor ChineseChessEnv::GetWinner() const {
int result = CalcResult();
if (result == 3){
return ChineseChessComm::EMPTY;
}else if (result == 1){
return ChineseChessComm::RED;
}else if(result == 2){
return ChineseChessComm::BLACK;
}
}
void ChineseChessEnv::GetResult(float& res) {
if (GetWinner() == ChineseChessComm::EMPTY){
res = 0.0f;
}else{
res = GetWinner() == CurrentPlayer() ? -1.0f : 1.0f;
}
}
vector<bool> ChineseChessEnv::GetFeature(BaseFeature& feature) const { // HWC
feature.clear();
feature.resize(CHINESECHESSBOARD_SIZE * (SIZE_HISTORYEACHSIDE + 1));
//vector<bool> feature(CHINESECHESSBOARD_SIZE * (SIZE_HISTORYEACHSIDE + 1), 0);
// because push white into history first,
// if next player is white,
// we should get planes swap of each two planes
int reverse_plane = int(Self() == RED);
for (ChineseChessSize i = 0; i < SIZE_HISTORYEACHSIDE && i < feature_history_list_.size(); ++i) {
const string &feature_str = *(feature_history_list_.rbegin() + (i ^ reverse_plane));
for (int j = 0, k = i; j < CHINESECHESSBOARD_SIZE; ++j, k += (SIZE_HISTORYEACHSIDE + 1)) {
feature[k] = feature_str[j] - '0';
}
}
if (Self() == RED) {
for (int j = 0, k = SIZE_HISTORYEACHSIDE; j < CHINESECHESSBOARD_SIZE; ++j, k += (SIZE_HISTORYEACHSIDE + 1)) {
feature[k] = 1;
}
}
return feature;
}
int ChineseChessEnv::GetActionNum() {//include RESIGN
return 162000 + 1;
}
void ChineseChessEnv::GetInputDim(vector<int>& input_dim) {
input_dim.clear();
input_dim.push_back(SIZE_HISTORYEACHSIDE);
input_dim.push_back(BORDER_SIZE_HEIGHT);
input_dim.push_back(BORDER_SIZE_LENGTH)
}
void ChineseChessEnv::GetLegalAction(vector<int>& action) {
action.clear();
for (unsigned int i=0; i < action_list_.size(); ++i){
action.push_back(action_list_[i]);
}
}
std::string ChineseChessEnv::action2str(int action){
ChineseChessCoordId from_id, to_id;
ChineseChessStoneColor piece;
ChineseChessFunction::ActionToId(action, piece, from_id, to_id);
char buffer[12];
buffer[0] = 'f';
buffer[1] = 'r';
buffer[2] = 'o';
buffer[3] = 'm';
buffer[4] = ':';
buffer[7] = 't';
buffer[8] = 'o';
buffer[9] = ':';
ChineseChessCoordId from_x, from_y, to_x, to_y;
ChineseChessFunction::IdToCoord(from_id, from_x, from_y);
ChineseChessFunction::IdToCoord(to_id, to_x, to_y);
if (!InBoard(from_x, from_y)) {
buffer[5] = buffer[6] = 'z';
} else if(InBoard(from_x, from_y)){
buffer[5] = from_x + 'a';
buffer[6] = from_y + 'a';
}
if (!InBoard(to_x, to_y)) {
buffer[10] = buffer[11] = 'z';
} else if(InBoard(to_x, to_y)){
buffer[10] = to_x + 'a';
buffer[11] = to_y + 'a';
}
return string(buffer, 12);
}
/*void ChineseChessEnv::TransformCoord(ChineseChessCoordId &x, ChineseChessCoordId &y, int mode, bool reverse = false)
{
if (reverse) {
if (mode & 4) std::swap(x, y);
if (mode & 2) y = ChineseChessComm::BORDER_SIZE - y - 1;
if (mode & 1) x = ChineseChessComm::BORDER_SIZE - x - 1;
} else {
if (mode & 1) x = ChineseChessComm::BORDER_SIZE - x - 1;
if (mode & 2) y = ChineseChessComm::BORDER_SIZE - y - 1;
if (mode & 4) std::swap(x, y);
}
}*/
// void ChineseChessEnv::TransformFeatures(BaseFeature& feature, int transform_mode) {
// BaseFeature ret(feature.size());
// int depth = features.size() / ChineseChessComm::CHINESECHESSBOARD_SIZE;
// for (int i = 0; i < ChineseChessComm::CHINESECHESSBOARD_SIZE; ++i) {
// ChineseChessCoordId x, y;
// ChineseChessFunction::IdToCoord(i, x, y);
// TransformCoord(x, y, mode);
// int j = ChineseChessFunction::CoordToId(x, y);
// for (int k = 0; k < depth; ++k) {
// ret[i * depth + k] = features[j * depth + k];
// }
// }
// feature = std::move(ret);
// }
// void TransformPolicy(int transform_mode) {
// }
#include "chinesechess_comm.h"
#include "base_env.h"
#include <cstdlib>
#include <cstring>
#include <stack>
#include <string>
#include <unordered_set>
#include <vector>
#if defined(_WIN32) || defined(_WIN64)
# include <intrin.h>
# define __builtin_popcount __popcnt
# define __builtin_popcountll __popcnt64
#endif
typedef vector<bool> BaseFeature;
struct ChineseChessStone {
ChineseChessCoordId self_id; // Id of this stone
ChineseChessCoordId next_id; // Use like link-list
ChineseChessCoordId parent_id; // Use like union-find-set (rooted by tail)
// inline void SetSelfId(ChineseChessCoordId id) { self_id = id; }
inline void Reset(ChineseChessCoordId id = ChineseChessComm::COORD_UNSET) {
next_id = parent_id = self_id;
}
} ;
class ChineseChessEnv: public BaseEnv {
public:
ChineseChessEnv();
// ChineseChessEnv(const ChineseChessEnv &ge);
~ChineseChessEnv();
int Move(int action) override;
bool IsFinish() override { return ((state_ != 0 ? true : false) || is_resign_); }
void GetResult(float& res) override;
void GetFeature(BaseFeature& feature) override;
int GetActionNum() override;
void GetInputDim(vector<int>& input_dim) override;
void GetLegalAction(vector<int>& action) override;
bool IsLegal(int action) override { return ChineseChessFunction::IsResign(action) || IsMoveValid(action); }
std::string action2str(int action) override;
int CurrentPlayer() override { return current_player_; }
protected:
// void Init();
// void CopyFrom(const ChineseChessEnv &src);
int CalcResult() const;
ChineseChessStoneColor GetWinner() const;
inline void HandOff() { current_player_ = Opponent(); }
inline ChineseChessStoneColor Opponent(const ChineseChessStoneColor color = ChineseChessComm::COLOR_UNKNOWN) const {
return ChineseChessComm::RED + ChineseChessComm::BLACK
- (ChineseChessComm::COLOR_UNKNOWN != color ? color : current_player_);
}
inline ChineseChessStoneColor Self() const { return current_player_; }
inline ChineseChessCoordId GetLastMove() const { return last_action_; }
// inline ChineseChessStoneColor CurrentPlayer() const { return current_player_; }
//void ChineseChessEnv::TransformCoord(ChineseChessCoordId &x, ChineseChessCoordId &y, int mode, bool reverse = false)
void GetSensibleMove();
bool IsSquareThreatened(ChineseChessCoordId& id) const;
bool IsKingInCheck() const
void Find_Legal_Moves() const;
bool IsMoveValid(int& action) const;
protected:
// board utils
std::vector<std::int> action_list_;
ChineseChessStone stones_[ChineseChessComm::CHINESECHESSBOARD_SIZE];
ChineseChessStoneColor board_state_[ChineseChessComm::CHINESECHESSBOARD_SIZE];
ChineseChessStoneColor current_player_;
int last_action_;
bool is_resign_;
int state_;
// hash board state
std::unordered_set<uint64_t> board_hash_states_;
uint64_t zobrist_hash_value_;
// features
ChineseChessSize move_count_[162000];
std::vector<std::string> feature_history_list_;
};
No preview for this file type
No preview for this file type
No preview for this file type
...现已初步实现种棋类的扩展代码... ...现已初步实现种棋类的扩展代码...
每个文件夹代表一种棋类: 每个文件夹代表一种棋类:
Chess ... 国际象棋 Chess ... 国际象棋
Connect4 ... 平面四子棋 ChineseChess ... 中国象棋
GoBang ... 五子棋 Connect4 ... 平面四子棋
Othello ... 黑白棋 GoBang ... 五子棋
Othello ... 黑白棋
棋类文件夹中的文件夹(棋类英文小写字母开头)封装了基于师兄已完成工作的棋类子类代码...和我们的AlphaGoZero框架相对应 棋类文件夹中以棋类英文小写字母命名的文件夹封装了基于师兄已完成工作的棋类子类代码xxx和我们的AlphaGoZero框架相对应
棋类文件夹里面的代码和可执行文件为原有棋类代码及其编译结果 棋类文件夹里面Origin_Code文件夹内代码和可执行文件为原有棋类代码及其编译结果
\ No newline at end of file \ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment