Commit 7518ec8d authored by Sam Moore's avatar Sam Moore

Improving C++ API

Changed the Piece and Board classes slightly so that I can make an efficient C++ agent.

I plan to make SilverFish as good as I possibly can.
Rather than writing 6 different mediocre sample agents like I did last year, I will just focus on this one.

Technically I can't enter my own competition, so SilverFish is still just a sample agent.

Anyway it still just defaults to random moves at the moment.
parent 8571bc0c
#Makefile for C++ agent
CXX = g++ -std=gnu++0x -Wall -Werror -pedantic -g
OBJ = qchess.o agent.o main.o
LIB =
LINKOBJ = $(OBJ)
RM = rm -f
BIN = agent++
$(BIN) : $(LINKOBJ)
$(CXX) -o $(BIN) $(LINKOBJ)
%.o : %.cpp
$(CXX) -c $<
no_main : $(OBJ)
main.o : main.cpp
$(CXX) -c main.cpp
clean :
$(RM) $(BIN) $(OBJ) $(LINKOBJ)
clean_full: #cleans up all backup files
$(RM) $(BIN) $(OBJ) $(LINKOBJ)
$(RM) *.*~
$(RM) *~
\ No newline at end of file
No preview for this file type
...@@ -9,24 +9,27 @@ ...@@ -9,24 +9,27 @@
using namespace std; using namespace std;
/** /**
* @constructor * @constructor
* @param new_x, new_y - Position of piece * @param new_x, new_y - Position of piece
* @param new_colour - Colour of piece * @param new_colour - Colour of piece
* @param type1, type2 - Types of piece * @param type1, type2 - Types of piece
* @param index - Index for initial type of piece * @param new_type_index - Index for initial type of piece
* @param new_piece_index - Index for piece in a vector
*/ */
Piece::Piece(int new_x, int new_y, const Piece::Colour & new_colour, const Piece::Type & type1, const Piece::Type & type2, int index) Piece::Piece(int new_x, int new_y, const Piece::Colour & new_colour, const Piece::Type & type1, const Piece::Type & type2,
: x(new_x), y(new_y), colour(new_colour), type_index(index), types(), current_type() int new_type_index, int new_piece_index)
: x(new_x), y(new_y), colour(new_colour), type_index(new_type_index), types(), current_type(), piece_index(new_piece_index)
{ {
types[0] = type1; types[1] = type2; types[0] = type1; types[1] = type2;
if (index < 0 || index >= 2) if (type_index < 0 || type_index >= 2)
{ {
current_type = Piece::UNKNOWN; current_type = Piece::UNKNOWN;
} }
else else
{ {
current_type = types[index]; current_type = types[type_index];
} }
} }
...@@ -34,7 +37,7 @@ Piece::Piece(int new_x, int new_y, const Piece::Colour & new_colour, const Piece ...@@ -34,7 +37,7 @@ Piece::Piece(int new_x, int new_y, const Piece::Colour & new_colour, const Piece
* @constructor * @constructor
* @param cpy - Piece to copy construct from * @param cpy - Piece to copy construct from
*/ */
Piece::Piece(const Piece & cpy) : x(cpy.x), y(cpy.y), colour(cpy.colour), type_index(cpy.type_index) Piece::Piece(const Piece & cpy) : x(cpy.x), y(cpy.y), colour(cpy.colour), type_index(cpy.type_index), piece_index(cpy.piece_index)
{ {
types[0] = cpy.types[0]; types[0] = cpy.types[0];
types[1] = cpy.types[1]; types[1] = cpy.types[1];
...@@ -44,7 +47,9 @@ Piece::Piece(const Piece & cpy) : x(cpy.x), y(cpy.y), colour(cpy.colour), type_i ...@@ -44,7 +47,9 @@ Piece::Piece(const Piece & cpy) : x(cpy.x), y(cpy.y), colour(cpy.colour), type_i
* @constructor * @constructor
* @param choose_types - Indicates whether Board should setup the 2nd types of pieces; default false * @param choose_types - Indicates whether Board should setup the 2nd types of pieces; default false
*/ */
Board::Board(bool choose_types) Board::Board(bool choose_types)
: white(), black(), white_unknown(), black_unknown(), white_nUnknown(0), black_nUnknown(0),
white_king(NULL), black_king(NULL), parent(NULL)
{ {
// initialise all the Squares // initialise all the Squares
...@@ -69,6 +74,14 @@ Board::Board(bool choose_types) ...@@ -69,6 +74,14 @@ Board::Board(bool choose_types)
freq[Piece::QUEEN] = 1; freq[Piece::QUEEN] = 1;
freq[Piece::PAWN] = 8; freq[Piece::PAWN] = 8;
if (!choose_types)
{
white_unknown = freq;
black_unknown = freq;
white_nUnknown = 15;
black_nUnknown = 15;
}
// for white and black... // for white and black...
for (int i = 0; i < 2; ++i) for (int i = 0; i < 2; ++i)
{ {
...@@ -80,26 +93,25 @@ Board::Board(bool choose_types) ...@@ -80,26 +93,25 @@ Board::Board(bool choose_types)
int y = (i == 0) ? 1 : BOARD_HEIGHT-2; int y = (i == 0) ? 1 : BOARD_HEIGHT-2;
for (int x = 0; x < BOARD_WIDTH; ++x) for (int x = 0; x < BOARD_WIDTH; ++x)
{ {
Piece * p = new Piece(x, y, colours[i], Piece::PAWN, Piece::UNKNOWN); Piece::AddPiece(v, x, y, colours[i], Piece::PAWN, Piece::UNKNOWN);
v.push_back(p);
} }
// add other pieces // add other pieces
y = (i == 0) ? 0 : BOARD_HEIGHT-1; y = (i == 0) ? 0 : BOARD_HEIGHT-1;
v.push_back(new Piece(0, y, colours[i], Piece::ROOK, Piece::UNKNOWN)); Piece::AddPiece(v, 0, y, colours[i], Piece::ROOK, Piece::UNKNOWN);
v.push_back(new Piece(BOARD_WIDTH-1, y, colours[i], Piece::ROOK, Piece::UNKNOWN)); Piece::AddPiece(v, BOARD_WIDTH-1, y, colours[i], Piece::ROOK, Piece::UNKNOWN);
v.push_back(new Piece(1, y, colours[i], Piece::KNIGHT, Piece::UNKNOWN)); Piece::AddPiece(v, 1, y, colours[i], Piece::KNIGHT, Piece::UNKNOWN);
v.push_back(new Piece(BOARD_WIDTH-2, y, colours[i], Piece::KNIGHT, Piece::UNKNOWN)); Piece::AddPiece(v, BOARD_WIDTH-2, y, colours[i], Piece::KNIGHT, Piece::UNKNOWN);
v.push_back(new Piece(2, y, colours[i], Piece::BISHOP, Piece::UNKNOWN)); Piece::AddPiece(v, 2, y, colours[i], Piece::BISHOP, Piece::UNKNOWN);
v.push_back(new Piece(BOARD_WIDTH-3, y, colours[i], Piece::BISHOP, Piece::UNKNOWN)); Piece::AddPiece(v, BOARD_WIDTH-3, y, colours[i], Piece::BISHOP, Piece::UNKNOWN);
v.push_back(new Piece(3, y, colours[i], Piece::QUEEN, Piece::UNKNOWN)); Piece::AddPiece(v, 3, y, colours[i], Piece::QUEEN, Piece::UNKNOWN);
Piece * k = new Piece(4, y, colours[i], Piece::KING, Piece::KING, 1); Piece * k = Piece::AddPiece(v, 4, y, colours[i], Piece::KING, Piece::KING, 1);
if (i == 0) if (i == 0)
white_king = k; white_king = k;
else else
black_king = k; black_king = k;
v.push_back(k);
// add to board and choose second types if required // add to board and choose second types if required
map<Piece::Type, int> f(freq); map<Piece::Type, int> f(freq);
...@@ -133,10 +145,12 @@ Board::Board(bool choose_types) ...@@ -133,10 +145,12 @@ Board::Board(bool choose_types)
/** /**
* @constructor * @constructor
* @param cpy - Board to copy construct from; each Piece in the copy will be *copied* * @param cpy - Board to clone
* The Piece's in the copied Board may be altered without affecting the original
*/ */
Board::Board(const Board & cpy) Board::Board(Board & cpy)
: white(cpy.white), black(cpy.black), white_unknown(cpy.white_unknown), black_unknown(cpy.black_unknown),
white_nUnknown(cpy.white_nUnknown), black_nUnknown(cpy.black_nUnknown),
white_king(cpy.white_king), black_king(cpy.black_king), parent(&cpy)
{ {
for (int x = 0; x < BOARD_WIDTH; ++x) for (int x = 0; x < BOARD_WIDTH; ++x)
{ {
...@@ -144,12 +158,7 @@ Board::Board(const Board & cpy) ...@@ -144,12 +158,7 @@ Board::Board(const Board & cpy)
{ {
grid[x][y].x = x; grid[x][y].x = x;
grid[x][y].y = y; grid[x][y].y = y;
grid[x][y].piece = cpy.grid[x][y].piece;
if (cpy.grid[x][y].piece != NULL)
{
grid[x][y].piece = new Piece(*(cpy.grid[x][y].piece));
pieces(grid[x][y].piece->colour).push_back(grid[x][y].piece);
}
} }
} }
} }
...@@ -176,17 +185,43 @@ Board::~Board() ...@@ -176,17 +185,43 @@ Board::~Board()
* @purpose Update Piece that has been selected * @purpose Update Piece that has been selected
* @param x, y - Position of Piece to update * @param x, y - Position of Piece to update
* @param index - 0 or 1 - State the Piece "collapsed" into * @param index - 0 or 1 - State the Piece "collapsed" into
* @param type - Type of the Piece * @param type - Type of the Piece as a string
*/ */
void Board::Update_select(int x, int y, int index, const string & type) void Board::Update_select(int x, int y, int index, const string & type)
{ {
cerr << "Updating " << x << "," << y << " " << grid[x][y].piece << " " << index << " " << type << "\n"; Board::Update_select(x, y, index, Piece::str2type(type));
}
/**
* @funct Update_select
* @purpose Update Piece that has been selected
* @param x, y - Position of Piece to update
* @param index - 0 or 1 - State the Piece "collapsed" into
* @param t - Type of the Piece
*/
void Board::Update_select(int x, int y, int index, const Piece::Type & t)
{
cerr << "Updating " << x << "," << y << " " << grid[x][y].piece << " " << index << " " << t << "\n";
Square & s = grid[x][y]; Square & s = grid[x][y];
Clone_copy(s);
assert(s.piece != NULL); assert(s.piece != NULL);
assert(index >= 0 && index < 2); assert(index >= 0 && index < 2);
s.piece->type_index = index; s.piece->type_index = index;
s.piece->types[index] = Piece::str2type(type);
s.piece->current_type = s.piece->types[index]; if (s.piece->types[index] == Piece::UNKNOWN)
{
map<Piece::Type, int> & m = unknown_types(s.piece->colour);
int n = (m[t]--);
if (n < 0)
throw Exception("Board::Update_select", "Too many pieces of type %s found", Piece::type2str(t));
nUnknown(s.piece->colour)--;
}
s.piece->types[index] = t;
s.piece->current_type = t;
} }
/** /**
...@@ -201,6 +236,12 @@ void Board::Update_move(int x1, int y1, int x2, int y2) ...@@ -201,6 +236,12 @@ void Board::Update_move(int x1, int y1, int x2, int y2)
{ {
Square & s1 = grid[x1][y1]; Square & s1 = grid[x1][y1];
Square & s2 = grid[x2][y2]; Square & s2 = grid[x2][y2];
Clone_copy(s1);
if (s2.piece != NULL) if (s2.piece != NULL)
{ {
vector<Piece*> & p = pieces(s2.piece->colour); vector<Piece*> & p = pieces(s2.piece->colour);
...@@ -214,6 +255,11 @@ void Board::Update_move(int x1, int y1, int x2, int y2) ...@@ -214,6 +255,11 @@ void Board::Update_move(int x1, int y1, int x2, int y2)
} }
++i; ++i;
} }
while (i != p.end())
{
(*i)->piece_index -= 1;
++i;
}
Piece * k = king(s2.piece->colour); Piece * k = king(s2.piece->colour);
if (k == s2.piece) if (k == s2.piece)
{ {
...@@ -222,8 +268,10 @@ void Board::Update_move(int x1, int y1, int x2, int y2) ...@@ -222,8 +268,10 @@ void Board::Update_move(int x1, int y1, int x2, int y2)
else else
black_king = NULL; black_king = NULL;
} }
if ((IsClone() && s2.piece == parent->grid[x2][y2].piece) == false)
delete s2.piece; {
delete s2.piece;
}
} }
s1.piece->x = s2.x; s1.piece->x = s2.x;
...@@ -375,6 +423,36 @@ Piece::Type Piece::str2type(const string & str) ...@@ -375,6 +423,36 @@ Piece::Type Piece::str2type(const string & str)
return Piece::UNKNOWN; return Piece::UNKNOWN;
} }
/**
* @funct type2str
* @purpose Convert Piece::Type to string
* @param t - The Types
* @returns a const char*
*/
const char * Piece::type2str(const Piece::Type & t)
{
switch (t)
{
case PAWN:
return "pawn";
case BISHOP:
return "bishop";
case KNIGHT:
return "knight";
case ROOK:
return "rook";
case QUEEN:
return "queen";
case UNKNOWN:
return "unknown";
case KING:
return "king";
default:
throw Exception("Piece::type2str", "Unknown type %d", (int)t);
return "";
}
}
/** /**
* @funct str2colour * @funct str2colour
* @purpose Convert string to Piece::Colour * @purpose Convert string to Piece::Colour
...@@ -392,4 +470,34 @@ Piece::Colour Piece::str2colour(const string & str) ...@@ -392,4 +470,34 @@ Piece::Colour Piece::str2colour(const string & str)
return Piece::BLACK; // should never get here return Piece::BLACK; // should never get here
} }
/**
* @funct AddPiece
* @purpose Creates a new Piece and adds it to a vector
* @param v - The vector
* @params - All remaining parameters passed to Piece::Piece
* @returns Pointer to the new Piece
*/
Piece * Piece::AddPiece(vector<Piece*> & v, int x, int y, const Piece::Colour & colour, const Piece::Type & t1, const Piece::Type & t2,
int type_index)
{
Piece * p = new Piece(x,y,colour,t1, t2,type_index, v.size());
v.push_back(p);
return p;
}
/**
* @funct Clone_copy
* @purpose If necessary, copy the piece in the square
* @param s - The square
*/
void Board::Clone_copy(Square & s)
{
if (s.piece == NULL || !IsClone()) return;
if (parent->grid[s.x][s.y].piece == s.piece)
{
s.piece = new Piece(*(s.piece));
vector<Piece*> & v = pieces(s.piece->colour);
v[s.piece->piece_index] = s.piece;
}
}
\ No newline at end of file
...@@ -32,8 +32,6 @@ class Piece ...@@ -32,8 +32,6 @@ class Piece
typedef enum {PAWN, BISHOP, KNIGHT, ROOK, QUEEN, KING, UNKNOWN} Type; typedef enum {PAWN, BISHOP, KNIGHT, ROOK, QUEEN, KING, UNKNOWN} Type;
typedef enum {WHITE=0, BLACK=1} Colour; typedef enum {WHITE=0, BLACK=1} Colour;
Piece(int x, int y, const Colour & colour, const Type & type1, const Type & type2=UNKNOWN, int type_index = -1); // constructor
Piece(const Piece & cpy); // copy constructor
virtual ~Piece() {} // destructor virtual ~Piece() {} // destructor
int x; int y; // position of the piece int x; int y; // position of the piece
...@@ -44,6 +42,18 @@ class Piece ...@@ -44,6 +42,18 @@ class Piece
static Type str2type(const std::string & str); static Type str2type(const std::string & str);
static Colour str2colour(const std::string & str); static Colour str2colour(const std::string & str);
static const char * type2str(const Type & t);
static Piece * AddPiece(std::vector<Piece*> & v, int x, int y, const Colour & colour, const Type & type1, const Type & type2, int type_index=-1);
private:
friend class Board;
Piece(int x, int y, const Colour & colour, const Type & type1, const Type & type2
, int type_index, int new_piece_index); // constructor
Piece(const Piece & cpy); // copy constructor
int piece_index; // index of the piece in Board's pieces vector
}; };
...@@ -70,33 +80,55 @@ class Board ...@@ -70,33 +80,55 @@ class Board
{ {
public: public:
Board(bool choose_types = false); // constructor Board(bool choose_types = false); // constructor
Board(const Board & cpy); // copy constructor Board(Board & parent); // clones a board, copy on write
virtual ~Board(); // destructor virtual ~Board(); // destructor
// helper; return vector of pieces given player colour // helper; return vector of pieces given player colour
std::vector<Piece*> & pieces(const Piece::Colour & colour) {return ((colour == Piece::WHITE) ? white : black);} std::vector<Piece*> & pieces(const Piece::Colour & colour) {return ((colour == Piece::WHITE) ? white : black);}
// helper; return map of unidentified 2nd types for given colour
std::map<Piece::Type, int> & unknown_types(const Piece::Colour & colour)
{
return ((colour == Piece::WHITE) ? white_unknown : black_unknown);
}
int & nUnknown(const Piece::Colour & colour)
{
return ((colour == Piece::WHITE) ? white_nUnknown : black_nUnknown);
}
// helper; return king given player colour // helper; return king given player colour
Piece * king(const Piece::Colour & colour) {return ((colour == Piece::WHITE) ? white_king : black_king);} Piece * king(const Piece::Colour & colour) {return ((colour == Piece::WHITE) ? white_king : black_king);}
void Update_move(int x, int y, int x2, int y2); // move a piece void Update_move(int x, int y, int x2, int y2); // move a piece
void Update_select(int x, int y, int index, const std::string & type); // update a selected piece void Update_select(int x, int y, int index, const std::string & type); // update a selected piece
void Update_select(int x, int y, int index, const Piece::Type & t);
Square & square(int x, int y) {return grid[x][y];} // get square on board Square & square(int x, int y) {return grid[x][y];} // get square on board
void Get_moves(Piece * p, std::vector<Square*> & v); // get allowed moves for piece void Get_moves(Piece * p, std::vector<Square*> & v); // get allowed moves for piece of known type
// determine if position is on the board // determine if position is on the board
bool Valid_position(int x, int y) {return (x >= 0 && x <= BOARD_WIDTH-1 && y >= 0 && y <= BOARD_HEIGHT-1);} bool Valid_position(int x, int y) {return (x >= 0 && x <= BOARD_WIDTH-1 && y >= 0 && y <= BOARD_HEIGHT-1);}
bool IsClone() const {return parent != NULL;}
void Clone_copy(Square & s);
private: private:
Square grid[BOARD_WIDTH][BOARD_HEIGHT]; Square grid[BOARD_WIDTH][BOARD_HEIGHT];
// All pieces for each player
std::vector<Piece*> white; std::vector<Piece*> white;
std::vector<Piece*> black; std::vector<Piece*> black;
// The number of pieces with each 2nd type that are still unidentified
std::map<Piece::Type, int> white_unknown;
std::map<Piece::Type, int> black_unknown;
int white_nUnknown;
int black_nUnknown;
Piece * white_king; Piece * white_king;
Piece * black_king; Piece * black_king;
Board * parent;
// Add a move to the vector if it is valid // Add a move to the vector if it is valid
void Move(Piece * p, int x, int y, std::vector<Square*> & v); void Move(Piece * p, int x, int y, std::vector<Square*> & v);
......
#Makefile for SilverFish agent
CXX = g++ -std=gnu++0x -Wall -Werror -pedantic -g
OBJ = qchess.o agent.o silverfish.o main.o
LIB =
LINKOBJ = $(OBJ)
RM = rm -f
BIN = silver
$(BIN) : $(LINKOBJ)
$(CXX) -o $(BIN) $(LINKOBJ)
%.o : %.cpp
$(CXX) -c $<
no_main : $(OBJ)
main.o : main.cpp
$(CXX) -c main.cpp
clean :
$(RM) $(BIN) $(OBJ) $(LINKOBJ)
clean_full: #cleans up all backup files
$(RM) $(BIN) $(OBJ) $(LINKOBJ)
$(RM) *.*~
$(RM) *~
\ No newline at end of file
../c++/agent.cpp
\ No newline at end of file
../c++/agent.h
\ No newline at end of file
/**
* silverfish : A Sample agent for UCC::Progcomp2013
* @file main.cpp
* @purpose The main function
*/
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include "silverfish.h"
using namespace std;
/**
* @funct main
* @purpose The main function; starts the agent
* @param argc - Number of arguments, unused
* @param argv - Argument string array, unused
*/
int main(int argc, char ** argv)
{
srand(time(NULL)); // seed random number generator
string colour; cin >> colour; // first read the colour of the agent
try
{
Silver agent(colour); // create an agent using the colour
agent.Run(cin, cout); // run the agent (it will read from cin and output to cout)
}
catch (const Exception & e)
{
return 1;
}
return 0; // Don't use exit(3), because it causes memory leaks in the C++ stdlib
}
../c++/qchess.cpp
\ No newline at end of file
../c++/qchess.h
\ No newline at end of file