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
stone 17
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 <string>
......
......@@ -29,8 +29,8 @@ const ChessCoordId COORD_UNSET = -2;
const ChessCoordId COORD_RESIGN = -3;
const ChessStoneColor EMPTY = 0;
const ChessStoneColor BLACK = 1;
const ChessStoneColor WHITE = 2;
const ChessStoneColor WHITE= 1;
const ChessStoneColor BLACK = 2;
const ChessStoneColor WALL = 3;
const ChessStoneColor WHITE_PAWN = 4;
const ChessStoneColor WHITE_KNIGHT = 5;
......
......@@ -895,7 +895,7 @@ bool ChessEnv::IsMoveValid(int &action) const{
ChessFunction::ActionToId(action, from_id, to_id, captured_stone, promoted_stone, piece);
piece = board_state_[from_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;
}
}
......@@ -924,9 +924,9 @@ int ChessEnv::CalcResult() const {
if(kingid == ChessComm::COORD_UNSET){
if(current_player_ == ChessComm::WHITE){
state_ = 1;//BLACK WIN!
state_ = 2;//BLACK WIN!
}else if (current_player_ == ChessComm::BLACK){
state_ = 2;//WHITE WIN!
state_ = 1;//WHITE WIN!
}
}
......@@ -939,9 +939,9 @@ ChessStoneColor ChessEnv::GetWinner() const {
if (result == 3){
return CHessComm::EMPTY;
}else if (result == 1){
return ChessComm::BLACK;
}else if(result == 2){
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 "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
/***************************************************************************
* 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_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 ... 国际象棋
Connect4 ... 平面四子棋
GoBang ... 五子棋
Othello ... 黑白棋
Chess ... 国际象棋
ChineseChess ... 中国象棋
Connect4 ... 平面四子棋
GoBang ... 五子棋
Othello ... 黑白棋
棋类文件夹中的文件夹(棋类英文小写字母开头)封装了基于师兄已完成工作的棋类子类代码...和我们的AlphaGoZero框架相对应
棋类文件夹里面的代码和可执行文件为原有棋类代码及其编译结果
\ No newline at end of file
棋类文件夹中以棋类英文小写字母命名的文件夹封装了基于师兄已完成工作的棋类子类代码xxx和我们的AlphaGoZero框架相对应
棋类文件夹里面Origin_Code文件夹内代码和可执行文件为原有棋类代码及其编译结果
\ 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