Commit f6c221a4 by WangChenxi

Add games

parents
File added
File added
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
#include <vector>
#include <string>
#include "CMove.h"
#include "CMoveList.h"
#ifndef _C_BOARD_H_
#define _C_BOARD_H_
// The board is represented as an array of 120 elements.
// arranged in 12 rows and 10 columns.
// The following diagram shows the numbering of the elements.
// Note: The square A1 is element number 21
//
// 110 111 112 113 114 115 116 117 118 119
// 100 101 102 103 104 105 106 107 108 109
// 90 A1 92 93 94 95 96 97 H8 99
// 80 81 82 83 84 85 86 87 88 89
// 70 71 72 73 74 75 76 77 78 79
// 60 61 62 63 64 65 66 67 68 69
// 50 51 52 53 54 55 56 57 58 59
// 40 41 42 43 44 45 46 47 48 49
// 30 31 32 33 34 35 36 37 H2 39
// 20 A1 22 23 24 25 26 27 H1 29
// 10 11 12 13 14 15 16 17 18 19
// 0 1 2 3 4 5 6 7 8 9
//
// Each element contains one of the following values:
// 0 : Empty
// 1 : White Pawn
// 2 : White Knight
// 3 : White Bishop
// 4 : White Rook
// 5 : White Queen
// 6 : White King
// -1 : Black Pawn
// -2 : Black Knight
// -3 : Black Bishop
// -4 : Black Rook
// -5 : Black Queen
// -6 : Black King
// 99 : INVALID
enum // Directions
{
N = 10, S = -10, E = -1, W = 1,
NW = 11, SW = -9, NE = 9, SE = -11,
NNW = 21, NNE = 19, NWW = 12, NEE = 8,
SSW = -19, SSE = -21, SWW = -8, SEE = -12
};
/***************************************************************
* declaration of CBoard
***************************************************************/
class CBoard
{
public:
CBoard() { newGame(); }
// Copy constructor
CBoard(const CBoard& rhs);
void newGame();
void find_legal_moves(CMoveList &moves) const;
void make_move(const CMove &move);
void undo_move(const CMove &move);
int get_value();
bool IsMoveValid(CMove &move) const;
bool isKingInCheck() const;
int isGameOver() const;//add
bool isOtherKingInCheck() const;
friend std::ostream& operator <<(std::ostream &os, const CBoard &rhs);
private:
bool isSquareThreatened(const CSquare& sq) const;
void swap_sides() {m_side_to_move = -m_side_to_move;}
std::vector<int8_t> m_board;
int m_side_to_move;
int m_material;
}; // end of class CBoard
#endif // _C_BOARD_H_
File added
#include <sstream>
#include <assert.h>
#include "CMove.h"
std::ostream& operator <<(std::ostream &os, const CMove &rhs)
{
return os << rhs.ToShortString();
} // end of std::ostream& operator <<(std::ostream &os, const CMove &rhs)
static char pieces[] = "kqrbnp.PNBRQK";
std::string CMove::ToShortString() const
{
std::stringstream ss;
assert (m_captured != IV);
ss << m_from;
ss << m_to;
if (m_promoted != EM)
{
ss << (char) tolower(pieces[m_promoted+6]);
}
return ss.str();
}
std::string CMove::ToLongString() const
{
std::stringstream ss;
assert (m_captured != IV);
ss << pieces[m_piece+6];
ss << m_from;
if (m_captured != EM)
ss << "*";
else
ss << "-";
ss << m_to;
if (m_promoted != EM)
{
ss << "=";
ss << (char) tolower(pieces[m_promoted+6]);
}
return ss.str();
}
// Returns NULL if error
const char * CMove::FromString(const char *s)
{
if (m_from.FromString(s) || m_to.FromString(s+2))
return NULL;
s += 4;
m_piece = EM;
m_captured = EM;
m_promoted = EM;
if (m_to.row() == 1)
{
switch (tolower(s[0]))
{
case 'q' : m_promoted = BQ; s++; break;
case 'r' : m_promoted = BR; s++; break;
case 'b' : m_promoted = BB; s++; break;
case 'n' : m_promoted = BN; s++; break;
case ' ' : break;
case '\0': break;
default : return NULL;
}
}
else if (m_to.row() == 8)
{
switch (tolower(s[0]))
{
case 'q' : m_promoted = WQ; s++; break;
case 'r' : m_promoted = WR; s++; break;
case 'b' : m_promoted = WB; s++; break;
case 'n' : m_promoted = WN; s++; break;
case ' ' : break;
case '\0': break;
default : return NULL;
}
}
while (s[0] == ' ')
s++;
return s;
} /* end of FromString */
This diff is collapsed. Click to expand it.
#ifndef _C_MOVE_H_
#define _C_MOVE_H_
#include "CSquare.h"
typedef enum {
EM = 0, // Empty
WP = 1, // White Pawn
WN = 2, // White Knight
WB = 3, // White Bishop
WR = 4, // White Rook
WQ = 5, // White Queen
WK = 6, // White King
BP = -1, // Black Pawn
BN = -2, // Black Knight
BB = -3, // Black Bishop
BR = -4, // Black Rook
BQ = -5, // Black Queen
BK = -6, // Black King
IV = 99 // INVALID
} e_piece;
/***************************************************************
* declaration of CMove
***************************************************************/
class CMove
{
public:
friend class CBoard;
friend std::ostream& operator <<(std::ostream &os, const CMove &rhs);
// Constructors
CMove() : m_piece(IV), m_captured(IV) {}
CMove(const char *str) : m_captured(IV) { FromString(str); }
CMove(int8_t piece, const CSquare& from, const CSquare& to, int8_t captured = EM, int8_t promoted=EM) :
m_from(from), m_to(to), m_piece(piece), m_captured(captured), m_promoted(promoted) {}
const char * FromString(const char *s); // Returns NULL if error
// Accessor functions
std::string ToShortString() const;
std::string ToLongString() const;
CSquare From(void) const {return m_from;}
CSquare To(void) const {return m_to;}
bool Valid(void) const { return m_captured != IV; }
bool is_captured_piece_a_king(void) const { return (m_captured == WK || m_captured == BK); }
bool is_it_a_capture(void) const { return (m_captured != EM); }
bool operator==(const CMove& rhs) const
{
if (rhs.From() != From())
return false;
if (rhs.To() != To())
return false;
return true;
}
private:
CSquare m_from;
CSquare m_to;
int8_t m_piece;
int8_t m_captured;
int8_t m_promoted;
}; /* end of CMove */
#endif // _C_MOVE_H_
File added
#include "CMoveList.h"
std::ostream& operator <<(std::ostream &os, const CMoveList &rhs)
{
for (std::vector<CMove>::const_iterator it = rhs.m_moveList.begin(); it != rhs.m_moveList.end(); ++it)
{
os << (*it).ToShortString() << " ";
}
return os;
} // end of std::ostream& operator <<(std::ostream &os, const CMoveList &rhs)
#ifndef _C_MOVELIST_H_
#define _C_MOVELIST_H_
#include <iostream>
#include <vector>
#include "CMove.h"
/***************************************************************
* declaration of CMoveList
*
* This is a wrapper for the std::vector class.
* It contains an array of moves.
***************************************************************/
class CMoveList
{
public:
CMoveList()
{
// Pre-allocate space for a large number of moves.
// This is a slight optimization.
m_moveList.reserve(100);
}
friend std::ostream& operator <<(std::ostream &os, const CMoveList &rhs);
void push_back(const CMove& move)
{
m_moveList.push_back(move);
}
void clear()
{
m_moveList.clear();
}
unsigned int size() const
{
return m_moveList.size();
}
const CMove & operator [] (unsigned int ix) const { return m_moveList[ix]; }
private:
std::vector<CMove> m_moveList;
}; /* end of CMoveList */
#endif // _C_MOVELIST_H_
#ifndef _C_SQUARE_H_
#define _C_SQUARE_H_
#include <stdint.h>
#include <iostream>
// Useful enumerations to conveniently address specific squares on the board
enum // Squares
{
A8 = 91, B8, C8, D8, E8, F8, G8, H8,
A7 = 81, B7, C7, D7, E7, F7, G7, H7,
A6 = 71, B6, C6, D6, E6, F6, G6, H6,
A5 = 61, B5, C5, D5, E5, F5, G5, H5,
A4 = 51, B4, C4, D4, E4, F4, G4, H4,
A3 = 41, B3, C3, D3, E3, F3, G3, H3,
A2 = 31, B2, C2, D2, E2, F2, G2, H2,
A1 = 21, B1, C1, D1, E1, F1, G1, H1,
};
/***************************************************************
* declaration of CSquare
*
* This class encapsulates the notion of a square.
* This class converts between an internal (0 - 119)
* and a (column,row) representation.
***************************************************************/
class CSquare
{
public:
CSquare(uint32_t square = 0) :
m_sq(square) {}
operator int() const { return m_sq; } // Implicit conversion to integer
int row() const {return (m_sq/10) - 1;} // returns 1 - 8
int col() const {return (m_sq%10);} // returns 1 - 8
friend std::ostream& operator <<(std::ostream &os, const CSquare &rhs)
{
char c = (rhs.m_sq%10) + 'a' - 1;
char r = (rhs.m_sq/10) + '1' - 2;
return os << c << r;
}
int FromString(const char *s)
{
uint32_t col = s[0] - 'a';
uint32_t row = s[1] - '1';
m_sq = row*10 + col + 21;
return (col >= 8 || row >= 8);
// Returns true if error
}
private:
uint8_t m_sq; // Internal representation, 0 - 119.
}; /* end of class CSquare */
#endif // _C_SQUARE_H_
This diff is collapsed. Click to expand it.
sources = main.cc
sources += CBoard.cc
sources += CMove.cc
sources += ai.cc
sources += CMoveList.cc
program = mchess
TARGET = linux
#TARGET = windows
objects = $(sources:.cc=.o)
depends = $(sources:.cc=.d)
OPTIONS = -W -Wall
OPTIONS += -O3
OPTIONS += -g # debug info
#OPTIONS += -pg # profiling
ifeq ($(TARGET),linux)
CC = g++
else
CC = i686-w64-mingw32-g++
OPTIONS += -static-libgcc -static-libstdc++
program := $(program).exe
endif
$(program): $(objects) Makefile
$(CC) -o $@ $(objects) $(OPTIONS)
cp $@ $(HOME)/bin
%.d: %.cc Makefile
set -e; $(CC) -M $(CPPFLAGS) $(DEFINES) $(INCLUDE_DIRS) $< \
| sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \
[ -s $@ ] || rm -f $@
include $(depends)
%.o : %.cc Makefile
$(CC) $(OPTIONS) $(DEFINES) $(INCLUDE_DIRS) -c $< -o $@
clean:
-rm -f $(objects)
-rm -f $(depends)
-rm -f $(program)
mchess
======
This is a simple (minimalistic) chess engine, but functional never the less.
It is meant as a starting point for writing your own chess engine. Use it for
inspiration :-)
This is actually a part of the answer:
http://www.quora.com/How-do-I-get-started-with-my-own-chess-engine/answer/Michael-J%C3%B8rgensen-2
Features
========
- Includes a simple alpha-beta search strategy.
- Searches a fixed depth of four ply.
- A simple console (ASCII) user interface.
Documentation
=============
The design is documented in the file DOC.html.
The source code has comments in most places. Otherwise, the best place for a high level description is the
above link.
Additionally, the directory doc contains some additional information on writing chess engines.
TODO
====
- Castling
- En passant
- UCI interface (for GUI)
- Time control
- Opening book
#include <stdlib.h> // exit()
#include "ai.h"
#include "CMoveList.h"
/***************************************************************
* search
*
* This is a very basic (Fail-Soft Negamax) alpha-beta search.
* For details, see e.g.
* http://chessprogramming.wikispaces.com/Alpha-Beta
* and
* http://en.wikipedia.org/wiki/Alpha-beta_pruning
*
* Parameter values are:
* * alpha, beta. The search window
* * level. The number of ply (half-moves) to search.
*
* The value returned by this function is
* * positive, if the side to move has an advantage (is winning).
* * negative, if the side to move is losing.
* * zero, if the position is equal.
* * 900+, if the opponent is check-mated.
* * -999, if the side to move is check-mated.
**=************************************************************/
int AI::search(int alpha, int beta, int level)
{
if (level == 0)
return m_board.get_value(); // We are at leaf, just return the static evaluation.
CMoveList moves;
m_board.find_legal_moves(moves);
int best_val = -999; // Assume the worst
// Search through all legal moves
for (unsigned int i=0; i<moves.size(); ++i)
{
if (best_val >= beta)
{
// This is the alpha-beta pruning.
// Stop searching, if we already have found a "killer" move.
break;
}
if (best_val > alpha)
{
// This is part of the alpha-beta pruning too.
// Tighten the search window.
alpha = best_val;
}
const CMove& move = moves[i];
if (move.is_captured_piece_a_king())
{
return 900 + level; // Opponent's king can be captured. That means he is check-mated.
}
// Do a recursive search
m_board.make_move(move);
int num = -search(-beta, -alpha, level-1);
m_board.undo_move(move);
if (num > best_val)
{
// Store the best value so far.
best_val = num;
}
}
return best_val;
} // end of int search
/***************************************************************
* find_best_move
*
* This is the main AI.
* It returns what it considers to be the best legal move in the
* current position.
***************************************************************/
CMove AI::find_best_move()
{
// Make a list of all legal moves
CMoveList moves;
m_board.find_legal_moves(moves);
std::cout << "info string " << moves.size() << " legal moves." << std::endl;
CMoveList best_moves; // Array of the (one or more) best moves so far
int best_val = -999;
// Search through all legal moves
for (unsigned int i=0; i<moves.size(); ++i)
{
CMove move = moves[i];
// Get value of current move
m_board.make_move(move);
int val = -search(-999, 999, 4);
m_board.undo_move(move);
std::cout << "info string " << val << " : " << move << std::endl;
if (val > best_val)
{
best_val = val;
best_moves.clear();
best_moves.push_back(move);
}
else if (val == best_val)
{
best_moves.push_back(move);
}
}
if (best_moves.size() == 0)
{
std::cout << "BUG: No legal moves!" << std::endl;
exit(1);
}
return best_moves[rand() % best_moves.size()];
} // end of find_best_move
This diff is collapsed. Click to expand it.
#ifndef _AI_H_
#define _AI_H_
#include "CBoard.h"
class AI
{
public:
AI(CBoard& board) : m_board(board) { }
CMove find_best_move();
private:
int search(int alpha, int beta, int level);
CBoard& m_board;
}; // end of class AI
#endif // _AI_H_
File added
/*
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 64
to_id 64
captured 10
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
#include <cstdlib>
#include <string>
class Chess: public BaseEnv {
public:
Chess();
~Chess();
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 "chess_comm.h"
#include <cstring>
#include <mutex>
// #include <glog/logging.h>
#define x first
#define y second
using namespace std;
using namespace ChessComm;
ChessHashValuePair g_hash_weight[BORDER_SIZE][BORDER_SIZE];
ChessCoordId g_log2_table[67];
uint64_t g_zobrist_board_hash_weight[4][CHESSBOARD_SIZE];
uint64_t g_zobrist_player_hash_weight[4];
namespace ChessFunction {
bool InBoard(const ChessCoordId id) {
return 0 <= id && id < CHESSBOARD_SIZE;
}
bool InBoard(const ChessCoordId x, const ChessCoordId y) {
return 0 <= x && x < BORDER_SIZE
&& 0 <= y && y < BORDER_SIZE;
}
bool IsUnset(const ChessCoordId id) {
return COORD_UNSET == id;
}
bool IsUnset(const ChessCoordId x, const ChessCoordId y) {
return COORD_UNSET == CoordToId(x, y);
}
bool IsResign(const ChessCoordId id) {
return COORD_RESIGN == id;
}
bool IsResign(const ChessCoordId x, const ChessCoordId y) {
return COORD_RESIGN == CoordToId(x, y);
}
void IdToCoord(const ChessCoordId id, ChessCoordId &x, ChessCoordId &y) {
if (COORD_RESIGN == id) {
x = y = COORD_RESIGN;
} else if (!InBoard(id)) {
x = y = COORD_UNSET;
} else {
x = id / BORDER_SIZE;
y = id % BORDER_SIZE;
}
}
ChessCoordId CoordToId(const ChessCoordId x, const ChessCoordId y) {
if (COORD_RESIGN == x && COORD_RESIGN == y) {
return COORD_RESIGN;
}
if (!InBoard(x, y)) {
return COORD_UNSET;
}
return x * BORDER_SIZE + y;
}
void StrToCoord(const string &str, ChessCoordId &x, ChessCoordId &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 ChessCoordId x, const ChessCoordId 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 ChessCoordId id) {
ChessCoordId x, y;
IdToCoord(id, x, y);
return CoordToStr(x, y);
}
ChessCoordId StrToId(const std::string &str) {
ChessCoordId x, y;
StrToCoord(str, x, y);
return CoordToId(x, y);
}
void ActionToId(const int &action, const ChessCoordId &from_id, const ChessCoordId &to_id, ChessStoneColor &captured_stone, ChessStoneColor &promoted_stone, ChessStoneColor &stone){
int code[5];
int i;
if(action == COORD_RESIGN){
from_id = to_id = COORD_RESIGN;
captured_stone = promoted_stone = stone = EMPTY;
}else if(action == COORD_UNSET){
from_id = to_id = COORD_UNSET;
captured_stone = promoted_stone = stone = EMPTY;
}else{
for(i = 0; i < 5; i++){
code[i] = 0;
}
for(i = 0; i < 5; i++){
code[i] = action % 64;
action = action / 64;
}
from_id = code[4];
to_id = code[3];
captured_stone = code[2];
promoted_stone = code[1];
stone = code[0];
}
}
void IdToAction(const ChessCoordId &from_id, const ChessCoordId &to_id, ChessStoneColor &captured_stone, ChessStoneColor &promoted_stone, ChessStoneColor &stone, const int &action){
int code[5];
int i, j;
for(i = 0; i < 5; 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(from_id >= 0 && to_id >= 0 && captured_stone >= 0 && promoted_stone >= 0 && stone >= 0){
for(i = 0; i < 5; i++){
for(j = i; j > 0; j--){
code[i] *= 64;
}
}
action = from_id * code[4] + to_id * code[3] + captured_stone * code[2] + promoted_stone * code[1] + stone * code[0];
}
}
once_flag CreateGlobalVariables_once;
void CreateGlobalVariables() {
call_once(
CreateGlobalVariables_once,
[]() {
CreateHashWeights();
CreateQuickLog2Table();
CreateZobristHash();
}
);
}
void CreateHashWeights() {
g_hash_weight[0][0] = ChessHashValuePair(1, 1);
for (ChessCoordId i = 1; i < CHESSBOARD_SIZE; ++i) {
g_hash_weight[i / BORDER_SIZE][i % BORDER_SIZE] =
ChessHashValuePair(g_hash_weight[(i - 1) / BORDER_SIZE][(i - 1) % BORDER_SIZE].x * g_hash_unit.x,
g_hash_weight[(i - 1) / BORDER_SIZE][(i - 1) % BORDER_SIZE].y * g_hash_unit.y);
}
}
void CreateQuickLog2Table() {
memset(g_log2_table, -1, sizeof(g_log2_table));
int tmp = 1;
for (ChessCoordId 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 < CHESSBOARD_SIZE; ++j) {
g_zobrist_board_hash_weight[i][j] = (uint64_t) rand_r(&seed) << 32 | rand_r(&seed);
}
}
}
} // namespace ChessFunction
#undef y
#undef x
#pragma once
#include <inttypes.h>
#include <string>
#include <vector>
//Chess Board Id:
// 0 1 2 3 4 5 6 7
// 8 9 10 11 12 13 14 15
// 16 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
//End
// Return code of functions should be "int"
typedef uint8_t ChessStoneColor; // Stone color
typedef int16_t ChessCoordId; // Stone IDs or coordinates
typedef int16_t ChessSize; // Counts of visit times, used blocks, .. or other count
namespace ChessComm {
const ChessCoordId BORDER_SIZE = 8; // sxk_modify
const ChessCoordId CHESSBOARD_SIZE = BORDER_SIZE * BORDER_SIZE;
const ChessCoordId COORD_UNSET = -2;
//const ChessCoordId COORD_PASS = -1;
const ChessCoordId COORD_RESIGN = -3;
const ChessStoneColor EMPTY = 0;
const ChessStoneColor BLACK = 1;
const ChessStoneColor WHITE = 2;
const ChessStoneColor WALL = 3;
const ChessStoneColor WHITE_PAWN = 4;
const ChessStoneColor WHITE_KNIGHT = 5;
const ChessStoneColor WHITE_BISHOP = 6;
const ChessStoneColor WHITE_ROOK = 7;
const ChessStoneColor WHITE_QUEEN = 8;
const ChessStoneColor WHITE_KING = 9;
const ChessStoneColor BLACK_PAWN = 10;
const ChessStoneColor BLACK_KNIGHT = 11;
const ChessStoneColor BLACK_BISHOP = 12;
const ChessStoneColor BLACK_ROOK = 13;
const ChessStoneColor BLACK_QUEEN = 14;
const ChessStoneColor BLACK_KING = 15;
const ChessStoneColor COLOR_UNKNOWN = -1;
const char *const COLOR_STRING[] = { "Empty", "Black", "White", "Wall", "White Pawn", "White Knight", "White Bishop", "White Rook", "White Queen", "White King", "Black Pawn", "Black Knight", "Black Bishop", "Black Rook", "Black Queen", "Black King" };
const ChessCoordId N = -8;
const ChessCoordId S = 8;
const ChessCoordId E = 1;
const ChessCoordId W = -1;
const ChessCoordId NW = -9;
const ChessCoordId SW = 7;
const ChessCoordId NE = -7;
const ChessCoordId SE = 9;
const ChessCoordId NNW = -17;
const ChessCoordId NNE = -15;
const ChessCoordId NWW = -10;
const ChessCoordId NEE = -6;
const ChessCoordId SSW = 15;
const ChessCoordId SSE = 17;
const ChessCoordId SWW = 6;
const ChessCoordId SEE = 10;
} // namespace ChessComm
namespace ChessFeature {
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 ChessFeature
namespace ChessFunction {
extern bool InBoard(const ChessCoordId id);
extern bool InBoard(const ChessCoordId x, const ChessCoordId y);
extern bool IsUnset(const ChessCoordId id);
extern bool IsUnset(const ChessCoordId x, const ChessCoordId y);
extern bool IsResign(const ChessCoordId id);
extern bool IsResign(const ChessCoordId x, const ChessCoordId y);
extern void IdToCoord(const ChessCoordId id, ChessCoordId &x, ChessCoordId &y);
extern ChessCoordId CoordToId(const ChessCoordId x, const ChessCoordId y);
extern void StrToCoord(const std::string &str, ChessCoordId &x, ChessCoordId &y);
extern std::string CoordToStr(const ChessCoordId x, const ChessCoordId y);
extern std::string IdToStr(const ChessCoordId id);
extern ChessCoordId StrToId(const std::string &str);
extern void ActionToId(const int &action, const ChessCoordId &from_id, const ChessCoordId &to_id, ChessStone &captured_stone, ChessStone &promoted_stone, ChessStone &stone);
extern void IdToAction(const ChessCoordId &from_id, const ChessCoordId &to_id, ChessStone &captured_stone, ChessStone &promoted_stone, ChessStone &stone, const int &action);
extern void CreateGlobalVariables();
extern void CreateHashWeights();
extern void CreateQuickLog2Table();
extern void CreateZobristHash();
} // namespace ChessFunction
//typedef std::pair<ChessCoordId, ChessCoordId> ChessPosition;
typedef std::pair<uint64_t, uint64_t> ChessHashValuePair;
extern ChessHashValuePair g_hash_weight[ChessComm::BORDER_SIZE][ChessComm::BORDER_SIZE];
const ChessHashValuePair g_hash_unit(3, 7);
extern uint64_t g_zobrist_board_hash_weight[4][ChessComm::CHESSBOARD_SIZE];
extern uint64_t g_zobrist_player_hash_weight[4];
//action_count:统计已落子数,state:统计棋局状态,0未分胜负,1黑胜,2白胜,3平局
//extern int action_count = 0;
//extern int state = 0;
extern ChessCoordId g_log2_table[67];
#define FOR_EACHCOORD(id) for (ChessCoordId id = 0; id < ChessComm::CHESSBOARD_SIZE; ++id)
#include "chess_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 ChessStone {
ChessCoordId self_id; // Id of this stone
ChessCoordId next_id; // Use like link-list
ChessCoordId parent_id; // Use like union-find-set (rooted by tail)
// inline void SetSelfId(ChessCoordId id) { self_id = id; }
inline void Reset(ChessCoordId id = ChessComm::COORD_UNSET) {
next_id = parent_id = self_id;
}
} ;
class ChessEnv: public BaseEnv {
public:
ChessEnv();
// ChessEnv(const ChessEnv &ge);
~ChessEnv();
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 ChessFunction::IsResign(action) || IsMoveValid(action); }
//std::string action2str(int action) override { return ChessFunction::IdToStr(const ChessCoordId action); }
std::string action2str(int action) override;
int CurrentPlayer() override { return current_player_; }
protected:
// void Init();
// void CopyFrom(const ChessEnv &src);
//int ChessResult(ChessCoordId x, ChessCoordId y) const;
int CalcResult() const;
ChessStoneColor GetWinner() const;
inline void HandOff() { current_player_ = Opponent(); }
inline ChessStoneColor Opponent(const ChessStoneColor color = ChessComm::COLOR_UNKNOWN) const {
return ChessComm::BLACK + ChessComm::WHITE
- (ChessComm::COLOR_UNKNOWN != color ? color : current_player_);
}
inline ChessStoneColor Self() const { return current_player_; }
inline ChessCoordId GetLastMove() const { return last_action_; }
// inline ChessStoneColor CurrentPlayer() const { return current_player_; }
//void ChessEnv::TransformCoord(ChessCoordId &x, ChessCoordId &y, int mode, bool reverse = false)
void GetSensibleMove();
bool IsSquareThreatened(ChessCoordId& id) const;
bool IsKingInCheck() const
void Find_Legal_Moves() const;
bool IsMoveValid(int& action) const;
protected:
// board utils
std::vector<std::int> action_list_;
ChessStone stones_[ChessComm::CHESSBOARD_SIZE];
ChessStoneColor board_state_[ChessComm::CHESSBOARD_SIZE];
ChessStoneColor current_player_;
int last_action_;
bool is_resign_;
int state_;
int material_;
// hash board state
std::unordered_set<uint64_t> board_hash_states_;
uint64_t zobrist_hash_value_;
// features
ChessSize move_count_[1073741824];
std::vector<std::string> feature_history_list_;
};
#include <iostream>
#include <stdlib.h>
#include "CBoard.h"
#include "ai.h"
/***************************************************************
* main
*
* This is where it all starts!
***************************************************************/
int main()
{
srand(time(0)); // Seed the random number generator.
CBoard board;
AI ai(board);
int judge = 0;
while (true) // Repeat forever
{
// Display board
std::cout << board;
std::cout << "Input command : ";
// Read input from player
std::string str;
getline(std::cin, str);
std::cout << std::endl;
// Parse input from player
if (std::cin.eof() || str == "quit")
{
exit(1);
}
if (str.compare(0, 5, "move ") == 0)
{
CMove move;
if (move.FromString(str.c_str()+5) == NULL)
{
// Could not parse move.
std::cout << "Try again. Use long notation, e.g. e2e4" << std::endl;
continue; // Go back to beginning
}
if (board.IsMoveValid(move))
{
board.make_move(move);
bool check = board.isOtherKingInCheck();
judge = board.isGameOver();
board.undo_move(move);
if (check)
{
std::cout << "You are in CHECK! Be serious!" << std::endl;
//continue;
}
/*if(judge == 1){
std::cout << "BLACK WIN!" << std::endl;
return 0;
}else if(judge == 2){
std::cout << "WHITE WIN!" << std::endl;
return 0;
}*/
std::cout << "You move : " << move << std::endl;
board.make_move(move);
if(judge == 1){
std::cout << "BLACK WIN!" << std::endl;
exit(1);
}else if(judge == 2){
std::cout << "WHITE WIN!" << std::endl;
exit(1);
}
}
else
{
std::cout << "Move " << move << " is not legal." << std::endl;
continue;
}
} // end of "move "
else if (str.compare(0, 2, "go") == 0)
{
CMove best_move = ai.find_best_move();
std::cout << "bestmove " << best_move << std::endl;
judge = board.isGameOver();
/*if(judge == 1){
std::cout << "BLACK WIN!" << std::endl;
return 0;
}else if(judge == 2){
std::cout << "WHITE WIN!" << std::endl;
return 0;
}*/
board.make_move(best_move);
if(judge == 1){
std::cout << "BLACK WIN!" << std::endl;
exit(1);
}else if(judge == 2){
std::cout << "WHITE WIN!" << std::endl;
exit(1);
}
} // end of "go"
else if (str == "show")
{
CMoveList moves;
board.find_legal_moves(moves);
// std::cout << moves.ToShortString() << std::endl;
std::cout << moves << std::endl;
}
else
{
std::cout << "Unknown command" << std::endl;
std::cout << "Valid commands are: quit, move, go, show" << std::endl;
}
} // end of while (true)
return 0;
} // end of int main()
This diff is collapsed. Click to expand it.
File added
File added
/*
* Filename: AI.cpp
* Author: Aaron Schraner
* Date: April 12, 2015
* Purpose: Artificial Intelligence member functions for connect 4 program
* CST 136
*/
#include "AI.h"
#include "Board.h"
//pick a spot and drop AI tile
void AI::runAI()
{
//try to find one-move wins first
if( (!SpecialAI(board->p2)) && (!SpecialAI(board->p1)))
NormalAI();
//if none are found, put a tile close to the last one
}
//log moves if desired
#ifdef LOGMOVES
void AI::runAI(std::vector<Move*> &v)
{
runAI();
v.push_back(new Move(lastX, lastY, board->p2));
}
#endif
//AI for cases where nobody can win in 1 move
void AI::NormalAI()
{
//if this isn't the first move
if(lastX>=0&&lastY>=0)
{
for(int i=travelDir;i<8;i++)
{
//try all directions adjacent to this tile, starting with
//the last successful direction
//if the spot I want is blank
if(board->relTo(lastX, lastY, i, 1)==' ')
{
//put a tile there and save the coordinates
lastX+=rtX[i];
lastY=board->drop(lastX, board->p2);
//save the direction
travelDir=i;
//exit the function
return;
}
}
}
//if a successful match wasn't found
{
do
{
//put it somewhere random, keep trying until either success
//or board fills up
srand(time(NULL)-randoff);
randoff++;
lastX = rand() % ( board->getWidth() );
} while(
(lastY=board->drop(lastX, board->p2)) == -1 && //repeat if placing fails
!board->full() ); //break if board is full
}
}
//AI for cases in which <player> can win in one move
bool AI::SpecialAI(char player)
{
Board bcopy(board->getHeight(), board->getWidth());
//create another board
//iterate through all columns
for(int x=0; x<board->getWidth(); x++)
{
for(int a=0; a<board->getWidth(); a++)
for(int b=0; b<board->getHeight(); b++)
bcopy.board[b][a]=board->board[b][a];
//copy the original board into the new one
int z=bcopy.drop(x,player); //try placing a tile in column i
if(bcopy.won(x,z,player)) //if that beat the game
{
board->drop(x,board->p2); //put my piece there
lastX=x;
lastY=z; //and save the coords
return true; //and exit
}
bcopy.clear();
}
return false; //if nothing beat the game in one move, return false
}
//did the AI just win?
bool AI::justWon()
{
return board->won(lastX, lastY, board->p2);
}
/*
* Filename: AI.h
* Author: Aaron Schraner
* Date: April 12, 2015
* Purpose: Artificial intelligence (solver)
* class definition for connect 4 program
* CST 136
*/
#ifndef AI_H
#define AI_H
#include "Board.h"
//for logging moves
#ifdef LOGMOVES
#include "Move.h"
#include <vector>
#endif
class AI
{
private:
Board* board; //board to operate on
int lastX, lastY; //hold last move
const int8_t rtX[8];
int randoff; //used to change random seed in less than clock resolution
int travelDir; //keep track of travel direction
public:
AI(Board *b): board(b), //set board
rtX{-1,0,1,-1,1,-1,0,1}, //init rtX
travelDir(0) {}; //set initial travel direction to left
void runAI(); //run AI on board
#ifdef LOGMOVES
void runAI(std::vector<Move*> &v); //run AI on board and log move
#endif
void NormalAI(); //AI when no one-move wins exist
bool SpecialAI(char player); //AI when game can be won or saved in one move
bool justWon(); //did I just win?
};
#endif
/*
* Filename: Board.cpp
* Author: Aaron Schraner
* Date: April 12, 2015
* Purpose: Board member functions for connect 4 program
* CST 136
*/
#include <iostream>
#include "Board.h"
using std::cout;
using std::endl;
using std::setw;
using std::left;
void Board::print()
{
//print number heading 1-width
for(int x=0;x<width;x++)
cout << "| " << setw(2) << left << x+1;
cout << "|" << endl;
//print divider line
for(int x=0;x<width;x++)
cout << "----";
cout << "-\n";
//print each row
for(int y=0;y<height;y++)
{
cout << "| ";
for(int x=0;x<width;x++)
cout << board[y][x] << " | ";
cout << "\n";
for(int x=0;x<width;x++)
cout << "----";
cout << "-\n";
}
}
void Board::clear()
{
for(int y=0;y<height;y++)
for(int x=0;x<width;x++)
board[y][x]=' ';
//reset entire board to spaces
}
Board::Board(int r, int c):height(r),width(c)
{
//allocate board
board = new char* [height];
for(int i = 0; i < height; i++)
board[i]=new char [width];
clear();
p1='X';
p2='O';
//access board with board[Y][X] for coordinate (X,Y)
}
Board::~Board()
{
//de-allocate board
for(int i=0; i<height; i++)
delete[] board[i];
delete board;
}
bool Board::place(int x, int y, char pc)
{
//attempt to place a tile at given coordinates
if(board[y][x]==' ')
{
board[y][x]=pc;
return true;
}
return false;
}
//returns the character relative to a point [x,y] in direction [op]
//that is [d] steps away (d=0 returns board[y][x])
char Board::relTo(int x, int y, int op, int d)
{
// | op directions
//\|/
//0 1 2
//3 X 4
//5 6 7
if(x<0||y<0||x>=width||y>=height)
return 0;
//if it's out of range, do nothing and fail
if(d<0)
{
//if distance is negative, make it positive and flip direction
op=(op+4)%4;
d=-d;
}
if(op>3)
{
//if op>3, use mathemagics to make it between 0 and 3
op=op%4;
d=-d;
}
if(d==0)
return board[y][x]; //if distance is 0
switch(op)
{
case 0: //up and left
if( y-d >= 0 && y-d < height && x-d >= 0 && x-d < width )
return board[y-d][x-d];
return 0;
case 1: //up and center
if(y-d>=0&&y-d<height)
return board[y-d][x];
return 0;
case 2: //up and right
if( y-d >= 0 && y-d < height && x+d >= 0 && x+d < width )
return board[y-d][x+d];
return 0;
case 3: //center and left
if(x-d>=0&&x-d<width)
return board[y][x-d];
return 0;
}
}
bool Board::won(int x, int y, char pc)
{
int depth=0;
int inDir[4]={0}; //how many pieces in a row:
//0: diagonally TL->BR
//1: vertically
//2: diagonally TR->BL
//3: horizontally
//first iterate through all 8 adjacent tiles
for(int i=0; i<8; i++)
//add the number of adjacent pieces in some direction
//to the proper inDir direction
for(int d=1;(d<WIN)&&(relTo(x,y,i,d)==pc);d++)
inDir[i%4]++;
//if some direction has more or greater than WIN-1 (usually 3) tiles, we have won
for(int i=0;i<4;i++)
if(inDir[i]>=WIN-1)
return true;
return false;
//otherwise, we have not.
}
//drop a piece in some column
int Board::drop(int col, char pc)
{
//if the column is full, fail.
if(board[0][col]!=' ')
return -1;
int i;
for(i=0;i<height&&board[i][col]==' ';i++);
board[--i][col]=pc; //otherwise put (pc) in the lowest empty spot
return i;
// ---IMPORTANT---
//returns the Y coordinate of where the tile was placed
}
int Board::getHeight() const
{
return height;
}
int Board::getWidth() const
{
return width;
}
//is the board full?
bool Board::full()
{
for(int x=0; x<width; x++)
for(int y=0;y<height;y++)
if(board[y][x]==' ')
return false;
//if any blank spaces are found, it can't be full
return true; //if none are found, it is full.
}
/*
* Filename: Board.h
* Author: Aaron Schraner
* Date: April 12, 2015
* Purpose: Board class for connect 4 program
* CST 136
*/
#ifndef BOARD_H
#define BOARD_H
#define WIN 4 //want to play connect 5?
#include <iostream>
#include <iomanip>
class Board
{
public:
Board(int r, int c); //constructor (rows,columns)
~Board(); //destructor
void print(); //display board
void clear(); //erase board
bool place(int x, int y, char pc); //place a tile
char p1; //player 1 (Human / X)
char p2; //player 2 (Computer / O)
bool won(int x, int y, char pc); //did this move win?
int drop(int col, char pc); //drop a piece at specified column
int getWidth() const; //return width
int getHeight() const; //return height
bool full(); //is the board full?
friend class AI; //so AI can access board and relTo
private:
char** board;
int height;
int width;
char relTo(int x, int y, int op, int d);
};
#endif
CC=g++
CFLAGS=-std=c++11 -DLOGMOVES
default:Connect4
Connect4:connectfour.cpp Board.h Board.cpp AI.h AI.cpp
$(CC) $(CFLAGS) -o Connect4 connectfour.cpp Board.cpp AI.cpp
run:Connect4
./Connect4
clean:
rm Connect4
/*
* Filename: Move.h (optional for connect4)
* Author: Aaron Schraner
* Date: April 12, 2015
* Purpose: Move class for connect 4 program move logging (useful for contests)
* CST 136
*/
#ifndef MOVE_H
#define MOVE_H
#include <iostream>
using std::cout;
using std::endl;
//Move class
//for keeping track of player and computer moves
class Move
{
private:
int x,y;
char piece;
public:
Move(int X, int Y, char P): x(X), y(Y), piece(P) {}
//construct move
//display move
void print()
{
cout << "[" << piece << "] (" << x << ", " << y << ")" << endl;
}
};
#endif
/*
Base class for environments of RL.
Following features:
Discrete and fix-size action space.
Used as:
Experiment environment;
Environment in Agents' mind
*/
#include <cstdlib>
#include <string>
class Connect4: public BaseEnv {
public:
Connect4();
~Connect4();
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 "connect4_comm.h"
#include <cstring>
#include <mutex>
// #include <glog/logging.h>
#define x first
#define y second
using namespace std;
using namespace Connect4Comm;
Connect4HashValuePair g_hash_weight[BORDER_SIZE_HEIGHT][BORDER_SIZE_LENGTH];
Connect4CoordId g_log2_table[67];
uint64_t g_zobrist_board_hash_weight[4][CONNECT4BOARD_SIZE];
uint64_t g_zobrist_player_hash_weight[4];
namespace Connect4Function {
bool InBoard(const Connect4CoordId id) {
return 0 <= id && id < CONNECT4BOARD_SIZE;
}
bool InBoard(const Connect4CoordId x, const Connect4CoordId y) {
return 0 <= x && x < BORDER_SIZE_HEIGHT
&& 0 <= y && y < BORDER_SIZE_LENGTH;
}
bool IsUnset(const Connect4CoordId id) {
return COORD_UNSET == id;
}
bool IsUnset(const Connect4CoordId x, const Connect4CoordId y) {
return COORD_UNSET == CoordToId(x, y);
}
bool IsResign(const Connect4CoordId id) {
return COORD_RESIGN == id;
}
bool IsResign(const Connect4CoordId x, const Connect4CoordId y) {
return COORD_RESIGN == CoordToId(x, y);
}
void IdToCoord(const Connect4CoordId id, Connect4CoordId &x, Connect4CoordId &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;
}
}
Connect4CoordId CoordToId(const Connect4CoordId x, const Connect4CoordId 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, Connect4CoordId &x, Connect4CoordId &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 Connect4CoordId x, const Connect4CoordId 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 Connect4CoordId id) {
Connect4CoordId x, y;
IdToCoord(id, x, y);
return CoordToStr(x, y);
}
Connect4CoordId StrToId(const std::string &str) {
Connect4CoordId x, y;
StrToCoord(str, x, y);
return CoordToId(x, y);
}
once_flag CreateGlobalVariables_once;
void CreateGlobalVariables() {
call_once(
CreateGlobalVariables_once,
[]() {
CreateHashWeights();
CreateQuickLog2Table();
CreateZobristHash();
}
);
}
void CreateHashWeights() {
g_hash_weight[0][0] = Connect4HashValuePair(1, 1);
for (Connect4CoordId i = 1; i < CONNECT4BOARD_SIZE; ++i) {
g_hash_weight[i / BORDER_SIZE_LENGTH][i % BORDER_SIZE_LENGTH] =
Connect4HashValuePair(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 (Connect4CoordId 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 < CONNECT4BOARD_SIZE; ++j) {
g_zobrist_board_hash_weight[i][j] = (uint64_t) rand_r(&seed) << 32 | rand_r(&seed);
}
}
}
} // namespace Connect4Function
#undef y
#undef x
#include <inttypes.h>
#include <string>
#include <vector>
// Return code of functions should be "int"
typedef uint8_t Connect4StoneColor; // Stone color
typedef int16_t Connect4CoordId; // Stone IDs or coordinates
typedef int16_t Connect4Size; // Counts of visit times, used blocks, ..
namespace Connect4Comm {
const Connect4CoordId BORDER_SIZE_LENGTH = 12; // sxk_modify
const Connect4CoordId BORDER_SIZE_HEIGHT = 7;
const Connect4CoordId CONNECT4BOARD_SIZE = BORDER_SIZE_LENGTH * BORDER_SIZE_HEIGHT;
const Connect4CoordId COORD_UNSET = -2;
//const Connect4CoordId COORD_PASS = -1;
const Connect4CoordId COORD_RESIGN = -3;
const Connect4StoneColor EMPTY = 0;
const Connect4StoneColor BLACK = 1;
const Connect4StoneColor WHITE = 2;
const Connect4StoneColor WALL = 3;
const Connect4StoneColor COLOR_UNKNOWN = -1;
//const char *const COLOR_STRING[] = { "Empty", "Black", "White"};
const char *const COLOR_STRING[] = { "Empty", "Black", "White", "Wall" };
} // namespace Connect4Comm
namespace Connect4Feature {
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 Connect4Feature
namespace Connect4Function {
extern bool InBoard(const Connect4CoordId id);
extern bool InBoard(const Connect4CoordId x, const Connect4CoordId y);
extern bool IsUnset(const Connect4CoordId id);
extern bool IsUnset(const Connect4CoordId x, const Connect4CoordId y);
extern bool IsResign(const Connect4CoordId id);
extern bool IsResign(const Connect4CoordId x, const Connect4CoordId y);
extern void IdToCoord(const Connect4CoordId id, Connect4CoordId &x, Connect4CoordId &y);
extern Connect4CoordId CoordToId(const Connect4CoordId x, const Connect4CoordId y);
extern void StrToCoord(const std::string &str, Connect4CoordId &x, Connect4CoordId &y);
extern std::string CoordToStr(const Connect4CoordId x, const Connect4CoordId y);
extern std::string IdToStr(const Connect4CoordId id);
extern Connect4CoordId StrToId(const std::string &str);
extern void CreateGlobalVariables();
extern void CreateHashWeights();
extern void CreateQuickLog2Table();
extern void CreateZobristHash();
} // namespace Connect4Function
//typedef std::pair<Connect4CoordId, Connect4CoordId> Connect4Position;
typedef std::pair<uint64_t, uint64_t> Connect4HashValuePair;
extern Connect4HashValuePair g_hash_weight[Connect4Comm::BORDER_SIZE_HEIGHT][Connect4Comm::BORDER_SIZE_LENGTH];
const Connect4HashValuePair g_hash_unit(3, 7);
extern uint64_t g_zobrist_board_hash_weight[4][Connect4Comm::CONNECT4BOARD_SIZE];
extern uint64_t g_zobrist_player_hash_weight[4];
//state:统计棋局状态,0未分胜负,1黑胜,2白胜,3平局
//extern int state;
//extern int action_count;
extern Connect4CoordId g_log2_table[67];
#define FOR_EACHCOORD(id) for (Connect4CoordId id = 0; id < Connect4Comm::CONNECT4BOARD_SIZE; ++id)
#include "connect4_env.h"
#include <algorithm>
#include <cmath>
using namespace std;
using namespace Connect4Comm;
using namespace Connect4Function;
using namespace Connect4Feature;
Connect4Env::Connect4Env() {
CreateGlobalVariables();
FOR_EACHCOORD(i) {
stones_[i].self_id = i;
}
feature_history_list_.clear();
memset(board_state_, 0, sizeof(board_state_));
memset(legal_move_map_, 1, sizeof(legal_move_map_));
memset(move_count_, 0, sizeof(move_count_));
current_player_ = BLACK;
last_position_ = COORD_UNSET;
is_resign_ = false;
state_ = 0;
action_count_ = 0;
board_hash_states_.clear();
zobrist_hash_value_ = 0;
}
//Connect4Env::Connect4Env(const Connect4Env &ge) : Connect4Env() {
// CopyFrom(ge);
// }
Connect4Env::~Connect4Env() {
}
// void Connect4Env::CopyFrom(const Connect4Env &src) {
// *this = src;
// }
void Connect4Env::GetSensibleMove() {
// Add new feature plane
string plane;
// black
{
plane = "";
FOR_EACHCOORD(id) {
if (board_state_[id] == BLACK) {
plane += "1";
} else {
plane += "0";
}
}
feature_history_list_.push_back(plane);
}
// white
{
plane = "";
FOR_EACHCOORD(id) {
if (board_state_[id] == WHITE) {
plane += "1";
} else {
plane += "0";
}
}
feature_history_list_.push_back(plane);
}
memset(legal_move_map_, 0, sizeof(legal_move_map_));
Find_Legal_Moves();
}
int Connect4State::Move(int action) {
Find_Legal_Moves();
if (!IsLegal(action)) {
return -1;
}
if(IsResign(action)){
is_resign_ = true;
return 0;
}
last_position_ = to;
{
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];
}
++move_count_[action];
board_state_[action] = Self();
++action_count_;
HandOff();
GetSensibleMove();
return 0;
}
void Connect4Env::Find_Legal_Moves() const{
Connect4CoordId i, j, id;
for(id = 0; id < Connect4Comm::CONNECT4BOARD_SIZE; id++){
legal_move_map_[id] = false;
}
for(j = 0; j < Connect4Comm::BORDER_SIZE_LENGTH; j++){
if(board_state_[Connect4Function::CoordToId(0, j)] != Connect4Comm::EMPTY){
continue;
}
for(i = 0;(i < Connect4Comm::BORDER_SIZE_HEIGHT && board_state_[Connect4Function::CoordToId(i, j)] == Connect4Comm::EMPTY); i++);
legal_move_map_[Connect4Function::CoordToId(--i, j)] = true;
}
}
int Connect4Env::dir_x() const{
static int angle = 135; //角度,从零开始,每次增加45度,135度的下一次回到零
if(angle < 135){
angle+=45;
}else{
angle = 0;
}
switch(angle){
case 0:
return 0;
break;
case 45:case 90:case 135:
return 1;
break;
default:
printf("error angle\n");
return 0;
break;
}
}
int Connect4Env::dir_y() const{
static int angle = 135; //角度,从零开始,每次增加45度,135度的下一次回到零
if(angle < 135){
angle+=45;
}else{
angle = 0;
}
switch(angle){
case 0: case 45:
return 1;
break;
case 90:
return 0;
break;
case 135:
return -1;
break;
default:
printf("error angle.\n");
return 0;
break;
}
}
int Connect4Env::Connect4Result(Connect4CoordId x, Connect4CoordId y) const{
Connect4StoneColor type; //如果最后落子为黑子,其为1,白子为2
int left_1[4] = {0,0,0,0}; //记录x轴反方相同棋子的个数
int right_1[4] = {0,0,0,0}; //记录x轴正方相同棋子的个数
int i, j;
int a, b;
if(action_count_ >= Connect4Comm::CONNECT4BOARD_SIZE){
state_ = 3;
}
if(is_resign_){
if(current_player_ == Connect4Comm::BLACK){
state_ = 2;
}else if(current_player_ == Connect4Comm::WHITE){
state_ = 1;
}
}
if(state_ != 0){ //如果游戏结束
return state_;
}
type = board_state_[Connect4Function::CoordToId(x, y)];
//type = current_player_;
for(i = 0; i < 4; i++){
j = 1; a = dir_x(); b = dir_y();
while((x + j * a < Connect4Comm::BORDER_SIZE_HEIGHT) && (y + j * b < Connect4Comm::BORDER_SIZE_LENGTH) && (x + j * a >= 0) && (y + j * b >= 0) && (board[Connect4Function::CoordToId(x + j * a, y + j * b)] == type)){ //当x轴正向的子与之相同时(前面的条件是为了保证不越界)
j++;
right_1[i]++;
}
j = 1;
while((x - j * a < Connect4Comm::BORDER_SIZE_HEIGHT) && (y - j * b <= Connect4Comm::BORDER_SIZE_LENGTH) && (x - j * a >= 0) && (y - j * b >= 0) && (board[Connect4Function::CoordToId(x - j * a, y - j * b)] == type)) //当x轴反向的子与之相同时
{
j++;
left_1[i]++;
}
}
for(i = 0; i < 4; i++){
if(type == Connect4Comm::WHITE){ //如果刚才为白棋落子,那么不进行禁手判断
if((left_1[i] + right_1[i]) >= 3){ //白棋只判断是否胜利
state_ = 2;
return state_;
}else{ //继续下一根轴检测
continue;
}
}else{ //如果是黑棋,需要考虑禁手状态
if((left_1[i] + right_1[i]) >= 3){ //胜利
state_ = 1; //将状态改为对应的棋子胜利
return state_; //结束函数
}
}
}
return state_;
}
int Connect4Env::CalcResult() const{
//Connect4StoneColor type;
Connect4CoordId x, y;
int result = 0;
FOR_EACHCOORD(i) {
if(board_state_[i] == Connect4Comm::EMPTY){
continue;
}else{
Connect4Function::IdToCoord(i, x, y);
result = Connect4Result(x, y);
if(result != 0){
return result;
}
}
}
return result;
}
Connect4StoneColor Connect4Env::GetWinner() const {
int result = CalcResult();
if (result == 3){
return Connect4Comm::EMPTY;
}else if (result == 1){
return Connect4Comm::BLACK;
}else if(result == 2){
return Connect4Comm::WHITE;
}
}
void Connect4Env::GetResult(float& res) {
if (GetWinner() == Connect4Comm::EMPTY){
res = 0.0f;
}else{
res = GetWinner() == CurrentPlayer() ? -1.0f : 1.0f;
}
}
vector<bool> Connect4Env::GetFeature(BaseFeature& feature) const { // HWC
feature.clear();
feature.resize(CONNECT4BOARD_SIZE * (SIZE_HISTORYEACHSIDE + 1));
// because push black into history first,
// if next player is black,
// we should get planes swap of each two planes
int reverse_plane = int(Self() == BLACK);
for (Connect4Size 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 < CONNECT4BOARD_SIZE; ++j, k += (SIZE_HISTORYEACHSIDE + 1)) {
feature[k] = feature_str[j] - '0';
}
}
if (Self() == BLACK) {
for (int j = 0, k = SIZE_HISTORYEACHSIDE; j < CONNECT4BOARD_SIZE; ++j, k += (SIZE_HISTORYEACHSIDE + 1)) {
feature[k] = 1;
}
}
return feature;
}
int Connect4Env::GetActionNum() {//include RESIGN
return Connect4Comm::CONNECT4BOARD_SIZE + 1;
}
void Connect4Env::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 Connect4Env::GetLegalAction(vector<int>& action) {
action.clear();
FOR_EACHCOORD(id) {
if (legal_move_map_[id]) {
action.push_back(id);
}
}
}
/*void Connect4Env::TransformCoord(Connect4CoordId &x, Connect4CoordId &y, int mode, bool reverse = false)
{
if (reverse) {
if (mode & 4) std::swap(x, y);
if (mode & 2) y = Connect4Comm::BORDER_SIZE_LENGTH - y - 1;
if (mode & 1) x = Connect4Comm::BORDER_SIZE_HEIGHT - x - 1;
} else {
if (mode & 1) x = Connect4Comm::BORDER_SIZE_HEIGHT - x - 1;
if (mode & 2) y = Connect4Comm::BORDER_SIZE_LENGTH - y - 1;
if (mode & 4) std::swap(x, y);
}
}*/
// void Connect4Env::TransformFeatures(BaseFeature& feature, int transform_mode) {
// BaseFeature ret(feature.size());
// int depth = features.size() / Connect4Comm::CONNECT4BOARD_SIZE;
// for (int i = 0; i < Connect4Comm::CONNECT4BOARD_SIZE; ++i) {
// Connect4CoordId x, y;
// Connect4Function::IdToCoord(i, x, y);
// TransformCoord(x, y, mode);
// int j = Connect4Function::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 "connect4_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
struct Connect4Stone {
Connect4CoordId self_id; // Id of this stone
Connect4CoordId next_id; // Use like link-list
Connect4CoordId parent_id; // Use like union-find-set (rooted by tail)
// inline void SetSelfId(Connect4CoordId id) { self_id = id; }
inline void Reset(Connect4CoordId id = Connect4Comm::COORD_UNSET) {
next_id = parent_id = self_id;
}
} ;
class Connect4Env: public BaseEnv {
public:
Connect4Env();
// Connect4Env(const Connect4Env &ge);
~Connect4Env();
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 Connect4Function::IsResign(action) || legal_move_map_[action]; }
std::string action2str(int action) override { return Connect4Function::IdToStr(const Connect4CoordId action); }
int CurrentPlayer() override { return current_player_; }
// void TransformFeatures(BaseFeature& feature, int transform_mode);
// void TransformPolicy(int transform_mode); // this is reverse mode of TransformFeature
protected:
// void Init();
// void CopyFrom(const Connect4Env &src);
int Connect4Result(Connect4CoordId x, Connect4CoordId y) const;
int CalcResult() const;
Connect4StoneColor GetWinner() const;
inline void HandOff() { current_player_ = Opponent(); }
inline Connect4StoneColor Opponent(const Connect4StoneColor color = Connect4Comm::COLOR_UNKNOWN) const {
return Connect4Comm::BLACK + Connect4Comm::WHITE
- (Connect4Comm::COLOR_UNKNOWN != color ? color : current_player_);
}
inline Connect4StoneColor Self() const { return current_player_; }
inline Connect4CoordId GetLastMove() const { return last_position_; }
// inline Connect4StoneColor CurrentPlayer() const { return current_player_; }
//void Connect4Env::TransformCoord(Connect4CoordId &x, Connect4CoordId &y, int mode, bool reverse = false)
void GetSensibleMove();
void Find_Legal_Moves() const{
int dir_x() const;
int dir_y() const;
protected:
// board utils
Connect4Stone stones_[Connect4Comm::CONNECT4BOARD_SIZE];
Connect4StoneColor board_state_[Connect4Comm::CONNECT4BOARD_SIZE];
Connect4StoneColor current_player_;
Connect4CoordId last_position_;
bool is_resign_;
int state_;
int action_count_;
// hash board state
std::unordered_set<uint64_t> board_hash_states_;
uint64_t zobrist_hash_value_;
// features
bool legal_move_map_[Connect4Comm::CONNECT4BOARD_SIZE];
Connect4Size move_count_[Connect4Comm::CONNECT4BOARD_SIZE];
std::vector<std::string> feature_history_list_;
};
/*
* Filename: connectfour.cpp
* Author: Aaron Schraner
* Date: April 12, 2015
* Purpose: connect 4 program implementation
* CST 136
*/
#include <iostream>
#include <iomanip>
#include <stdlib.h>//
#include <time.h> // for rand()
#include "Board.h"
#include "AI.h"
//define LOGMOVES if you want to know who moved where at the end
#ifdef LOGMOVES
#include "Move.h"
#include <vector>
#endif
using namespace std;
void promptForSize(int& height, int& width)
{
height=0;
width=0;
//initialize so we don't exit the loop due to garbage
do
{
cout << "Enter the board height (must be greater than 3)\n> ";
cin >> height;
}
while(height<4);
do
{
cout << "Enter the board width (must be greater than 3)\n> ";
cin >> width;
}
while(width<4);
//prompt for and store width and height
}
int main()
{
#ifdef LOGMOVES
cout << "Moves will be logged.\n";
#endif
//size of board
int height,width;
//get size
promptForSize(height,width);
//create board
Board board(height,width);
cout << "Board created\n";
int colToDrop=0; //column user wants to drop into
bool cont=true; //continue
AI myAI(&board); //create an AI
//if we are logging moves, make a vector to store them
#ifdef LOGMOVES
vector<Move*> moves;
#endif
do
{
//user's turn
board.print(); //print the board
do {
cout << "What column would you like to drop your piece into?\n> ";
cin >> colToDrop;
}
while(colToDrop < 1 || colToDrop > width);
//prompt for and store desired column
//we like to start at 0, not 1.
colToDrop--;
int res=board.drop(colToDrop, board.p1); //drop tile and store Y coordinate
//log move if desired
#ifdef LOGMOVES
moves.push_back(new Move(colToDrop, res, board.p1));
#endif
//if that move won the game
if(board.won(colToDrop, res, board.p1))
{
//say so and break
cout << board.p1 << " wins!\n";
cont=false;
break;
}
if(board.full()) //if the board is full, break.
{
cout << "Board full! Nobody wins.\n";
cont=false;
break;
}
//if we're also logging the AI's moves
#ifdef LOGMOVES
myAI.runAI(moves); //run the AI and log its moves
#else //otherwise
myAI.runAI(); //just run the AI
#endif
//if the AI just won
if(myAI.justWon())
{
//say so and break
cout << board.p2 << " wins!\n";
cont=false;
break;
}
//if the board is full, break.
if(board.full())
{
cout << "Board full! Nobody wins.\n";
cont=false;
break;
}
} while(cont);
//when the game is over, print the final board.
board.print();
//and if desired, show all the game moves
#ifdef LOGMOVES
cout << "Played with size " << width << "x" << height << endl;
for(int i=0; i<moves.size(); i++)
{
moves[i]->print();
delete moves[i];
}
#endif
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleIdentifier</key>
<string>com.apple.xcode.dsym.GoBang_GBK</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>dSYM</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
/*
Base class for environments of RL.
Following features:
Discrete and fix-size action space.
Used as:
Experiment environment;
Environment in Agents' mind
*/
#include <cstdlib>
#include <string>
class GoBang: public BaseEnv {
public:
GoBang();
~GoBang();
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 "gobang_comm.h"
#include <cstring>
#include <mutex>
// #include <glog/logging.h>
#define x first
#define y second
using namespace std;
using namespace GoBangComm;
GoBangHashValuePair g_hash_weight[BORDER_SIZE][BORDER_SIZE];
GoBangCoordId g_log2_table[67];
uint64_t g_zobrist_board_hash_weight[4][GOBANGBOARD_SIZE];
uint64_t g_zobrist_player_hash_weight[4];
namespace GoBangFunction {
bool InBoard(const GoBangCoordId id) {
return 0 <= id && id < GOBANGBOARD_SIZE;
}
bool InBoard(const GoBangCoordId x, const GoBangCoordId y) {
return 0 <= x && x < BORDER_SIZE
&& 0 <= y && y < BORDER_SIZE;
}
bool IsUnset(const GoBangCoordId id) {
return COORD_UNSET == id;
}
bool IsUnset(const GoBangCoordId x, const GoBangCoordId y) {
return COORD_UNSET == CoordToId(x, y);
}
bool IsResign(const GoBangCoordId id) {
return COORD_RESIGN == id;
}
bool IsResign(const GoBangCoordId x, const GoBangCoordId y) {
return COORD_RESIGN == CoordToId(x, y);
}
void IdToCoord(const GoBangCoordId id, GoBangCoordId &x, GoBangCoordId &y) {
if (COORD_RESIGN == id) {
x = y = COORD_RESIGN;
} else if (!InBoard(id)) {
x = y = COORD_UNSET;
} else {
x = id / BORDER_SIZE;
y = id % BORDER_SIZE;
}
}
GoBangCoordId CoordToId(const GoBangCoordId x, const GoBangCoordId y) {
if (COORD_RESIGN == x && COORD_RESIGN == y) {
return COORD_RESIGN;
}
if (!InBoard(x, y)) {
return COORD_UNSET;
}
return x * BORDER_SIZE + y;
}
void StrToCoord(const string &str, GoBangCoordId &x, GoBangCoordId &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 GoBangCoordId x, const GoBangCoordId 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 GoBangCoordId id) {
GoBangCoordId x, y;
IdToCoord(id, x, y);
return CoordToStr(x, y);
}
GobangCoordId StrToId(const std::string &str) {
GobangCoordId x, y;
StrToCoord(str, x, y);
return CoordToId(x, y);
}
once_flag CreateGlobalVariables_once;
void CreateGlobalVariables() {
call_once(
CreateGlobalVariables_once,
[]() {
CreateHashWeights();
CreateQuickLog2Table();
CreateZobristHash();
}
);
}
void CreateHashWeights() {
g_hash_weight[0][0] = GoBangHashValuePair(1, 1);
for (GoBangCoordId i = 1; i < GOBANGBOARD_SIZE; ++i) {
g_hash_weight[i / BORDER_SIZE][i % BORDER_SIZE] =
GoBangHashValuePair(g_hash_weight[(i - 1) / BORDER_SIZE][(i - 1) % BORDER_SIZE].x * g_hash_unit.x,
g_hash_weight[(i - 1) / BORDER_SIZE][(i - 1) % BORDER_SIZE].y * g_hash_unit.y);
}
}
void CreateQuickLog2Table() {
memset(g_log2_table, -1, sizeof(g_log2_table));
int tmp = 1;
for (GoBangCoordId 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 < GOBANGBOARD_SIZE; ++j) {
g_zobrist_board_hash_weight[i][j] = (uint64_t) rand_r(&seed) << 32 | rand_r(&seed);
}
}
}
} // namespace GoBangFunction
#undef y
#undef x
#pragma once
#include <inttypes.h>
#include <string>
#include <vector>
// Return code of functions should be "int"
typedef uint8_t GoBangStoneColor; // Stone color
typedef int16_t GoBangCoordId; // Stone IDs or coordinates
typedef int16_t GoBangSize; // Counts of visit times, used blocks, .. or other count
namespace GoBangComm {
const GoBangCoordId BORDER_SIZE = 15; // sxk_modify
const GoBangCoordId GOBANGBOARD_SIZE = BORDER_SIZE * BORDER_SIZE;
const GoBangCoordId COORD_UNSET = -2;
//const GoBangCoordId COORD_PASS = -1;
const GoBangCoordId COORD_RESIGN = -3;
const GoBangStoneColor EMPTY = 0;
const GoBangStoneColor BLACK = 1;
const GoBangStoneColor WHITE = 2;
const GoBangStoneColor WALL = 3;
const GoBangStoneColor COLOR_UNKNOWN = -1;
const char *const COLOR_STRING[] = { "Empty", "Black", "White", "Wall" };
} // namespace GoBangComm
namespace GoBangFeature {
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 GoBangFeature
namespace GoBangFunction {
extern bool InBoard(const GoBangCoordId id);
extern bool InBoard(const GoBangCoordId x, const GoBangCoordId y);
extern bool IsUnset(const GoBangCoordId id);
extern bool IsUnset(const GoBangCoordId x, const GoBangCoordId y);
extern bool IsResign(const GoBangCoordId id);
extern bool IsResign(const GoBangCoordId x, const GoBangCoordId y);
extern void IdToCoord(const GoBangCoordId id, GoBangCoordId &x, GoBangCoordId &y);
extern GoBangCoordId CoordToId(const GoBangCoordId x, const GoBangCoordId y);
extern void StrToCoord(const std::string &str, GoBangCoordId &x, GoBangCoordId &y);
extern std::string CoordToStr(const GoBangCoordId x, const GoBangCoordId y);
extern std::string IdToStr(const GoBangCoordId id);
extern GoBangCoordId StrToId(const std::string &str);
extern void CreateGlobalVariables();
extern void CreateHashWeights();
extern void CreateQuickLog2Table();
extern void CreateZobristHash();
} // namespace GoBangFunction
//typedef std::pair<GoBangCoordId, GoBangCoordId> GoBangPosition;
typedef std::pair<uint64_t, uint64_t> GoBangHashValuePair;
extern GoBangHashValuePair g_hash_weight[GoBangComm::BORDER_SIZE][GoBangComm::BORDER_SIZE];
const GoBangHashValuePair g_hash_unit(3, 7);
extern uint64_t g_zobrist_board_hash_weight[4][GoBangComm::GOBANGBOARD_SIZE];
extern uint64_t g_zobrist_player_hash_weight[4];
//action_count:统计已落子数,state:统计棋局状态,0未分胜负,1黑胜,2白胜,3平局
//extern int action_count = 0;
//extern int state = 0;
extern GoBangCoordId g_log2_table[67];
#define FOR_EACHCOORD(id) for (GoBangCoordId id = 0; id < GoBangComm::GOBANGBOARD_SIZE; ++id)
#include "gobang_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 GoBangStone {
GoBangCoordId self_id; // Id of this stone
GoBangCoordId next_id; // Use like link-list
GoBangCoordId parent_id; // Use like union-find-set (rooted by tail)
// inline void SetSelfId(GoBangCoordId id) { self_id = id; }
inline void Reset(GoBangCoordId id = GoBangComm::COORD_UNSET) {
next_id = parent_id = self_id;
}
} ;
class GoBangEnv: public BaseEnv {
public:
GoBangEnv();
// GoBangEnv(const GoBangEnv &ge);
~GoBangEnv();
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 GoBangFunction::IsResign(action) || legal_move_map_[action]; }
std::string action2str(int action) override { return GoBangFunction::IdToStr(const GoBangCoordId action); }
int CurrentPlayer() override { return current_player_; }
protected:
// void Init();
// void CopyFrom(const GoBangEnv &src);
int GobangResult(GoBangCoordId x, GoBangCoordId y) const;
int CalcResult() const;
GoBangStoneColor GetWinner() const;
inline void HandOff() { current_player_ = Opponent(); }
inline GoBangStoneColor Opponent(const GoBangStoneColor color = GoBangComm::COLOR_UNKNOWN) const {
return GoBangComm::BLACK + GoBangComm::WHITE
- (GoBangComm::COLOR_UNKNOWN != color ? color : current_player_);
}
inline GoBangStoneColor Self() const { return current_player_; }
inline GoBangCoordId GetLastMove() const { return last_position_; }
// inline GoBangStoneColor CurrentPlayer() const { return current_player_; }
//void GoBangEnv::TransformCoord(GoBangCoordId &x, GoBangCoordId &y, int mode, bool reverse = false)
void GetSensibleMove();
int dir_x() const;
int dir_y() const;
bool live_four(int left[4], int right[4]) const;
bool live_three(int left[4], int right[4]) const;
bool Ban(GoBangCoordId x, GoBangCoordId y) const;
protected:
// board utils
GoBangStone stones_[GoBangComm::GOBANGBOARD_SIZE];
GoBangStoneColor board_state_[GoBangComm::GOBANGBOARD_SIZE];
GoBangStoneColor current_player_;
GoBangCoordId last_position_;
bool is_resign_;
int state_;
int action_count_;
// hash board state
std::unordered_set<uint64_t> board_hash_states_;
uint64_t zobrist_hash_value_;
// features
bool legal_move_map_[GoBangComm::GOBANGBOARD_SIZE];
GoBangSize move_count_[GoBangComm::GOBANGBOARD_SIZE];
std::vector<std::string> feature_history_list_;
};
/*
Base class for environments of RL.
Following features:
Discrete and fix-size action space.
Used as:
Experiment environment;
Environment in Agents' mind
*/
#include <cstdlib>
#include <string>
class Othello: public BaseEnv {
public:
Othello();
~Othello();
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 "othello_comm.h"
#include <cstring>
#include <mutex>
// #include <glog/logging.h>
#define x first
#define y second
using namespace std;
using namespace OthelloComm;
OthelloHashValuePair g_hash_weight[BORDER_SIZE][BORDER_SIZE];
OthelloCoordId g_log2_table[67];
uint64_t g_zobrist_board_hash_weight[4][OTHELLOBOARD_SIZE];
uint64_t g_zobrist_player_hash_weight[4];
namespace OthelloFunction {
bool InBoard(const OthelloCoordId id) {
return 0 <= id && id < OTHELLOBOARD_SIZE;
}
bool InBoard(const OthelloCoordId x, const OthelloCoordId y) {
return 0 <= x && x < BORDER_SIZE
&& 0 <= y && y < BORDER_SIZE;
}
bool IsPass(const OthelloCoordId id) {
return COORD_PASS == id;
}
bool IsPass(const OthelloCoordId x, const OthelloCoordId y) {
return COORD_PASS == CoordToId(x, y);
}
bool IsUnset(const OthelloCoordId id) {
return COORD_UNSET == id;
}
bool IsUnset(const OthelloCoordId x, const OthelloCoordId y) {
return COORD_UNSET == CoordToId(x, y);
}
bool IsResign(const OthelloCoordId id) {
return COORD_RESIGN == id;
}
bool IsResign(const OthelloCoordId x, const OthelloCoordId y) {
return COORD_RESIGN == CoordToId(x, y);
}
void IdToCoord(const OthelloCoordId id, OthelloCoordId &x, OthelloCoordId &y) {
if (COORD_PASS == id) {
x = y = COORD_PASS;
} else if (COORD_RESIGN == id) {
x = y = COORD_RESIGN;
} else if (!InBoard(id)) {
x = y = COORD_UNSET;
} else {
x = id / BORDER_SIZE;
y = id % BORDER_SIZE;
}
}
OthelloCoordId CoordToId(const OthelloCoordId x, const OthelloCoordId y) {
if (COORD_PASS == x && COORD_PASS == y) {
return COORD_PASS;
}
if (COORD_RESIGN == x && COORD_RESIGN == y) {
return COORD_RESIGN;
}
if (!InBoard(x, y)) {
return COORD_UNSET;
}
return x * BORDER_SIZE + y;
}
void StrToCoord(const string &str, OthelloCoordId &x, OthelloCoordId &y) {
// CHECK_EQ(str.length(), 2) << "string[" << str << "] length not equal to 2";
x = str[0] - 'a';
y = str[1] - 'a';
if (str == "zz") {
x = y = COORD_PASS;
} else if (!InBoard(x, y)) {
x = y = COORD_UNSET;
}
}
string CoordToStr(const OthelloCoordId x, const OthelloCoordId 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 OthelloCoordId id) {
OthelloCoordId x, y;
IdToCoord(id, x, y);
return CoordToStr(x, y);
}
OthelloCoordId StrToId(const std::string &str) {
OthelloCoordId x, y;
StrToCoord(str, x, y);
return CoordToId(x, y);
}
once_flag CreateGlobalVariables_once;
void CreateGlobalVariables() {
call_once(
CreateGlobalVariables_once,
[]() {
CreateHashWeights();
CreateQuickLog2Table();
CreateZobristHash();
}
);
}
void CreateHashWeights() {
g_hash_weight[0][0] = OthelloHashValuePair(1, 1);
for (OthelloCoordId i = 1; i < OTHELLOBOARD_SIZE; ++i) {
g_hash_weight[i / BORDER_SIZE][i % BORDER_SIZE] =
OthelloHashValuePair(g_hash_weight[(i - 1) / BORDER_SIZE][(i - 1) % BORDER_SIZE].x * g_hash_unit.x,
g_hash_weight[(i - 1) / BORDER_SIZE][(i - 1) % BORDER_SIZE].y * g_hash_unit.y);
}
}
void CreateQuickLog2Table() {
memset(g_log2_table, -1, sizeof(g_log2_table));
int tmp = 1;
for (OthelloCoordId 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 < OTHELLOBOARD_SIZE; ++j) {
g_zobrist_board_hash_weight[i][j] = (uint64_t) rand_r(&seed) << 32 | rand_r(&seed);
}
}
}
} // namespace OthelloFunction
#undef y
#undef x
#pragma once
#include <inttypes.h>
#include <string>
#include <vector>
// Return code of functions should be "int"
typedef uint8_t OthelloStoneColor; // Stone color
typedef int16_t OthelloCoordId; // Stone IDs or coordinates
typedef int16_t OthelloSize; // Counts of visit times, used blocks, ..
namespace OthelloComm {
const OthelloCoordId BORDER_SIZE = 8; // sxk_modify
const OthelloCoordId OTHELLOBOARD_SIZE = BORDER_SIZE * BORDER_SIZE;
const OthelloCoordId COORD_UNSET = -2;
const OthelloCoordId COORD_PASS = -1;
const OthelloCoordId COORD_RESIGN = -3;
const OthelloStoneColor EMPTY = 0;
const OthelloStoneColor BLACK = 1;
const OthelloStoneColor WHITE = 2;
const OthelloStoneColor WALL = 3;
const OthelloStoneColor COLOR_UNKNOWN = -1;
//const char *const COLOR_STRING[] = { "Empty", "Black", "White"};
const char *const COLOR_STRING[] = { "Empty", "Black", "White", "Wall" };
const OthelloCoordId DirX[8] = {0, 1, 1, 1, 0, -1, -1, -1};
const OthelloCoordId DirY[8] = {-1, -1, 0, 1, 1, 1, 0, -1};
} // namespace OthelloComm
namespace OthelloFeature {
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 OthelloFeature
namespace OthelloFunction {
extern bool InBoard(const OthelloCoordId id);
extern bool InBoard(const OthelloCoordId x, const OthelloCoordId y);
extern bool IsPass(const OthelloCoordId id);
extern bool IsPass(const OthelloCoordId x, const OthelloCoordId y);
extern bool IsUnset(const OthelloCoordId id);
extern bool IsUnset(const OthelloCoordId x, const OthelloCoordId y);
extern bool IsResign(const OthelloCoordId id);
extern bool IsResign(const OthelloCoordId x, const OthelloCoordId y);
extern void IdToCoord(const OthelloCoordId id, OthelloCoordId &x, OthelloCoordId &y);
extern OthelloCoordId CoordToId(const OthelloCoordId x, const OthelloCoordId y);
extern void StrToCoord(const std::string &str, OthelloCoordId &x, OthelloCoordId &y);
extern std::string CoordToStr(const OthelloCoordId x, const OthelloCoordId y);
extern std::string IdToStr(const OthelloCoordId id);
extern OthelloCoordId StrToId(const std::string &str);
extern void CreateGlobalVariables();
extern void CreateHashWeights();
extern void CreateQuickLog2Table();
extern void CreateZobristHash();
} // namespace OthelloFunction
//typedef std::pair<OthelloCoordId, OthelloCoordId> OthelloPosition;
typedef std::pair<uint64_t, uint64_t> OthelloHashValuePair;
extern OthelloHashValuePair g_hash_weight[OthelloComm::BORDER_SIZE][OthelloComm::BORDER_SIZE];
const OthelloHashValuePair g_hash_unit(3, 7);
extern uint64_t g_zobrist_board_hash_weight[4][OthelloComm::OTHELLOBOARD_SIZE];
extern uint64_t g_zobrist_player_hash_weight[4];
//state:统计棋局状态,0未分胜负,1黑胜,2白胜,3平局
//extern int state;
//extern int B, W;
extern OthelloCoordId g_log2_table[67];
#define FOR_EACHCOORD(id) for (OthelloCoordId id = 0; id < OthelloComm::OTHELLOBOARD_SIZE; ++id)
#include "othello_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
struct OthelloStone {
OthelloCoordId self_id; // Id of this stone
OthelloCoordId next_id; // Use like link-list
OthelloCoordId parent_id; // Use like union-find-set (rooted by tail)
// inline void SetSelfId(OthelloCoordId id) { self_id = id; }
inline void Reset(OthelloCoordId id = OthelloComm::COORD_UNSET) {
next_id = parent_id = self_id;
}
} ;
class OthelloEnv: public BaseEnv {
public:
OthelloEnv();
// OthelloEnv(const OthelloEnv &ge);
~OthelloEnv();
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 OthelloFunction::IsResign(action) || OthelloFunction::IsPass(action) || legal_move_map_[action]; }
std::string action2str(int action) override { return OthelloFunction::IdToStr(const OthelloCoordId action); }
int CurrentPlayer() override { return current_player_; }
// void TransformFeatures(BaseFeature& feature, int transform_mode);
// void TransformPolicy(int transform_mode); // this is reverse mode of TransformFeature
protected:
// void Init();
// void CopyFrom(const OthelloEnv &src);
int CalcResult() const;
OthelloStoneColor GetWinner() const;
inline void HandOff() { current_player_ = Opponent(); }
inline OthelloStoneColor Opponent(const OthelloStoneColor color = OthelloComm::COLOR_UNKNOWN) const {
return OthelloComm::BLACK + OthelloComm::WHITE
- (OthelloComm::COLOR_UNKNOWN != color ? color : current_player_);
}
inline OthelloStoneColor Self() const { return current_player_; }
inline OthelloCoordId GetLastMove() const { return last_position_; }
// inline OthelloStoneColor CurrentPlayer() const { return current_player_; }
//void OthelloEnv::TransformCoord(OthelloCoordId &x, OthelloCoordId &y, int mode, bool reverse = false)
void GetSensibleMove();
bool Check_Cross(OthelloCoordId x, OthelloCoordId y, int update) const;
int Check_Straight_Army(OthelloCoordId x, OthelloCoordId y, int d, int update) const;
int Find_Legal_Moves(int color) const;
protected:
// board utils
OthelloStone stones_[OthelloComm::OTHELLOBOARD_SIZE];
OthelloStoneColor board_state_[OthelloComm::OTHELLOBOARD_SIZE];
OthelloStoneColor current_player_;
OthelloCoordId last_position_;
bool is_resign_;
int state_;
int B_;
int W_;
// hash board state
std::unordered_set<uint64_t> board_hash_states_;
uint64_t zobrist_hash_value_;
// features
bool legal_move_map_[OthelloComm::OTHELLOBOARD_SIZE];
OthelloSize move_count_[OthelloComm::OTHELLOBOARD_SIZE];
std::vector<std::string> feature_history_list_;
};
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleIdentifier</key>
<string>com.apple.xcode.dsym.othello10</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>dSYM</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleIdentifier</key>
<string>com.apple.xcode.dsym.othello8</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>dSYM</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
...现已初步实现四种棋类的扩展代码...
每个文件夹代表一种棋类:
Chess ... 国际象棋
Connect4 ... 平面四子棋
GoBang ... 五子棋
Othello ... 黑白棋
棋类文件夹中的文件夹(棋类英文小写字母开头)封装了基于师兄已完成工作的棋类子类代码...和我们的AlphaGoZero框架相对应
棋类文件夹里面的代码和可执行文件为原有棋类代码及其编译结果
\ 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