From: Sam Moore Date: Mon, 25 Feb 2013 13:49:13 +0000 (+0800) Subject: Added C++ agent X-Git-Url: https://git.ucc.asn.au/?p=progcomp2013.git;a=commitdiff_plain;h=559edeecb292c9971ddd8226e132e1bf1d2640e1 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. --- diff --git a/agents/agent++/Makefile b/agents/agent++/Makefile new file mode 100644 index 0000000..3f9f60f --- /dev/null +++ b/agents/agent++/Makefile @@ -0,0 +1,21 @@ +# 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) *~ diff --git a/agents/agent++/agent.cpp b/agents/agent++/agent.cpp new file mode 100644 index 0000000..d24f1f0 --- /dev/null +++ b/agents/agent++/agent.cpp @@ -0,0 +1,113 @@ +/** + * agent++ : A Sample agent for UCC::Progcomp2013 + * @file agent.cpp + * @purpose Definition of Agent class + */ + +#include "agent.h" +#include // 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 & 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 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 + } + + } + } +} diff --git a/agents/agent++/agent.h b/agents/agent++/agent.h new file mode 100644 index 0000000..bade05b --- /dev/null +++ b/agents/agent++/agent.h @@ -0,0 +1,39 @@ +/** + * agent++ : A Sample agent for UCC::Progcomp2013 + * @file agent.h + * @purpose Declaration of Agent class + */ + +#ifndef _AGENT_H +#define _AGENT_H + +#include +#include +#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 diff --git a/agents/agent++/main.cpp b/agents/agent++/main.cpp new file mode 100644 index 0000000..316df26 --- /dev/null +++ b/agents/agent++/main.cpp @@ -0,0 +1,29 @@ +/** + * agent++ : A Sample agent for UCC::Progcomp2013 + * @file main.cpp + * @purpose The main function + */ +#include +#include +#include + +#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 +} diff --git a/agents/agent++/qchess.cpp b/agents/agent++/qchess.cpp new file mode 100644 index 0000000..57bee89 --- /dev/null +++ b/agents/agent++/qchess.cpp @@ -0,0 +1,347 @@ +/** + * agent++ : A Sample agent for UCC::Progcomp2013 + * @file qchess.h + * @purpose Definitions for game related classes; Piece, Square, Board + */ + +#include "qchess.h" +#include + +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 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 & 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 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 & p = pieces(s2.piece->colour); + vector::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 & 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 & 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 & 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; + } +} diff --git a/agents/agent++/qchess.h b/agents/agent++/qchess.h new file mode 100644 index 0000000..154e440 --- /dev/null +++ b/agents/agent++/qchess.h @@ -0,0 +1,101 @@ +/** + * 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 +#include +#include +#include +#include + +/** + * @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 + */ +class Board +{ + public: + Board(bool choose_types = false); // constructor + Board(const Board & cpy); // copy constructor + virtual ~Board(); // destructor + + + // helper; return vector of pieces given player colour + std::vector & pieces(const std::string & colour) {return ((colour == "white") ? white : black);} + // helper; return king given player colour + Piece * king(const std::string & colour) {return ((colour == "white") ? white_king : black_king);} + + 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 + + Square & square(int x, int y) {return grid[x][y];} // get square on board + + void Get_moves(Piece * p, std::vector & v); // get allowed moves for piece + + // 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);} + + private: + Square grid[BOARD_WIDTH][BOARD_HEIGHT]; + + + std::vector white; + std::vector black; + Piece * white_king; + Piece * black_king; + + // Add a move to the vector if it is valid + void Move(Piece * p, int x, int y, std::vector & v); + + // Add all valid moves in a direction, stopping at the first invalid move + void Scan(Piece * p, int vx, int vy, std::vector & v); +}; + +#endif //_QCHESS_H + +//EOF diff --git a/qchess/build/exe.linux-x86_64-2.7.zip b/qchess/build/exe.linux-x86_64-2.7.zip index 5b7b25c..b9fbb07 100644 Binary files a/qchess/build/exe.linux-x86_64-2.7.zip and b/qchess/build/exe.linux-x86_64-2.7.zip differ diff --git a/qchess/build/exe.win32-2.7.zip b/qchess/build/exe.win32-2.7.zip index 9250604..69b5d58 100644 Binary files a/qchess/build/exe.win32-2.7.zip and b/qchess/build/exe.win32-2.7.zip differ diff --git a/qchess/qchess.py b/qchess/qchess.py index ced6001..3554da5 100755 --- a/qchess/qchess.py +++ b/qchess/qchess.py @@ -76,7 +76,7 @@ class Piece(): # Collapses the wave function! def select(self): - if self.current_type == "unknown": + if self.current_type == "unknown" or not self.choice in [0,1]: self.choice = random.randint(0,1) if self.types[self.choice][0] == '?': self.types[self.choice] = self.types[self.choice][1:] @@ -2546,4 +2546,4 @@ if __name__ == "__main__": sys.exit(102) # --- main.py --- # -# EOF - created from make on Tue Feb 12 17:06:37 WST 2013 +# EOF - created from make on Mon Feb 25 21:46:16 WST 2013 diff --git a/qchess/src/piece.py b/qchess/src/piece.py index 81fbb56..a72937c 100644 --- a/qchess/src/piece.py +++ b/qchess/src/piece.py @@ -75,7 +75,7 @@ class Piece(): # Collapses the wave function! def select(self): - if self.current_type == "unknown": + if self.current_type == "unknown" or not self.choice in [0,1]: self.choice = random.randint(0,1) if self.types[self.choice][0] == '?': self.types[self.choice] = self.types[self.choice][1:]