Added C++ agent
authorSam Moore <[email protected]>
Mon, 25 Feb 2013 13:49:13 +0000 (21:49 +0800)
committerSam Moore <[email protected]>
Mon, 25 Feb 2013 13:49:13 +0000 (21:49 +0800)
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.

agents/agent++/Makefile [new file with mode: 0644]
agents/agent++/agent.cpp [new file with mode: 0644]
agents/agent++/agent.h [new file with mode: 0644]
agents/agent++/main.cpp [new file with mode: 0644]
agents/agent++/qchess.cpp [new file with mode: 0644]
agents/agent++/qchess.h [new file with mode: 0644]
qchess/build/exe.linux-x86_64-2.7.zip
qchess/build/exe.win32-2.7.zip
qchess/qchess.py
qchess/src/piece.py

diff --git a/agents/agent++/Makefile b/agents/agent++/Makefile
new file mode 100644 (file)
index 0000000..3f9f60f
--- /dev/null
@@ -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 (file)
index 0000000..d24f1f0
--- /dev/null
@@ -0,0 +1,113 @@
+/**
+ * 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
+                       }
+                       
+               }
+       }
+}
diff --git a/agents/agent++/agent.h b/agents/agent++/agent.h
new file mode 100644 (file)
index 0000000..bade05b
--- /dev/null
@@ -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 <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
diff --git a/agents/agent++/main.cpp b/agents/agent++/main.cpp
new file mode 100644 (file)
index 0000000..316df26
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * 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
+}
diff --git a/agents/agent++/qchess.cpp b/agents/agent++/qchess.cpp
new file mode 100644 (file)
index 0000000..57bee89
--- /dev/null
@@ -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 <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;
+       }
+}
diff --git a/agents/agent++/qchess.h b/agents/agent++/qchess.h
new file mode 100644 (file)
index 0000000..154e440
--- /dev/null
@@ -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 <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
+ */
+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<Piece*> & 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<Square*> & 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<Piece*> white;
+               std::vector<Piece*> 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<Square*> & v);
+
+               // Add all valid moves in a direction, stopping at the first invalid move
+               void Scan(Piece * p, int vx, int vy, std::vector<Square*> & v);
+};
+
+#endif //_QCHESS_H
+
+//EOF
index 5b7b25c..b9fbb07 100644 (file)
Binary files a/qchess/build/exe.linux-x86_64-2.7.zip and b/qchess/build/exe.linux-x86_64-2.7.zip differ
index 9250604..69b5d58 100644 (file)
Binary files a/qchess/build/exe.win32-2.7.zip and b/qchess/build/exe.win32-2.7.zip differ
index ced6001..3554da5 100755 (executable)
@@ -76,7 +76,7 @@ class Piece():
        
        # Collapses the wave function!          
        def select(self):
        
        # 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:]
                        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 --- #
                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
index 81fbb56..a72937c 100644 (file)
@@ -75,7 +75,7 @@ class Piece():
        
        # Collapses the wave function!          
        def select(self):
        
        # 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:]
                        self.choice = random.randint(0,1)
                        if self.types[self.choice][0] == '?':
                                self.types[self.choice] = self.types[self.choice][1:]

UCC git Repository :: git.ucc.asn.au