Commit 559edeec authored by Sam Moore's avatar Sam Moore

Added C++ agent

As a result, found an issue where qchess was printing "-1" as the index whenever a King was selected.
I don't know why that didn't break the python agent, but it definitely breaks the C++ agent, so I fixed it.
parent 8ac03672
# Makefile for agent++
CXX = g++ -std=gnu++0x -g -Werror -Wall -pedantic
LINK_OBJ = qchess.o agent.o main.o
BIN = agent++
$(BIN) : $(LINK_OBJ)
$(CXX) -o $(BIN) $(LINK_OBJ)
%.o : %.c
$(CXX) $(FLAGS) -c $<
clean :
$(RM) $(BIN) $(OBJ) $(LINK_OBJ)
clean_full: #cleans up all backup files
$(RM) $(BIN) $(OBJ) $(LINK_OBJ)
$(RM) *.*~
$(RM) *~
/**
* agent++ : A Sample agent for UCC::Progcomp2013
* @file agent.cpp
* @purpose Definition of Agent class
*/
#include "agent.h"
#include <cassert> // for sanity checks
using namespace std;
/**
* @constructor Agent
* @param new_colour - colour of the Agent
*/
Agent::Agent(const string & new_colour) : colour(new_colour), board(), selected(NULL)
{
assert(colour == "white" || colour == "black");
}
/**
* @destructor ~Agent
*/
Agent::~Agent()
{
}
/**
* @funct Select
* @purpose Selects a piece at random
* @returns Square containing the selected piece
*/
Square & Agent::Select()
{
vector<Piece*> & v = board.pieces(colour); // get pieces
int choice = rand() % v.size(); // pick random index
Piece * p = v[choice]; // get piece at the index
assert(p->colour == colour);
selected = p; // update selected
//cerr << "Selected " << p->x << "," << p->y << " [" << p->types[0] << "," << p->types[1] << "]\n";
return board.square(p->x, p->y); // get Square from board
}
/**
* @funct Move
* @purpose Pick a square to move a selected piece into
* @returns Square to move last selected piece into
*/
Square & Agent::Move()
{
assert(selected != NULL);
vector<Square*> moves; // all possible moves for selected piece
board.Get_moves(selected, moves); // populate possible moves
assert(moves.size() > 0);
int choice = rand() % moves.size(); // pick random index
return *(moves[choice]); // return that move
}
/**
* @funct Run
* @purpose The "Game Loop" for the agent; read commands and call appropriate function to make responses
* @param in - Stream to read input from (use std::cin)
* @param out - Stream to write output to (use std::cout)
*/
void Agent::Run(istream & in, ostream & out)
{
string cmd; // buffer for tokens
while (in.good())
{
in >> cmd; // read first token only
if (cmd == "QUIT")
{
break;
}
else if (cmd == "SELECTION?")
{
Square & s = Select(); // get selection
out << s.x << " " << s.y << "\n"; // return response through output
}
else if (cmd == "MOVE?")
{
Square & s = Move(); // get move
out << s.x << " " << s.y << "\n"; // return response through output
}
else
{
// There were multiple tokens...
stringstream s(cmd);
int x; int y;
s >> x; // Convert first token (in cmd) to an int
in >> y; // Read second token from in
in >> cmd; // Read third token
if (cmd == "->") // Token indicates a move was made
{
int x2; int y2; // remaining two tokens indicate destination
in >> x2; in >> y2;
board.Update_move(x, y, x2, y2); // update the board
}
else
{
// Tokens are for a selection
int index; stringstream s2(cmd);
s2 >> index; // convert third token to an index
in >> cmd; // Read fourth token - the new type of the piece
board.Update_select(x, y, index, cmd); // update the board
}
}
}
}
/**
* agent++ : A Sample agent for UCC::Progcomp2013
* @file agent.h
* @purpose Declaration of Agent class
*/
#ifndef _AGENT_H
#define _AGENT_H
#include <iostream>
#include <sstream>
#include "qchess.h" // Declarations of Board, Piece and Square classes; see also qchess.cpp
/**
* @class Agent
* @purpose Class that represents an agent which will play qchess
*/
class Agent
{
public:
Agent(const std::string & colour); // initialise with colour
virtual ~Agent(); // destructor
void Run(std::istream & in, std::ostream & out); // agent run loop, specify input and output streams
virtual Square & Select(); // select a square (default: random square containing one of my pieces)
virtual Square & Move(); // select a move (default: random valid move for selected piece)
protected:
const std::string colour; // colour of the agent; do not change it
Board board; // board, see qchess.h
Piece * selected; // last piece chosen by Agent::Select, see qchess.h
};
#endif //_AGENT_H
//EOF
/**
* agent++ : A Sample agent for UCC::Progcomp2013
* @file main.cpp
* @purpose The main function
*/
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include "agent.h" // Declarations for agent, see also agent.cpp
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
Agent agent(colour); // create an agent using the colour
agent.Run(cin, cout); // run the agent (it will read from cin and output to cout)
return 0; // Don't use exit(3), because it causes memory leaks in the C++ stdlib
}
/**
* agent++ : A Sample agent for UCC::Progcomp2013
* @file qchess.h
* @purpose Definitions for game related classes; Piece, Square, Board
*/
#include "qchess.h"
#include <cassert>
using namespace std;
/**
* @constructor
* @param new_x, new_y - Position of piece
* @param new_colour - Colour of piece
* @param type1, type2 - Types of piece
* @param index - Index for initial type of piece
*/
Piece::Piece(int new_x, int new_y, const string & new_colour, const string & type1, const string & type2, int index)
: x(new_x), y(new_y), colour(new_colour), type_index(index), types(), current_type()
{
types[0] = type1; types[1] = type2;
if (index < 0 || index >= 2)
{
current_type = "unknown";
}
else
{
current_type = types[index];
}
}
/**
* @constructor
* @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)
{
types[0] = cpy.types[0];
types[1] = cpy.types[1];
}
/**
* @constructor
* @param choose_types - Indicates whether Board should setup the 2nd types of pieces; default false
*/
Board::Board(bool choose_types)
{
// initialise all the Squares
for (int x = 0; x < BOARD_WIDTH; ++x)
{
for (int y = 0; y < BOARD_HEIGHT; ++y)
{
grid[x][y].x = x;
grid[x][y].y = y;
}
}
// const arrays simplify below code
string colours[] = {"black", "white"};
string types[] = {"rook", "bishop", "knight", "queen", "pawn"};
// frequency of each type of piece
map<string, int> freq;
freq["rook"] = 2;
freq["bishop"] = 2;
freq["knight"] = 2;
freq["queen"] = 1;
freq["pawn"] = 8;
// for white and black...
for (int i = 0; i < 2; ++i)
{
vector<Piece*> & v = pieces(colours[i]); // get vector of pieces
// add pawns
int y = (i == 0) ? 1 : BOARD_HEIGHT-2;
for (int x = 0; x < BOARD_WIDTH; ++x)
{
Piece * p = new Piece(x, y, colours[i], "pawn", "unknown");
v.push_back(p);
}
// add other pieces
y = (i == 0) ? 0 : BOARD_HEIGHT-1;
v.push_back(new Piece(0, y, colours[i], "rook", "unknown"));
v.push_back(new Piece(BOARD_WIDTH-1, y, colours[i], "rook", "unknown"));
v.push_back(new Piece(1, y, colours[i], "knight", "unknown"));
v.push_back(new Piece(BOARD_WIDTH-2, y, colours[i], "knight", "unknown"));
v.push_back(new Piece(2, y, colours[i], "bishop", "unknown"));
v.push_back(new Piece(BOARD_WIDTH-3, y, colours[i], "bishop", "unknown"));
v.push_back(new Piece(3, y, colours[i], "queen", "unknown"));
Piece * k = new Piece(4, y, colours[i], "king", "king", 1);
if (i == 0)
white_king = k;
else
black_king = k;
v.push_back(k);
// add to board and choose second types if required
map<string, int> f(freq);
int type2;
for (unsigned j = 0; j < v.size(); ++j)
{
Piece * p = v[j];
grid[p->x][p->y].piece = p;
if (choose_types)
{
if (p->types[1] != "unknown")
continue;
do
{
type2 = rand() % 5;
} while (f[types[type2]] <= 0);
f[types[type2]] -= 1;
p->types[1] = types[type2];
}
}
}
}
/**
* @constructor
* @param cpy - Board to copy construct from; each Piece in the copy will be *copied*
* The Piece's in the copied Board may be altered without affecting the original
*/
Board::Board(const Board & cpy)
{
for (int x = 0; x < BOARD_WIDTH; ++x)
{
for (int y = 0; y < BOARD_HEIGHT; ++y)
{
grid[x][y].x = x;
grid[x][y].y = y;
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);
}
}
}
}
/**
* @destructor
*/
Board::~Board()
{
white.clear();
black.clear();
for (int x = 0; x < BOARD_WIDTH; ++x)
{
for (int y = 0; y < BOARD_HEIGHT; ++y)
{
delete grid[x][y].piece;
}
}
}
/**
* @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 type - Type of the Piece
*/
void Board::Update_select(int x, int y, int index, const string & type)
{
cerr << "Updating " << x << "," << y << " " << grid[x][y].piece << " " << index << " " << type << "\n";
Square & s = grid[x][y];
assert(s.piece != NULL);
assert(index >= 0 && index < 2);
s.piece->type_index = index;
s.piece->types[index] = type;
s.piece->current_type = type;
}
/**
* @funct Update_move
* @purpose Move a Piece from one square to another
* @param x1, y1 - Coords of Square containing moving Piece
* @param x2, y2 - Coords of Square to move into
* NOTE: Any Piece in the destination Square will be destroyed ("taken")
* and the Board's other members updated accordingly
*/
void Board::Update_move(int x1, int y1, int x2, int y2)
{
Square & s1 = grid[x1][y1];
Square & s2 = grid[x2][y2];
if (s2.piece != NULL)
{
vector<Piece*> & p = pieces(s2.piece->colour);
vector<Piece*>::iterator i = p.begin();
while (i != p.end())
{
if (*i == s2.piece)
{
p.erase(i);
break;
}
++i;
}
Piece * k = king(s2.piece->colour);
if (k == s2.piece)
{
if (k->colour == "white")
white_king = NULL;
else
black_king = NULL;
}
delete s2.piece;
}
s1.piece->x = s2.x;
s1.piece->y = s2.y;
s2.piece = s1.piece;
s1.piece = NULL;
}
/**
* @funct Get_moves
* @purpose Get all moves for a Piece and store them
* @param p - Piece
* @param v - vector to store Squares in. Will *not* be cleared.
*/
void Board::Get_moves(Piece * p, vector<Square*> & v)
{
assert(p->current_type != "unknown");
int x = p->x; int y = p->y;
if (p->current_type == "king")
{
Move(p, x+1, y, v);
Move(p, x-1, y, v);
Move(p, x, y+1, v);
Move(p, x, y-1, v);
Move(p, x+1, y+1, v);
Move(p, x+1, y-1, v);
Move(p, x-1, y+1, v);
Move(p, x-1, y-1, v);
}
else if (p->current_type == "knight")
{
Move(p, x+2, y+1, v);
Move(p, x+2, y-1, v);
Move(p, x-2, y+1, v);
Move(p, x-2, y-1, v);
Move(p, x+1, y+2, v);
Move(p, x-1, y+2, v);
Move(p, x+1, y-2, v);
Move(p, x-1, y-2, v);
}
else if (p->current_type == "pawn")
{
int y1 = (p->colour == "white") ? BOARD_HEIGHT-2 : 1;
int y2 = (p->colour == "white") ? y1 - 2 : y1 + 2;
if (p->types[0] == "pawn" && p->y == y1)
{
Move(p, x, y2, v);
}
y2 = (p->colour == "white") ? y - 1 : y + 1;
Move(p, x, y2, v);
if (Valid_position(x-1, y2) && grid[x-1][y2].piece != NULL)
Move(p, x-1, y2, v);
if (Valid_position(x+1, y2) && grid[x+1][y2].piece != NULL)
Move(p, x+1, y2, v);
}
else if (p->current_type == "bishop")
{
Scan(p, 1, 1, v);
Scan(p, 1, -1, v);
Scan(p, -1, 1, v);
Scan(p, -1, -1, v);
}
else if (p->current_type == "rook")
{
Scan(p, 1, 0, v);
Scan(p, -1, 0, v);
Scan(p, 0, 1, v);
Scan(p, 0, -1, v);
}
else if (p->current_type == "queen")
{
Scan(p, 1, 1, v);
Scan(p, 1, -1, v);
Scan(p, -1, 1, v);
Scan(p, -1, -1, v);
Scan(p, 1, 0, v);
Scan(p, -1, 0, v);
Scan(p, 0, 1, v);
Scan(p, 0, -1, v);
}
}
/**
* @funct Move
* @purpose Add a move to the vector, if it is valid
* @param p - Piece that would move
* @param x, y - Destination Square coords
* @param v - vector to put the destination Square in, if the move is valid
*/
void Board::Move(Piece * p, int x, int y, vector<Square*> & v)
{
if (Valid_position(x, y) && (grid[x][y].piece == NULL || grid[x][y].piece->colour != p->colour))
{
v.push_back(&(grid[x][y]));
}
//else
// cerr << "Square " << x << "," << y << " invalid; " << grid[x][y].piece << "\n";
}
/**
* @funct Scan
* @purpose Add moves in a specified direction to the vector, until we get to an invalid move
* @param p - Piece to start scanning from
* @param vx, vy - "velocity" - change in coords each move
* @param v - vector to store valid Squares in
*/
void Board::Scan(Piece * p, int vx, int vy, vector<Square*> & v)
{
int x = p->x + vx;
int y = p->y + vy;
while (Valid_position(x, y) && (grid[x][y].piece == NULL || grid[x][y].piece->colour != p->colour))
{
v.push_back(&(grid[x][y]));
x += vx;
y += vy;
}
}
/**
* agent++ : A Sample agent for UCC::Progcomp2013
* @file qchess.h
* @purpose Declarations for game related classes; Piece, Square, Board
*/
#ifndef _QCHESS_H
#define _QCHESS_H
// board height and width (don't change!)
#define BOARD_HEIGHT 8
#define BOARD_WIDTH 8
#include <string>
#include <vector>
#include <map>
#include <cstdlib>
#include <iostream>
/**
* @class Piece
* @purpose Represent a quantum chess piece
*/
class Piece
{
public:
Piece(int x, int y, const std::string & new_colour, const std::string & type1 = "unknown", const std::string & type2 = "unknown", int new_type_index = -1); // constructor
Piece(const Piece & cpy); // copy constructor
virtual ~Piece() {} // destructor
int x; int y; // position of the piece
std::string colour; // colour of the piece
int type_index; // indicates state the piece is in; 0, 1, or -1 (unknown)
std::string types[2]; // states of the piece
std::string current_type; // current state of the piece
};
/**
* @class Square
* @purpose Represent a Square on the board; not necessarily occupied
*/
class Square
{
public:
Square() : x(-1), y(-1), piece(NULL) {} // constructor
Square(int new_x, int new_y, Piece * new_piece = NULL) : x(new_x), y(new_y), piece(new_piece) {} //UNUSED
Square(const Square & cpy) : x(cpy.x), y(cpy.y), piece(cpy.piece) {} // copy constructor (UNUSED)
virtual ~Square() {} //destructor
int x; int y; // position of the square
Piece * piece; // Piece that is in the Square (NULL if unoccupied)
};
/**
* @class Board
* @purpose Represent a quantum chess board
*/