From: Sam Moore Date: Thu, 8 Dec 2011 07:28:30 +0000 (+0800) Subject: The /home was not necessary X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=88fc5a96c424e9a451b98f3b680bc1980345320d;p=progcomp2012.git The /home was not necessary Derp --- diff --git a/home/progcomp/agents/asmodeus/asmodeus.py b/home/progcomp/agents/asmodeus/asmodeus.py deleted file mode 100644 index 29c50a2..0000000 --- a/home/progcomp/agents/asmodeus/asmodeus.py +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/python -u - -#NOTE: The -u option is required for unbuffered stdin/stdout. -# If stdin/stdout are buffered, the manager program will not recieve any messages and assume that the agent has timed out. - -''' - asmodeus.py - A sample Stratego AI for the UCC Programming Competition 2012 - - Written in python, the slithery language - - author Sam Moore (matches) [SZM] - website http://matches.ucc.asn.au/stratego - email progcomp@ucc.asn.au or matches@ucc.asn.au - git git.ucc.asn.au/progcomp2012.git -''' - -from basic_python import * -from path import * - - - -class Asmodeus(BasicAI): - " A slightly more advanced python based AI who calculates the optimum score for each move " - def __init__(self): - #sys.stderr.write("Asmodeus initialised...\n") - BasicAI.__init__(self) - self.riskScores = {'1' : 0.01 , '2' : 0.05 , '3' : 0.15 , '4' : 0.2, '5' : 0.2, '6' : 0.25, '7' : 0.25, '8' : 0.01 , '9' : 0.4, 's' : 0.01} - self.bombScores = {'1' : 0.0 , '2' : 0.0 , '3' : 0.05 , '4' : 0.1, '5' : 0.3, '6' : 0.4, '7' : 0.5, '8' : 1 , '9' : 0.6, 's' : 0.1} - self.flagScores = {'1' : 1.0 , '2' : 1.0 , '3' : 1.0 , '4' : 1.0, '5' : 1.0, '6' : 1.0, '7' : 1, '8' : 1.0 , '9' : 1.0, 's' : 1.0} - self.suicideScores = {'1' : 0.0 , '2' : 0.0 , '3' : 0.0, '4' : 0.0, '5' : 0.0, '6' : 0.05, '7' : 0.1, '8' : 0.0 , '9' : 0.0, 's' : 0.0} - self.killScores = {'1' : 1.0 , '2' : 0.9 , '3' : 0.8 , '4' : 0.5, '5' : 0.5, '6' : 0.5, '7' : 0.4, '8' : 0.9 , '9' : 0.6, 's' : 0.9} - - def MakeMove(self): - #sys.stderr.write("Asmodeus MakingMove...\n") - "Over-rides the default BasicAI.MakeMove function" - - moveList = [] - - for unit in self.units: - if unit.mobile() == False: - continue - - for enemy in self.enemyUnits: - if enemy == unit: - continue - path = PathFinder().pathFind((unit.x, unit.y), (enemy.x, enemy.y), self.board) - - #sys.stderr.write("Computed path: " + str(path) + "\n") - if path == False or len(path) <= 0: - continue - score = self.CalculateScore(unit, enemy) - - score = float(score / float(len(path) + 1)) - moveList.append([unit, path, enemy, score]) - - - - if len(moveList) <= 0: - #sys.stderr.write("NO Moves!\n") - return BasicAI.MakeMove(self) - - moveList.sort(key = lambda e : e[len(e)-1], reverse=True) - - #sys.stderr.write("Chosen move is: " + str(moveList[0][0].x) + " " + str(moveList[0][0].y) + " " + moveList[0][1][0] + " (targeting enemy with rank " + moveList[0][2].rank + " at position " + str(moveList[0][2].x) + " " + str(moveList[0][2].y) + " (my rank " + moveList[0][0].rank+")\n") - print str(moveList[0][0].x) + " " + str(moveList[0][0].y) + " " + moveList[0][1][0] - return True - - def CalculateScore(self, attacker, defender): - if defender.rank == '?': - return self.riskScores[attacker.rank] - elif defender.rank == 'B': - return self.bombScores[attacker.rank] - elif defender.rank == 'F': - return self.flagScores[attacker.rank] - elif defender.valuedRank() < attacker.valuedRank() or defender.rank == '1' and attacker.rank == 's': - return self.killScores[defender.rank] - else: - return self.suicideScores[attacker.rank] - - - diff --git a/home/progcomp/agents/asmodeus/basic_python.py b/home/progcomp/agents/asmodeus/basic_python.py deleted file mode 120000 index 3d6b342..0000000 --- a/home/progcomp/agents/asmodeus/basic_python.py +++ /dev/null @@ -1 +0,0 @@ -../basic_python/basic_python.py \ No newline at end of file diff --git a/home/progcomp/agents/asmodeus/info b/home/progcomp/agents/asmodeus/info deleted file mode 100644 index 2ce2124..0000000 --- a/home/progcomp/agents/asmodeus/info +++ /dev/null @@ -1 +0,0 @@ -run.py diff --git a/home/progcomp/agents/asmodeus/path.py b/home/progcomp/agents/asmodeus/path.py deleted file mode 100644 index 3f08979..0000000 --- a/home/progcomp/agents/asmodeus/path.py +++ /dev/null @@ -1,57 +0,0 @@ - -import sys -import random - - -class PathFinder: - def __init__(self): - self.visited = [] - - pass - - def pathFind(self, start, end, board): - if start[0] == end[0] and start[1] == end[1]: - #sys.stderr.write("Got to destination!\n") - return [] - - if self.visited.count(start) > 0: - #sys.stderr.write("Back track!!\n") - return False - if start[0] < 0 or start[0] >= len(board) or start[1] < 0 or start[1] >= len(board[start[0]]): - #sys.stderr.write("Out of bounds!\n") - return False - if len(self.visited) > 0 and board[start[0]][start[1]] != None: - #sys.stderr.write("Full position!\n") - return False - - - - self.visited.append(start) - left = (start[0]-1, start[1]) - right = (start[0]+1, start[1]) - up = (start[0], start[1]-1) - down = (start[0], start[1]+1) - choices = [left, right, up, down] - choices.sort(key = lambda e : random.randint(0,5)) - options = [] - for point in choices: - option = [point, self.pathFind(point,end,board)] - if option[1] != False: - options.append(option) - - options.sort(key = lambda e : len(e[1])) - if len(options) == 0: - #sys.stderr.write("NO options!\n") - return False - else: - if options[0][0] == left: - options[0][1].insert(0,"LEFT") - elif options[0][0] == right: - options[0][1].insert(0,"RIGHT") - elif options[0][0] == up: - options[0][1].insert(0,"UP") - elif options[0][0] == down: - options[0][1].insert(0,"DOWN") - #sys.stderr.write("PathFind got path " + str(options[0]) + "\n") - return options[0][1] - diff --git a/home/progcomp/agents/asmodeus/run.py b/home/progcomp/agents/asmodeus/run.py deleted file mode 100755 index b646d06..0000000 --- a/home/progcomp/agents/asmodeus/run.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/python -u - -from asmodeus import * - -asmodeus = Asmodeus() -if asmodeus.Setup(): - while asmodeus.MoveCycle(): - pass diff --git a/home/progcomp/agents/basic_cpp/Makefile b/home/progcomp/agents/basic_cpp/Makefile deleted file mode 100644 index 0a249b3..0000000 --- a/home/progcomp/agents/basic_cpp/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -#Makefile for basic_cpp -# Sample C++ Stratego AI -# UCC Programming Competition 2012 - -CPP = g++ -Wall -pedantic -lSDL -lGL -g -OBJ = basic_cpp.o - -BIN = basic_cpp - - - -$(BIN) : $(OBJ) - $(CPP) -o $(BIN) $(OBJ) - - - - -%.o : %.cpp %.h - $(CPP) -c $< - -clean : - $(RM) $(BIN) $(OBJ) $(LINKOBJ) - -#Cleans up all backup files -clean_full: - $(RM) $(BIN) $(OBJ) $(LINKOBJ) - $(RM) *.*~ - $(RM) *~ - - diff --git a/home/progcomp/agents/basic_cpp/basic_cpp.cpp b/home/progcomp/agents/basic_cpp/basic_cpp.cpp deleted file mode 100644 index ab00826..0000000 --- a/home/progcomp/agents/basic_cpp/basic_cpp.cpp +++ /dev/null @@ -1,560 +0,0 @@ -/** - * "basic_cpp", a sample Stratego AI for the UCC Programming Competition 2012 - * Implementations of main function, and Helper functions - * - * @author Sam Moore (matches) [SZM] - * @website http://matches.ucc.asn.au/stratego - * @email progcomp@ucc.asn.au or matches@ucc.asn.au - * @git git.ucc.asn.au/progcomp2012.git - */ - -#include "basic_cpp.h" //Needs class Base_Cpp and the includes in this file - -using namespace std; - -/** - * The characters used to represent various pieces - * NOTHING, BOULDER, FLAG, SPY, SCOUT, MINER, SERGEANT, LIETENANT, CAPTAIN, MAJOR, COLONEL, GENERAL, MARSHAL, BOMB, UNKNOWN - */ -char Piece::tokens[] = {'.','*','F','s','9','8','7','6','5','4','3','2','1','B','?'}; - -/** - * Gets a rank from the character used to represent it - * Basic lookup of Piece::tokens - */ -Rank Piece::GetRank(char token) -{ - for (int ii=0; ii <= 14; ++ii) - { - if (tokens[ii] == token) - return (Rank)(ii); - } - return UNKNOWN; -} - -/** - * IMPLEMENTATION of Helper FOLLOWS - */ - -/** - * Convert string to direction - */ -Direction Helper::StrToDir(const string & dir) -{ - if (dir == "UP") - return UP; - else if (dir == "DOWN") - return DOWN; - else if (dir == "LEFT") - return LEFT; - else if (dir == "RIGHT") - return RIGHT; - else - return DIRECTION_ERROR; -} - -/** - * Direction to String - */ -void Helper::DirToStr(const Direction & dir, std::string & buffer) -{ - switch (dir) - { - case UP: - buffer = "UP"; - break; - case DOWN: - buffer = "DOWN"; - break; - case LEFT: - buffer = "LEFT"; - break; - case RIGHT: - buffer = "RIGHT"; - break; - default: - buffer = "DIRECTION_ERROR"; - break; - } -} - -/** - * Move a point in a direction - */ -void Helper::MoveInDirection(int & x, int & y, const Direction & dir, int multiplier) -{ - switch (dir) - { - case UP: - y -= multiplier; - break; - case DOWN: - y += multiplier; - break; - case LEFT: - x -= multiplier; - break; - case RIGHT: - x += multiplier; - break; - default: - break; - } - -} - -/** - * Tokenise a string - */ -int Helper::Tokenise(std::vector & buffer, std::string & str, char split) -{ - string token = ""; - for (unsigned int x = 0; x < str.size(); ++x) - { - if (str[x] == split && token.size() > 0) - { - buffer.push_back(token); - token = ""; - } - if (str[x] != split) - token += str[x]; - } - if (token.size() > 0) - buffer.push_back(token); - return buffer.size(); -} - -/** - * Convert string to integer - */ -int Helper::Integer(std::string & fromStr) -{ - stringstream s(fromStr); - int result = 0; - s >> result; - return result; -} - -/** - * Read in a line from stdin - */ -void Helper::ReadLine(std::string & buffer) -{ - buffer = ""; - for (char c = cin.get(); c != '\n' && cin.good(); c = cin.get()) - { - buffer += c; - } -} - -/** - * IMPLEMENTATION of Board FOLLOWS - */ - -/** - * Constructer for Board - */ -Board::Board(int w, int h) : width(w), height(h), board(NULL) -{ - //Construct 2D array of P*'s - board = new Piece**[width]; - for (int x=0; x < width; ++x) - { - board[x] = new Piece*[height]; - for (int y=0; y < height; ++y) - board[x][y] = NULL; - } -} - -/** - * Destructor for board - */ -Board::~Board() -{ - //Destroy the 2D array of P*'s - for (int x=0; x < width; ++x) - { - for (int y=0; y < height; ++y) - delete board[x][y]; - delete [] board[x]; - } -} - -/** - * Retrieves a piece on the Board - * @param x x coordinate - * @param y y coordinate - * @returns A Piece* for the piece, or NULL if there is no piece at the point given - */ -Piece * Board::Get(int x, int y) const -{ - if (ValidPosition(x, y)) - return board[x][y]; - return NULL; -} -/** - * Sets a piece on the Board - * @param x x coordinate - * @param y y coordinate - * @param newPiece - * @param returns newPiece if successful, NULL if not - */ -Piece * Board::Set(int x, int y, Piece * newPiece) -{ - if (!ValidPosition(x, y)) - return NULL; - board[x][y] = newPiece; - assert(Get(x,y) == newPiece); - return newPiece; -} - -/** - * IMPLEMENTATION of Base_Cpp FOLLOWS - */ - -/** - * Constructor for AI - */ -BasicAI::BasicAI() : turn(0), board(NULL), units(), enemyUnits(), colour(NONE), colourStr("") -{ - srand(time(NULL)); - cin.rdbuf()->pubsetbuf(NULL, 0); - cout.rdbuf()->pubsetbuf(NULL, 0); -} - -/** - * Destructor for AI - */ -BasicAI::~BasicAI() -{ - if (board != NULL) - delete board; -} - - -/** - * Setup the AI - * @returns true if successful, false on error - */ -bool BasicAI::Setup() -{ - - cin >> colourStr; - - - std::string opponentName(""); //opponentName is unused, just read it - cin >> opponentName; - - int width = 0; int height = 0; - cin >> width; cin >> height; - - while(cin.get() != '\n' && cin.good()); //trim newline - - board = new Board(width, height); - - if (colourStr == "RED") - { - colour = RED; - cout << "FB8sB479B8\nBB31555583\n6724898974\n967B669999\n"; - } - else if (colourStr == "BLUE") - { - colour = BLUE; - cout << "967B669999\n6724898974\nBB31555583\nFB8sB479B8\n"; - } - else - return false; - - return (board != NULL); -} - -/** - * Performs a move, including the saving states bits - * @returns true if the game is to continue, false if it is to end - */ -bool BasicAI::MoveCycle() -{ - //cerr << "BasicAI at MoveCycle()\n"; - if (!InterpretResult()) - return false; - if (!ReadBoard()) - return false; - if (!MakeMove()) - return false; - - turn++; - return InterpretResult(); -} - -/** - * Interprets the result of a move. Ignores the first move - * @returns true if successful, false if there was an error - */ -bool BasicAI::InterpretResult() -{ - //cerr << "BasicAI at InterpretResult()\n"; - if (turn == 0) - { - while (cin.get() != '\n' && cin.good()); - return true; - } - - - string resultLine; Helper::ReadLine(resultLine); - vector tokens; Helper::Tokenise(tokens, resultLine, ' '); - - if (tokens.size() <= 0) - { - //cerr << "No tokens!\n"; - return false; - } - - if (tokens[0] == "QUIT") - { - return false; - } - - if (tokens[0] == "NO_MOVE") - { - return true; - - } - - if (tokens.size() < 4) - { - //cerr << "Only got " << tokens.size() << " tokens\n"; - return false; - } - - - int x = Helper::Integer(tokens[0]); - int y = Helper::Integer(tokens[1]); - - - - Direction dir = Helper::StrToDir(tokens[2]); - string & outcome = tokens[3]; - - int x2 = x; int y2 = y; Helper::MoveInDirection(x2,y2,dir); - - Piece * attacker = board->Get(x,y); - if (attacker == NULL) - { - //cerr << "No attacker!\n"; - return false; - } - Piece * defender = board->Get(x2,y2); - if (outcome == "OK") - { - board->Set(x2,y2, attacker); - board->Set(x,y,NULL); - attacker->x = x2; attacker->y = y2; - } - else if (outcome == "KILLS") - { - if (defender == NULL) - { - //cerr << "No defender!\n"; - return false; - } - - board->Set(x2,y2, attacker); - board->Set(x,y,NULL); - attacker->x = x2; attacker->y = y2; - - attacker->rank = Piece::GetRank(tokens[4][0]); - ForgetUnit(defender); - } - else if (outcome == "DIES") - { - if (defender == NULL) - { - //cerr << "No defender!\n"; - return false; - } - - - board->Set(x,y,NULL); - defender->rank = Piece::GetRank(tokens[5][0]); - ForgetUnit(attacker); - - - } - else if (outcome == "BOTHDIE") - { - board->Set(x,y,NULL); - board->Set(x2,y2, NULL); - - ForgetUnit(attacker); - ForgetUnit(defender); - } - else if (outcome == "FLAG") - { - //cerr << "BasicAI - Flag was captured, exit!\n"; - return false; - } - else if (outcome == "ILLEGAL") - { - //cerr << "BasicAI - Illegal move, exit!\n"; - return false; - } - - //cerr << "BasicAI finished InterpretResult()\n"; - return true; -} - -/** - * Performs a random move - * TODO: Overwrite with custom move - * @returns true if a move could be made (including NO_MOVE), false on error - */ -bool BasicAI::MakeMove() -{ - //cerr << "BasicAI at MakeMove()\n"; - if (units.size() <= 0) - { - //cerr << " No units!\n"; - return false; - - } - - int index = rand() % units.size(); - int startIndex = index; - while (true) - { - - - Piece * piece = units[index]; - if (piece != NULL && piece->Mobile()) - { - int dirIndex = rand() % 4; - int startDirIndex = dirIndex; - while (true) - { - int x = piece->x; int y = piece->y; - assert(board->Get(x,y) == piece); - Helper::MoveInDirection(x,y,(Direction)(dirIndex)); - if (board->ValidPosition(x,y)) - { - Piece * target = board->Get(x,y); - if (target == NULL || (target->colour != piece->colour && target->colour != NONE)) - { - string dirStr; - Helper::DirToStr((Direction)(dirIndex), dirStr); - cout << piece->x << " " << piece->y << " " << dirStr << "\n"; - return true; - } - } - - dirIndex = (dirIndex + 1) % 4; - if (dirIndex == startDirIndex) - break; - } - } - - index = (index+1) % (units.size()); - if (index == startIndex) - { - cout << "NO_MOVE\n"; - return true; - } - } - return true; -} - -/** - * Reads in the board - * On first turn, sets up Board - * On subsquent turns, takes no action - * @returns true on success, false on error - */ -bool BasicAI::ReadBoard() -{ - //cerr << "BasicAI at ReadBoard()\n"; - for (int y = 0; y < board->Height(); ++y) - { - string row; - Helper::ReadLine(row); - for (unsigned int x = 0; x < row.size(); ++x) - { - if (turn == 0) - { - switch (row[x]) - { - case '.': - break; - case '#': - board->Set(x,y, new Piece(x,y,Piece::Opposite(colour), UNKNOWN)); - enemyUnits.push_back(board->Get(x,y)); - break; - case '+': - board->Set(x,y, new Piece(x,y,NONE, BOULDER)); - break; - default: - board->Set(x,y,new Piece(x,y,colour, Piece::GetRank(row[x]))); - units.push_back(board->Get(x,y)); - break; - } - } - } - } - return true; -} - -/** - * Removes a piece from memory - * @param piece The piece to delete - * @returns true if the piece was actually found - */ -bool BasicAI::ForgetUnit(Piece * piece) -{ - //cerr << "BasicAI at ForgetUnit()\n"; - bool result = false; - vector::iterator i = units.begin(); - while (i != units.end()) - { - if ((*i) == piece) - { - i = units.erase(i); result = true; - continue; - } - ++i; - } - - i = enemyUnits.begin(); - while (i != enemyUnits.end()) - { - if ((*i) == piece) - { - i = enemyUnits.erase(i); result = true; - continue; - } - ++i; - } - - - delete piece; - return result; -} - - -/** - * The main function - * @param argc - * @param argv - * @returns zero on success, non-zero on failure - */ -int main(int argc, char ** argv) -{ - - srand(time(NULL)); - - BasicAI * basicAI = new BasicAI(); - if (basicAI->Setup()) - { - while (basicAI->MoveCycle()); - } - delete basicAI; - exit(EXIT_SUCCESS); - return 0; -} diff --git a/home/progcomp/agents/basic_cpp/basic_cpp.h b/home/progcomp/agents/basic_cpp/basic_cpp.h deleted file mode 100644 index 733d9a7..0000000 --- a/home/progcomp/agents/basic_cpp/basic_cpp.h +++ /dev/null @@ -1,145 +0,0 @@ -/** - * "basic_cpp", a sample Stratego AI for the UCC Programming Competition 2012 - * Declarations for classes Piece, Board and Basic_Cpp - * @author Sam Moore (matches) [SZM] - * @website http://matches.ucc.asn.au/stratego - * @email progcomp@ucc.asn.au or matches@ucc.asn.au - * @git git.ucc.asn.au/progcomp2012.git - */ - -#ifndef BASIC_CPP_H -#define BASIC_CPP_H - -#include -#include -#include -#include -#include -#include - - - - - -/** - * enum for possible ranks of pieces - */ -typedef enum {UNKNOWN=14,BOMB=13,MARSHAL=12, GENERAL=11, COLONEL=10, MAJOR=9, CAPTAIN=8, LIEUTENANT=7, SERGEANT=6, MINER=5, SCOUT=4, SPY=3, FLAG=2,BOULDER=1, NOTHING=0} Rank; - -/** - * enum for possible colours of pieces and the AI - */ -typedef enum {RED=0, BLUE=1, NONE, BOTH} Colour; - - -/** - * Class to represent a piece on the board - * TODO: Add features required for Pieces as used by your AI - * For example, can replace the single member "rank" with two, "maxRank" and "minRank" - * Or add an array of probability values for EVERY rank! - */ -class Piece -{ - public: - static char tokens[]; //The tokens used to identify various pieces - - Piece(int newX, int newY,const Colour & newColour, const Rank & newRank = UNKNOWN) - : x(newX), y(newY), colour(newColour), rank(newRank) {} - virtual ~Piece() {} - - bool Mobile() const {return rank != BOMB && rank != FLAG;} - - static Colour Opposite(const Colour & colour) {return colour == RED ? BLUE : RED;} - - int x; int y; - const Colour colour; //The colour of the piece - Rank rank; //The rank of the piece - - static Rank GetRank(char token); //Helper to get rank from character - -}; - -/** - * enum for Directions that a piece can move in - */ -typedef enum {UP=0, DOWN=1, LEFT=2, RIGHT=3, DIRECTION_ERROR=4} Direction; - -/** - * Class to represent a board - */ -class Board -{ - public: - Board(int width, int height); //Construct a board with width and height - virtual ~Board(); //Destroy the board - - Piece * Get(int x, int y) const; //Retrieve single piece - Piece * Set(int x, int y, Piece * newPiece); //Add piece to board - - int Width() const {return width;} //getter for width - int Height() const {return height;} //getter for height - - bool ValidPosition(int x, int y) const {return (x >= 0 && x < width && y >= 0 && y < height);} //Helper - is position within board? - private: - - int width; //The width of the board - int height; //The height of the board - Piece ** * board; //The pieces on the board -}; - -/** - * Basic AI class - * TODO: Make sure that if Piece is changed, BasicAI is updated to be consistent - * TODO: More complex AI's should inherit off this class. - * It is recommended that only MakeMove is altered - hence the other functions are not virtual. - */ -class BasicAI -{ - public: - BasicAI(); //Constructor - virtual ~BasicAI(); //Destructor - - bool Setup(); //Implements setup protocol - bool MoveCycle(); //Implements the MoveCycle protocol - bool ReadBoard(); //Reads the board as part of the MoveCycle protocol - bool InterpretResult(); //Interprets the result of a move - virtual bool MakeMove(); //Makes a move - defaults to randomised moves - bool DebugPrintBoard(); //DEBUG - Prints the board to stderr - protected: - int turn; - Board * board; //The board - std::vector units; //Allied units - std::vector enemyUnits; //Enemy units - - bool ForgetUnit(Piece * forget); //Delete and forget about a unit - - Colour colour; - std::string colourStr; -}; - -/** - * Purely static class of Helper functions - */ -class Helper -{ - public: - static Direction StrToDir(const std::string & str); //Helper - Convert string to a Direction enum - static void DirToStr(const Direction & dir, std::string & buffer); //Helper - Convert Direction enum to a string - static void MoveInDirection(int & x, int & y, const Direction & dir, int multiplier = 1); //Helper - Move a point in a direction - static int Tokenise(std::vector & buffer, std::string & str, char split = ' '); //Helper - Split a string into tokens - static int Integer(std::string & fromStr); //Helper - convert a string to an integer - static void ReadLine(std::string & buffer); //Helper - read a line from stdin - private: - //By making these private we ensure that no one can create an instance of Helper - //Yes we could use namespaces instead, but I prefer this method because you can use private static member variables - //Not that I am. But you can. - Helper() {} - ~Helper() {} -}; - - - -#endif //BASIC_CPP_H - -//EOF - diff --git a/home/progcomp/agents/basic_cpp/info b/home/progcomp/agents/basic_cpp/info deleted file mode 100644 index ee1b502..0000000 --- a/home/progcomp/agents/basic_cpp/info +++ /dev/null @@ -1 +0,0 @@ -basic_cpp diff --git a/home/progcomp/agents/basic_python/basic_python.py b/home/progcomp/agents/basic_python/basic_python.py deleted file mode 100644 index cf024e8..0000000 --- a/home/progcomp/agents/basic_python/basic_python.py +++ /dev/null @@ -1,294 +0,0 @@ -#!/usr/bin/python -u - -#NOTE: The -u option is required for unbuffered stdin/stdout. -# If stdin/stdout are buffered, the manager program will not recieve any messages and assume that the agent has timed out. - -""" - basic_python.py - A sample Stratego AI for the UCC Programming Competition 2012 - - Written in python, the slithery language - Simply makes random moves, as long as possible - - author Sam Moore (matches) [SZM] - website http://matches.ucc.asn.au/stratego - email progcomp@ucc.asn.au or matches@ucc.asn.au - git git.ucc.asn.au/progcomp2012.git -""" - -import sys -import random - -ranks = ['B','1','2','3','4','5','6','7','8','9','s','F', '?', '+'] - -def move(x, y, direction): - """ Moves point (x,y) in direction, returns a pair """ - if direction == "UP": - return (x,y-1) - elif direction == "DOWN": - return (x,y+1) - elif direction == "LEFT": - return (x-1, y) - elif direction == "RIGHT": - return (x+1, y) - return (x,y) - - - -def oppositeColour(colour): - """ Returns the opposite colour to that given """ - if colour == "RED": - return "BLUE" - elif colour == "BLUE": - return "RED" - else: - return "NONE" - -class Piece: - """ Class representing a piece - Pieces have colour, rank and co-ordinates - """ - def __init__(self, colour, rank, x, y): - self.colour = colour - self.rank = rank - self.x = x - self.y = y - self.lastMoved = -1 - - def mobile(self): - return self.rank != 'F' and self.rank != 'B' and self.rank != '?' and self.rank != '+' - - def valuedRank(self): - if ranks.count(self.rank) > 0: - return len(ranks) - 2 - ranks.index(self.rank) - else: - return 0 - - - - -class BasicAI: - """ - BasicAI class to play a game of stratego - Implements the protocol correctly. Stores the state of the board in self.board - Only makes random moves. - Override method "MakeMove" for more complex moves - """ - def __init__(self): - """ Constructs the BasicAI agent, and starts it playing the game """ - #sys.stderr.write("BasicAI __init__ here...\n"); - self.turn = 0 - self.board = [] - self.units = [] - self.enemyUnits = [] - - - - def Setup(self): - """ Implements Setup part of protocol. Always uses the same setup. Override to create custom setups """ - #sys.stderr.write("BasicAI Setup here...\n"); - setup = sys.stdin.readline().split(' ') - if len(setup) != 4: - sys.stderr.write("BasicAI setup fails, expected 4 tokens, got " + str(len(setup)) + " "+str(setup) + "\n") - self.colour = setup[0] - self.opponentName = setup[1] - self.width = int(setup[2]) - self.height = int(setup[3]) - for x in range(0, self.width): - self.board.append([]) - for y in range(0, self.height): - self.board[x].append(None) - if self.colour == "RED": - print "FB8sB479B8\nBB31555583\n6724898974\n967B669999" - elif self.colour == "BLUE": - print "967B669999\n6724898974\nBB31555583\nFB8sB479B8" - return True - - def MoveCycle(self): - #sys.stderr.write("BasicAI MakeMove here...\n"); - if self.InterpretResult() == False or self.ReadBoard() == False or self.MakeMove() == False: - return False - self.turn += 1 - return self.InterpretResult() - - def MakeMove(self): - """ Randomly moves any moveable piece, or prints "NO_MOVE" if there are none """ - #TODO: Over-ride this function in base classes with more complex move behaviour - - #sys.stderr.write("BasicAI MakeMove here...\n") - #self.debugPrintBoard() - - if len(self.units) <= 0: - return False - - index = random.randint(0, len(self.units)-1) - startIndex = index - - directions = ("UP", "DOWN", "LEFT", "RIGHT") - while True: - piece = self.units[index] - if piece != None and piece.mobile(): - dirIndex = random.randint(0, len(directions)-1) - startDirIndex = dirIndex - - while True: - #sys.stderr.write("Trying index " + str(dirIndex) + "\n") - p = move(piece.x, piece.y, directions[dirIndex]) - if p[0] >= 0 and p[0] < self.width and p[1] >= 0 and p[1] < self.height: - target = self.board[p[0]][p[1]] - if target == None or (target.colour != piece.colour and target.colour != "NONE" and target.colour != "BOTH"): - print str(piece.x) + " " + str(piece.y) + " "+directions[dirIndex] - return True - dirIndex = (dirIndex + 1) % len(directions) - if startDirIndex == dirIndex: - break - - index = (index + 1) % len(self.units) - if startIndex == index: - print "NO_MOVE" - return True - - - def ReadBoard(self): - """ Reads in the board. - On the very first turn, sets up the self.board structure - On subsequent turns, the board is simply read, but the self.board structure is not updated here. - """ - #sys.stderr.write("BasicAI ReadBoard here...\n"); - for y in range(0,self.height): - row = sys.stdin.readline().strip() - if len(row) < self.width: - sys.stderr.write("Row has length " + str(len(row)) + " vs " + str(self.width) + "\n") - return False - for x in range(0,self.width): - if self.turn == 0: - if row[x] == '.': - pass - elif row[x] == '#': - self.board[x][y] = Piece(oppositeColour(self.colour), '?',x,y) - self.enemyUnits.append(self.board[x][y]) - elif row[x] == '+': - self.board[x][y] = Piece("NONE", '+', x, y) - else: - self.board[x][y] = Piece(self.colour, row[x],x,y) - self.units.append(self.board[x][y]) - else: - pass - return True - - - def InterpretResult(self): - """ Interprets the result of a move, and updates the board. - The very first move is ignored. - On subsequent moves, the self.board structure is updated - """ - #sys.stderr.write("BasicAI InterpretResult here...\n") - result = sys.stdin.readline().split(' ') - #sys.stderr.write(" Read status line \"" + str(result) + "\"\n") - if self.turn == 0: - return True - - if result[0].strip() == "QUIT": #Make sure we exit when the manager tells us to! - return False - - if result[0].strip() == "NO_MOVE": #No move was made, don't need to update anything - return True - - if len(result) < 4: #Should be at least 4 tokens (X Y DIRECTION OUTCOME) in any other case - return False - - x = int(result[0].strip()) - y = int(result[1].strip()) - - - #sys.stderr.write(" Board position " + str(x) + " " + str(y) + " is OK!\n") - - direction = result[2].strip() - outcome = result[3].strip() - - p = move(x,y,direction) - - - - if outcome == "OK": - self.board[p[0]][p[1]] = self.board[x][y] - self.board[x][y].x = p[0] - self.board[x][y].y = p[1] - - self.board[x][y] = None - elif outcome == "KILLS": - if self.board[p[0]][p[1]] == None: - return False - - if self.board[p[0]][p[1]].colour == self.colour: - self.units.remove(self.board[p[0]][p[1]]) - elif self.board[p[0]][p[1]].colour == oppositeColour(self.colour): - self.enemyUnits.remove(self.board[p[0]][p[1]]) - - self.board[x][y].x = p[0] - self.board[x][y].y = p[1] - - - self.board[p[0]][p[1]] = self.board[x][y] - self.board[x][y].rank = result[4].strip() - - self.board[x][y] = None - - elif outcome == "DIES": - if self.board[p[0]][p[1]] == None: - return False - - if self.board[x][y].colour == self.colour: - self.units.remove(self.board[x][y]) - elif self.board[x][y].colour == oppositeColour(self.colour): - self.enemyUnits.remove(self.board[x][y]) - - self.board[p[0]][p[1]].rank = result[5].strip() - self.board[x][y] = None - elif outcome == "BOTHDIE": - if self.board[p[0]][p[1]] == None: - return False - - - if self.board[x][y].colour == self.colour: - self.units.remove(self.board[x][y]) - elif self.board[x][y].colour == oppositeColour(self.colour): - self.enemyUnits.remove(self.board[x][y]) - if self.board[p[0]][p[1]].colour == self.colour: - self.units.remove(self.board[p[0]][p[1]]) - elif self.board[p[0]][p[1]].colour == oppositeColour(self.colour): - self.enemyUnits.remove(self.board[p[0]][p[1]]) - - - self.board[p[0]][p[1]] = None - self.board[x][y] = None - elif outcome == "FLAG": - #sys.stderr.write(" Game over!\n") - return False - elif outcome == "ILLEGAL": - #sys.stderr.write(" Illegal move!\n") - return False - else: - #sys.stderr.write(" Don't understand outcome \"" + outcome + "\"!\n"); - return False - - #sys.stderr.write(" Completed interpreting move!\n"); - return True - - def debugPrintBoard(self): - """ For debug purposes only. Prints the board to stderr. - Does not indicate difference between allied and enemy pieces - Unknown (enemy) pieces are shown as '?' - """ - for y in range(0, self.height): - for x in range(0, self.width): - if self.board[x][y] == None: - sys.stderr.write("."); - else: - sys.stderr.write(str(self.board[x][y].rank)); - sys.stderr.write("\n") - -#basicAI = BasicAI() -#if basicAI.Setup(): -# while basicAI.MoveCycle(): -# pass - diff --git a/home/progcomp/agents/basic_python/info b/home/progcomp/agents/basic_python/info deleted file mode 100644 index 9260726..0000000 --- a/home/progcomp/agents/basic_python/info +++ /dev/null @@ -1 +0,0 @@ -basic_python.py diff --git a/home/progcomp/agents/basic_python/run.py b/home/progcomp/agents/basic_python/run.py deleted file mode 100755 index 80b4cfe..0000000 --- a/home/progcomp/agents/basic_python/run.py +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/python -u - -from basic_python import * - -basicAI = BasicAI() -if basicAI.Setup(): - while basicAI.MoveCycle(): - pass diff --git a/home/progcomp/judge/manager/Makefile b/home/progcomp/judge/manager/Makefile deleted file mode 100644 index fe319df..0000000 --- a/home/progcomp/judge/manager/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -#Makefile for Stratego - -CPP = g++ -Wall -pedantic -lSDL -lGL -g -OBJ = main.o controller.o ai_controller.o human_controller.o program.o thread_util.o stratego.o graphics.o game.o - -BIN = stratego - - - -$(BIN) : $(OBJ) - $(CPP) -o $(BIN) $(OBJ) - - - - -%.o : %.cpp %.h - $(CPP) -c $< - -clean : - $(RM) $(BIN) $(OBJ) $(LINKOBJ) - -clean_full: #cleans up all backup files - $(RM) $(BIN) $(OBJ) $(LINKOBJ) - $(RM) *.*~ - $(RM) *~ - - diff --git a/home/progcomp/judge/manager/ai_controller.cpp b/home/progcomp/judge/manager/ai_controller.cpp deleted file mode 100644 index 40ee3df..0000000 --- a/home/progcomp/judge/manager/ai_controller.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include - -#include "game.h" -#include "stratego.h" - -#include "ai_controller.h" - -using namespace std; - - -/** - * Queries the AI program to setup its pieces. Stores the setup in a st - * @implements Controller::QuerySetup - * @param - * @returns A MovementResult - */ - -MovementResult AI_Controller::QuerySetup(const char * opponentName, std::string setup[]) -{ - switch (colour) - { - case Piece::RED: - if (!SendMessage("RED %s %d %d", opponentName, Game::theGame->theBoard.Width(), Game::theGame->theBoard.Height())) - return MovementResult::BAD_RESPONSE; - break; - case Piece::BLUE: - if (!SendMessage("BLUE %s %d %d", opponentName, Game::theGame->theBoard.Width(), Game::theGame->theBoard.Height())) - return MovementResult::BAD_RESPONSE; - break; - case Piece::NONE: - case Piece::BOTH: - return MovementResult::COLOUR_ERROR; - break; - } - - for (int y = 0; y < 4; ++y) - { - if (!GetMessage(setup[y], timeout)) - return MovementResult::BAD_RESPONSE; - } - - return MovementResult::OK; -} - - -/** - * Queries the AI program to make a move - * @implements Controller::QueryMove - * @param buffer String which stores the AI program's response - * @returns A MovementResult which will be MovementResult::OK if a move was made, or MovementResult::NO_MOVE if the AI did not respond - */ -MovementResult AI_Controller::QueryMove(string & buffer) -{ - if (!Running()) - return MovementResult::NO_MOVE; //AI has quit - Game::theGame->theBoard.Print(output, colour); - - if (!GetMessage(buffer,timeout)) - { - return MovementResult::NO_MOVE; //AI did not respond (within the timeout). It will lose by default. - } - return MovementResult::OK; //Got the message -} - diff --git a/home/progcomp/judge/manager/ai_controller.h b/home/progcomp/judge/manager/ai_controller.h deleted file mode 100644 index 7d62591..0000000 --- a/home/progcomp/judge/manager/ai_controller.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef AI_CONTROLLER_H -#define AI_CONTROLLER_H - -#include "controller.h" -#include "program.h" - -/** - * Class to control an AI program playing Stratego - * Inherits mostly from Program - */ -class AI_Controller : public Controller, private Program -{ - public: - AI_Controller(const Piece::Colour & newColour, const char * executablePath, const double newTimeout = 2.0) : Controller(newColour, executablePath), Program(executablePath), timeout(newTimeout) {} - virtual ~AI_Controller() {} - - - - virtual MovementResult QuerySetup(const char * opponentName,std::string setup[]); - virtual MovementResult QueryMove(std::string & buffer); - - virtual void Message(const char * message) {Program::SendMessage(message);} - - virtual bool Valid() const {return Program::Running();} - - - private: - const double timeout; //Timeout in seconds for messages from the AI Program - -}; - -#endif //AI_CONTROLLER_H diff --git a/home/progcomp/judge/manager/array.h b/home/progcomp/judge/manager/array.h deleted file mode 100644 index 2a1d29f..0000000 --- a/home/progcomp/judge/manager/array.h +++ /dev/null @@ -1,128 +0,0 @@ -#ifndef ARRAY_H -#define ARRAY_H - -typedef long unsigned int LUint; -#include - -template -class Array -{ - public: - Array() : start(NULL), size(0), reserved(0) {} - Array(LUint newSize) : start(new T[newSize]), size(newSize), reserved(newSize) {} - ~Array() {delete [] start;} - - void Empty() {size = 0;} - void Add(const T & add); - void Reserve(LUint reserve); - void Resize(LUint newSize); - void RemoveBack(); - - LUint Size() const {return size;} - LUint Capacity() const {return reserved;} - - void operator=(const Array & equ); - bool operator==(const Array & equ) const; - bool operator!=(const Array & equ) const {return !operator==(equ);} - - class Iterator - { - public: - Iterator(const Array & from) : parent(from), index(0) {} - Iterator(const Iterator & cpy) : parent(cpy.parent), index(cpy.index) {} - ~Iterator() {} - - bool Good() const {return index < parent.Size();} - - T & operator*() const {return parent.start[index];} - - void operator++() {++index;} - void operator--() {--index;} - void operator++(int) {operator++();} - void operator--(int) {operator--();} - Iterator & operator+=(int amount) {index += amount;} - Iterator & operator-=(int amount) {index -= amount;} - Iterator operator+(int amount) {return Iterator(*this) += amount;} - Iterator operator-(int amount) {return Iterator(*this) -= amount;} - - void operator=(const Iterator & set) {index = set.index;} - bool operator==(const Iterator & set) {return (&parent == &(set.parent) && index == set.index);} - private: - const Array & parent; - LUint index; - }; - - Iterator First() const {return Iterator(*this);} - Iterator Last() const {return Iterator(*this) -= (size-1);} - - - T & operator[](LUint at) const - { - #ifdef DEBUGALL - printf(" Array::operator[] - called for index %lu/%lu (reserved %lu)\n", at, size, reserved); - - #endif //DEBUG - assert(at < size); return start[at]; - } - - int Find(const T & find) - { - - LUint result; - for (result = 0; result < size; result++) - { - //printf("%p %lu/%lu\n", (void*)(start), result, size); - if (start[result] == find) - return (int)(result); - } - return -1; - } - - private: - T * start; - LUint size; LUint reserved; -}; - -template void Array::Add(const T & add) -{ - if (size >= reserved) - { - T * old = start; - reserved *= 2; ++reserved; - start = new T[reserved]; - for (LUint ii=0; ii < size; ++ii) - start[ii] = old[ii]; - delete [] old; - } - start[size++] = add; -} - -template void Array::RemoveBack() -{ - if (size > 0) - --size; -} - -template void Array::Resize(LUint newSize) -{ - T * old = start; - start = new T[newSize]; - for (LUint ii=0; ii < size; ++ii) - start[ii] = old[ii]; - size = newSize; reserved = newSize; - delete [] old; -} - -template void Array::Reserve(LUint newReserve) -{ - if (newReserve > reserved) - { - T * old = start; - start = new T[newReserve]; - for (LUint ii=0; ii < size; ++ii) - start[ii] = old[ii]; - reserved = newReserve; - } -} - -#endif //ARRAY_H diff --git a/home/progcomp/judge/manager/controller.cpp b/home/progcomp/judge/manager/controller.cpp deleted file mode 100644 index 1b0a35d..0000000 --- a/home/progcomp/judge/manager/controller.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include "controller.h" - -#include -#include "game.h" - -using namespace std; - -/** - * Queries the player to setup their pieces - * - */ - -MovementResult Controller::Setup(const char * opponentName) -{ - string setup[4] = {"","","",""}; - MovementResult query = this->QuerySetup(opponentName, setup); - if (query != MovementResult::OK) - return query; - - - - int usedUnits[(int)(Piece::BOMB)]; - for (int ii = 0; ii <= (int)(Piece::BOMB); ++ii) - usedUnits[ii] = 0; - - int yStart = 0; - switch (colour) - { - case Piece::RED: - yStart = 0; - break; - case Piece::BLUE: - yStart = Game::theGame->theBoard.Height()-4; - break; - default: - return MovementResult::COLOUR_ERROR; - break; - } - - - for (int y = 0; y < 4; ++y) - { - if ((int)setup[y].length() != Game::theGame->theBoard.Width()) - return MovementResult::BAD_RESPONSE; - - for (int x = 0; x < Game::theGame->theBoard.Width(); ++x) - { - Piece::Type type = Piece::GetType(setup[y][x]); - if (type != Piece::NOTHING) - { - usedUnits[(int)(type)]++; - if (usedUnits[type] > Piece::maxUnits[(int)type]) - { - //fprintf(stderr, "Too many units of type %c\n", Piece::tokens[(int)(type)]); - return MovementResult::BAD_RESPONSE; - } - Game::theGame->theBoard.AddPiece(x, yStart+y, type, colour); - } - } - } - if (usedUnits[(int)Piece::FLAG] <= 0) - { - return MovementResult::BAD_RESPONSE; //You need to include a flag! - } - - return MovementResult::OK; - -} - - -/** - * Queries the player to respond to a state of Game::theGame->theBoard - * @param buffer String which is used to store the player's responses - * @returns The result of the response and/or move if made - */ -MovementResult Controller::MakeMove(string & buffer) -{ - buffer.clear(); - MovementResult query = this->QueryMove(buffer); - if (query != MovementResult::OK) - return query; - - if (buffer == "NO_MOVE") - { - buffer += " OK"; - return MovementResult::OK; - } - if (buffer == "SURRENDER") - { - buffer += " OK"; - return MovementResult::SURRENDER; - } - - int x; int y; string direction=""; - stringstream s(buffer); - s >> x; - s >> y; - - - s >> direction; - Board::Direction dir; - if (direction == "UP") - { - dir = Board::UP; - } - else if (direction == "DOWN") - { - dir = Board::DOWN; - } - else if (direction == "LEFT") - { - dir = Board::LEFT; - } - else if (direction == "RIGHT") - { - dir = Board::RIGHT; - } - else - { - if (Game::theGame->allowIllegalMoves) - return MovementResult::OK; - else - return MovementResult::BAD_RESPONSE; //Player gave bogus direction - it will lose by default. - } - - int multiplier = 1; - if (s.peek() != EOF) - s >> multiplier; - MovementResult moveResult = Game::theGame->theBoard.MovePiece(x, y, dir, multiplier, colour); - - s.clear(); s.str(""); - - //I stored the ranks in the wrong order; rank 1 is the marshal, 2 is the general etc... - //So I am reversing them in the output... great work - s << Piece::tokens[(int)(moveResult.attackerRank)] << " " << Piece::tokens[(int)(moveResult.defenderRank)]; - switch (moveResult.type) - { - case MovementResult::OK: - buffer += " OK"; - break; - case MovementResult::VICTORY: - buffer += " FLAG"; - break; - case MovementResult::KILLS: - buffer += " KILLS "; - buffer += s.str(); - - break; - case MovementResult::DIES: - buffer += " DIES "; - buffer += s.str(); - break; - case MovementResult::BOTH_DIE: - buffer += " BOTHDIE "; - buffer += s.str(); - break; - default: - buffer += " ILLEGAL"; - break; - - } - - - if (!Board::LegalResult(moveResult)) - { - - if (Game::theGame->allowIllegalMoves) - { - - return MovementResult::OK; //HACK - Illegal results returned as legal! (Move not made) - } - else if (this->HumanController()) //Cut human controllers some slack and let them try again... - { - //Yes, checking type of object is "not the C++ way" - // But sometimes its bloody useful to know!!! - Message("Bad move: \'" + buffer + "\' <- Please try again!"); - buffer = ""; - return this->MakeMove(buffer); - } - } - - return moveResult; - -} diff --git a/home/progcomp/judge/manager/controller.h b/home/progcomp/judge/manager/controller.h deleted file mode 100644 index 55c233d..0000000 --- a/home/progcomp/judge/manager/controller.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef CONTROLLER_H -#define CONTROLLER_H - -#include "stratego.h" -#include - -/** - * Class to control a player for Stratego - * Abstract base class - */ - -class Controller -{ - public: - Controller(const Piece::Colour & newColour, const char * newName = "no-name") : colour(newColour), name(newName) {} - virtual ~Controller() {} - - MovementResult Setup(const char * opponentName); - - MovementResult MakeMove(std::string & buffer); - - virtual bool HumanController() const {return false;} //Hacky... overrides in human_controller... avoids having to use run time type info - - void Message(const std::string & buffer) {Message(buffer.c_str());} - virtual void Message(const char * string) = 0; - - virtual MovementResult QuerySetup(const char * opponentName, std::string setup[]) = 0; - virtual MovementResult QueryMove(std::string & buffer) = 0; - virtual bool Valid() const {return true;} - - const Piece::Colour colour; - - std::string name; - - -}; - - - - - -#endif //CONTROLLER_H - - diff --git a/home/progcomp/judge/manager/game.cpp b/home/progcomp/judge/manager/game.cpp deleted file mode 100644 index 26369d3..0000000 --- a/home/progcomp/judge/manager/game.cpp +++ /dev/null @@ -1,585 +0,0 @@ -#include "game.h" - -using namespace std; - - - -Game* Game::theGame = NULL; -bool Game::gameCreated = false; - -Game::Game(const char * redPath, const char * bluePath, const bool enableGraphics, double newStallTime, const bool allowIllegal, FILE * newLog, const Piece::Colour & newReveal, int newMaxTurns, bool newPrintBoard) : red(NULL), blue(NULL), turn(Piece::RED), theBoard(10,10), graphicsEnabled(enableGraphics), stallTime(newStallTime), allowIllegalMoves(allowIllegal), log(newLog), reveal(newReveal), turnCount(0), input(NULL), maxTurns(newMaxTurns), printBoard(newPrintBoard) -{ - gameCreated = false; - if (gameCreated) - { - fprintf(stderr, "Game::Game - Error - Tried to create more than one Game!\n"); - exit(EXIT_FAILURE); - } - gameCreated = true; - Game::theGame = this; - signal(SIGPIPE, Game::HandleBrokenPipe); - - - if (graphicsEnabled && (!Graphics::Initialised())) - Graphics::Initialise("Stratego", theBoard.Width()*32, theBoard.Height()*32); - - if (strcmp(redPath, "human") == 0) - red = new Human_Controller(Piece::RED, graphicsEnabled); - else - red = new AI_Controller(Piece::RED, redPath); - - - if (strcmp(bluePath, "human") == 0) - blue = new Human_Controller(Piece::BLUE, graphicsEnabled); - else - blue = new AI_Controller(Piece::BLUE, bluePath); - - -} - -Game::Game(const char * fromFile, const bool enableGraphics, double newStallTime, const bool allowIllegal, FILE * newLog, const Piece::Colour & newReveal, int newMaxTurns, bool newPrintBoard) : red(NULL), blue(NULL), turn(Piece::RED), theBoard(10,10), graphicsEnabled(enableGraphics), stallTime(newStallTime), allowIllegalMoves(allowIllegal), log(newLog), reveal(newReveal), turnCount(0), input(NULL), maxTurns(newMaxTurns), printBoard(newPrintBoard) -{ - gameCreated = false; - if (gameCreated) - { - fprintf(stderr, "Game::Game - Error - Tried to create more than one Game!\n"); - exit(EXIT_FAILURE); - } - gameCreated = true; - Game::theGame = this; - signal(SIGPIPE, Game::HandleBrokenPipe); - - - if (graphicsEnabled && (!Graphics::Initialised())) - Graphics::Initialise("Stratego", theBoard.Width()*32, theBoard.Height()*32); - - input = fopen(fromFile, "r"); - - red = new FileController(Piece::RED, input); - blue = new FileController(Piece::BLUE, input); - - -} - -Game::~Game() -{ - - delete red; - delete blue; - - if (log != NULL && log != stdout && log != stderr) - fclose(log); - - if (input != NULL && input != stdin) - fclose(input); -} - -/** - * Attempts to setup the board and controllers - * @param redName the name of the red AI - * @param blueName the name of the blue AI - * @returns A colour, indicating if there were any errors - Piece::NONE indicates no errors - Piece::BOTH indicates errors with both AI - Piece::RED / Piece::BLUE indicates an error with only one of the two AI - */ -Piece::Colour Game::Setup(const char * redName, const char * blueName) -{ - - if (!red->Valid()) - { - logMessage("Controller for Player RED is invalid!\n"); - } - if (!blue->Valid()) - { - logMessage("Controller for Player BLUE is invalid!\n"); - } - if (!red->Valid()) - { - if (!blue->Valid()) - return Piece::BOTH; - return Piece::RED; - } - else if (!blue->Valid()) - { - return Piece::BLUE; - } - - for (int y = 4; y < 6; ++y) - { - for (int x = 2; x < 4; ++x) - { - theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE); - } - for (int x = 6; x < 8; ++x) - { - theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE); - } - } - - - MovementResult redSetup = red->Setup(blueName); - MovementResult blueSetup = blue->Setup(redName); - - - Piece::Colour result = Piece::NONE; - if (redSetup != MovementResult::OK) - { - if (blueSetup != MovementResult::OK) - { - logMessage("BOTH players give invalid setup!\n"); - result = Piece::BOTH; - } - else - { - logMessage("Player RED gave an invalid setup!\n"); - result = Piece::RED; - } - - } - else if (blueSetup != MovementResult::OK) - { - logMessage("Player BLUE gave an invalid setup!\n"); - result = Piece::BLUE; - } - - logMessage("%s RED SETUP\n", red->name.c_str()); - for (int y=0; y < 4; ++y) - { - for (int x=0; x < theBoard.Width(); ++x) - { - if (theBoard.GetPiece(x, y) != NULL) - logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, y)->type)]); - else - logMessage("."); - } - logMessage("\n"); - } - - logMessage("%s BLUE SETUP\n", blue->name.c_str()); - for (int y=0; y < 4; ++y) - { - for (int x=0; x < theBoard.Width(); ++x) - logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, theBoard.Height()-4 + y)->type)]); - logMessage("\n"); - } - - - return result; - -} - -void Game::Wait(double wait) -{ - if (wait <= 0) - return; - - TimerThread timer(wait*1000000); //Wait in seconds - timer.Start(); - - if (!graphicsEnabled) - { - while (!timer.Finished()); - timer.Stop(); - return; - } - - - while (!timer.Finished()) - { - SDL_Event event; - while (SDL_PollEvent(&event)) - { - switch (event.type) - { - case SDL_QUIT: - timer.Stop(); - exit(EXIT_SUCCESS); - break; - } - } - } - timer.Stop(); - -} - -void Game::HandleBrokenPipe(int sig) -{ - if (theGame == NULL) - { - fprintf(stderr, "ERROR - Recieved SIGPIPE during game exit!\n"); - exit(EXIT_FAILURE); - } - if (theGame->turn == Piece::RED) - { - theGame->logMessage("Game ends on RED's turn - REASON: "); - theGame->blue->Message("DEFAULT"); - } - else if (theGame->turn == Piece::BLUE) - { - - theGame->logMessage("Game ends on BLUE's turn - REASON: "); - theGame->red->Message("DEFAULT"); - } - else - { - theGame->logMessage("Game ends on ERROR's turn - REASON: "); - - } - - theGame->logMessage("SIGPIPE - Broken pipe (AI program may have segfaulted)\n"); - - if (Game::theGame->printBoard) - Game::theGame->theBoard.PrintPretty(stdout, Piece::BOTH); - - if (Game::theGame->graphicsEnabled && theGame->log == stdout) - { - theGame->logMessage("CLOSE WINDOW TO EXIT\n"); - Game::theGame->theBoard.Draw(Piece::BOTH); - while (true) - { - SDL_Event event; - while (SDL_PollEvent(&event)) - { - switch (event.type) - { - case SDL_QUIT: - exit(EXIT_SUCCESS); - break; - } - } - } - } - else - { - if (theGame->log == stdout) - { - theGame->logMessage( "PRESS ENTER TO EXIT\n"); - theGame->theBoard.Print(theGame->log); - while (fgetc(stdin) != '\n'); - } - } - - - exit(EXIT_SUCCESS); -} - -void Game::PrintEndMessage(const MovementResult & result) -{ - if (turnCount == 0) - { - logMessage("Game ends in the SETUP phase - REASON: "); - } - else - { - if (turn == Piece::RED) - { - logMessage("Game ends on RED's turn - REASON: "); - } - else if (turn == Piece::BLUE) - { - logMessage("Game ends on BLUE's turn - REASON: "); - } - else - { - logMessage("Game ends on ERROR's turn - REASON: "); - - } - } - switch (result.type) - { - case MovementResult::OK: - logMessage("Status returned OK, unsure why game halted...\n"); - break; - case MovementResult::DIES: - logMessage("Status returned DIES, unsure why game halted...\n"); - break; - case MovementResult::KILLS: - logMessage("Status returned KILLS, unsure why game halted...\n"); - break; - case MovementResult::BOTH_DIE: - logMessage("Status returned BOTH_DIE, unsure why game halted...\n"); - break; - case MovementResult::NO_BOARD: - logMessage("Board does not exit?!\n"); - break; - case MovementResult::INVALID_POSITION: - logMessage("Coords outside board\n"); - break; - case MovementResult::NO_SELECTION: - logMessage("Move does not select a piece\n"); - break; - case MovementResult::NOT_YOUR_UNIT: - logMessage("Selected piece belongs to other player\n"); - break; - case MovementResult::IMMOBILE_UNIT: - logMessage("Selected piece is not mobile (FLAG or BOMB)\n"); - break; - case MovementResult::INVALID_DIRECTION: - logMessage("Selected unit cannot move that way\n"); - break; - case MovementResult::POSITION_FULL: - logMessage("Attempted move into square occupied by neutral or allied piece\n"); - break; - case MovementResult::VICTORY: - logMessage("Captured the flag\n"); - break; - case MovementResult::BAD_RESPONSE: - logMessage("Unintelligable response\n"); - break; - case MovementResult::NO_MOVE: - logMessage("Did not make a move (may have exited)\n"); - break; - case MovementResult::COLOUR_ERROR: - logMessage("Internal controller error - COLOUR_ERROR\n"); - break; - case MovementResult::ERROR: - logMessage("Internal controller error - Unspecified ERROR\n"); - break; - case MovementResult::DRAW_DEFAULT: - logMessage("Game declared a draw after %d turns\n", turnCount); - break; - case MovementResult::DRAW: - logMessage("Game declared a draw because neither player has mobile pieces\n"); - break; - case MovementResult::SURRENDER: - logMessage("This player has surrendered!\n"); - break; - case MovementResult::BAD_SETUP: - switch (turn) - { - case Piece::RED: - logMessage("An illegal setup was made by RED\n"); - break; - case Piece::BLUE: - logMessage("An illegal setup was made by BLUE\n"); - break; - case Piece::BOTH: - logMessage("An illegal setup was made by BOTH players\n"); - break; - case Piece::NONE: - logMessage("Unknown internal error.\n"); - break; - } - break; - - } - - if (printBoard) - { - system("clear"); - fprintf(stdout, "%d Final State\n", turnCount); - theBoard.PrintPretty(stdout, Piece::BOTH); - fprintf(stdout, "\n"); - } - if (graphicsEnabled && log == stdout) - { - logMessage("CLOSE WINDOW TO EXIT\n"); - theBoard.Draw(Piece::BOTH); - while (true) - { - SDL_Event event; - while (SDL_PollEvent(&event)) - { - switch (event.type) - { - case SDL_QUIT: - exit(EXIT_SUCCESS); - break; - } - } - } - } - else - { - if (log == stdout) - { - logMessage("PRESS ENTER TO EXIT\n"); - while (fgetc(stdin) != '\n'); - } - } - -} - - - -MovementResult Game::Play() -{ - - MovementResult result = MovementResult::OK; - turnCount = 1; - string buffer; - - Piece::Colour toReveal = reveal; - - - - - - red->Message("START"); - - - - while (!Board::HaltResult(result) && (turnCount < maxTurns || maxTurns < 0)) - { - if (red->HumanController()) - toReveal = Piece::RED; - if (printBoard) - { - system("clear"); - if (turnCount == 0) - fprintf(stdout, "START:\n"); - else - fprintf(stdout, "%d BLUE:\n", turnCount); - theBoard.PrintPretty(stdout, toReveal); - fprintf(stdout, "\n\n"); - } - - if (graphicsEnabled) - theBoard.Draw(toReveal); - - turn = Piece::RED; - logMessage( "%d RED: ", turnCount); - result = red->MakeMove(buffer); - red->Message(buffer); - blue->Message(buffer); - logMessage( "%s\n", buffer.c_str()); - if (Board::HaltResult(result)) - break; - - if (stallTime > 0) - Wait(stallTime); - else - ReadUserCommand(); - - if (blue->HumanController()) - toReveal = Piece::BLUE; - if (printBoard) - { - system("clear"); - fprintf(stdout, "%d RED:\n", turnCount); - theBoard.PrintPretty(stdout, toReveal); - fprintf(stdout, "\n\n"); - } - if (graphicsEnabled) - theBoard.Draw(toReveal); - - - - turn = Piece::BLUE; - logMessage( "%d BLU: ", turnCount); - result = blue->MakeMove(buffer); - blue->Message(buffer); - red->Message(buffer); - logMessage( "%s\n", buffer.c_str()); - - if (Board::HaltResult(result)) - break; - - - - - - if (stallTime > 0) - Wait(stallTime); - else - ReadUserCommand(); - - if (theBoard.MobilePieces(Piece::BOTH) == 0) - result = MovementResult::DRAW; - - ++turnCount; - } - - if ((maxTurns >= 0 && turnCount >= maxTurns) && result == MovementResult::OK) - { - result = MovementResult::DRAW_DEFAULT; - } - - - return result; - - - -} - -/** - * Logs a message to the game's log file if it exists - * @param format the format string - * @param additional parameters - printed using va_args - * @returns the result of vfprintf or a negative number if the log file does not exist - */ -int Game::logMessage(const char * format, ...) -{ - if (log == NULL) - return -666; - va_list ap; - va_start(ap, format); - - int result = vfprintf(log, format, ap); - va_end(ap); - - return result; -} - -/** - * Waits for a user command - * Currently ignores the command. - */ -void Game::ReadUserCommand() -{ - fprintf(stdout, "Waiting for user to press enter...\n"); - string command(""); - for (char c = fgetc(stdin); c != '\n' && (int)(c) != EOF; c = fgetc(stdin)) - { - command += c; - } -} - -MovementResult FileController::QuerySetup(const char * opponentName, std::string setup[]) -{ - - char c = fgetc(file); - name = ""; - while (c != ' ') - { - name += c; - c = fgetc(file); - } - - while (fgetc(file) != '\n'); - - for (int y = 0; y < 4; ++y) - { - setup[y] = ""; - for (int x = 0; x < Game::theGame->theBoard.Width(); ++x) - { - setup[y] += fgetc(file); - } - - if (fgetc(file) != '\n') - { - return MovementResult::BAD_RESPONSE; - } - } - return MovementResult::OK; - - -} - -MovementResult FileController::QueryMove(std::string & buffer) -{ - char buf[BUFSIZ]; - - fgets(buf, sizeof(buf), file); - char * s = (char*)(buf); - while (*s != ':' && *s != '\0') - ++s; - - s += 2; - - buffer = string(s); - return MovementResult::OK; -} - - diff --git a/home/progcomp/judge/manager/game.h b/home/progcomp/judge/manager/game.h deleted file mode 100644 index 1212b89..0000000 --- a/home/progcomp/judge/manager/game.h +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef MAIN_H -#define MAIN_H - -#include "stratego.h" -#include "ai_controller.h" -#include "human_controller.h" - - - -/** - * Class to manage the game - */ -class Game -{ - public: - Game(const char * redPath, const char * bluePath, const bool enableGraphics, double newStallTime = 1.0, const bool allowIllegal=false, FILE * newLog = NULL, const Piece::Colour & newRevealed = Piece::BOTH, int maxTurns = 5000, const bool printBoard = false); - Game(const char * fromFile, const bool enableGraphics, double newStallTime = 1.0, const bool allowIllegal=false, FILE * newLog = NULL, const Piece::Colour & newRevealed = Piece::BOTH, int maxTurns = 5000, const bool printBoard = false); - virtual ~Game(); - - - - void Wait(double wait); - - Piece::Colour Setup(const char * redName, const char * blueName); - MovementResult Play(); - void PrintEndMessage(const MovementResult & result); - - - static void HandleBrokenPipe(int signal); - void ReadUserCommand(); - - const Piece::Colour Turn() const {return turn;} - void ForceTurn(const Piece::Colour & newTurn) {turn = newTurn;} - int TurnCount() const {return turnCount;} - - static Game * theGame; - public: - int logMessage(const char * format, ...); - FILE * GetLogFile() const {return log;} - Controller * red; - Controller * blue; - private: - Piece::Colour turn; - - public: - Board theBoard; - private: - const bool graphicsEnabled; - double stallTime; - public: - const bool allowIllegalMoves; - - private: - FILE * log; - - public: - const Piece::Colour reveal; - int turnCount; - - static bool gameCreated; - - FILE * input; - - int maxTurns; - const bool printBoard; - -}; - -class FileController : public Controller -{ - public: - FileController(const Piece::Colour & newColour, FILE * newFile) : Controller(newColour, "file"), file(newFile) {} - virtual ~FileController() {} - - virtual void Message(const char * string) {} //Don't send messages - virtual MovementResult QuerySetup(const char * opponentName, std::string setup[]); - virtual MovementResult QueryMove(std::string & buffer); - virtual bool Valid() const {return file != NULL;} - - private: - FILE * file; - - -}; - - - -#endif //MAIN_H diff --git a/home/progcomp/judge/manager/graphics.cpp b/home/progcomp/judge/manager/graphics.cpp deleted file mode 100644 index 5b708df..0000000 --- a/home/progcomp/judge/manager/graphics.cpp +++ /dev/null @@ -1,449 +0,0 @@ -#include "graphics.h" -#include -#include -#include - - -#undef DEBUG -//#define DEBUG - -std::list Graphics::allTextures = std::list(); -Screen * Graphics::screen = NULL; - -int Graphics::screenWidth = 0; -int Graphics::screenHeight = 0; -bool Graphics::initialised = false; - -using namespace std; - -Texture::Texture(const char * filename, bool newDrawCentred) : surface(NULL), texture(0), drawCentred(newDrawCentred) -{ - #ifdef DEBUG - printf("Texture::Texture - loading \"%s\".\n", filename); - #endif //DEBUG - - surface = Graphics::LoadTextureBMP(filename); - if (surface == NULL) - { - fprintf(stderr, "Texture::Texture - Could not open texture from file \"%s\"! ABORT\n", filename); - exit(EXIT_FAILURE); - } - - GLenum texture_format; - GLint nOfColours = surface->format->BytesPerPixel; - switch (nOfColours) - { - case 4: //contains alpha - texture_format = (surface->format->Rmask == 0x000000FF) ? GL_RGBA : GL_BGRA; - break; - case 3: //does not contain alpha - texture_format = (surface->format->Rmask == 0x000000FF) ? GL_RGB : GL_BGR; - break; - default: - fprintf(stderr,"Texture::Texture - Could not understand SDL_Surface format (%d colours)! ABORT\n", nOfColours); - exit(EXIT_FAILURE); - break; - } - - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - glTexImage2D(GL_TEXTURE_2D, 0, nOfColours, surface->w, surface->h,0, texture_format, GL_UNSIGNED_BYTE, surface->pixels); - -} - -Texture::~Texture() -{ - #ifdef DEBUG - printf("Texture::~Texture - %p has been deleted. glDeleteTexture and SDL_FreeSurface here.\n", (void*)(this)); - #endif //DEBUG - glDeleteTextures(1, &texture); - //SDL_FreeSurface(surface); -} - -void Texture::DrawColour(int x, int y, double angle, double scale, Colour colour) -{ - if (scale > surface->w || scale > surface->h) - { - Graphics::DrawPixel(x/scale,y/scale,colour); - } - else - { - glColor3f(colour.r,colour.g,colour.b); - Draw(x,y,angle,scale); - glColor3f(1,1,1); - } -} - -void Texture::Draw(int x, int y, double angle , double scale ) -{ - //Draws the CENTRE of the texture at x, y, rotated by angle - - #ifdef DEBUG - printf(" Texture::Draw - Drawing %p at (%d, %d) ; angle %2f ; scale % 2f\n", (void*)(this), x, y, angle, scale); - #endif //DEBUG - - //if (x/scale < 0 || x/scale > Graphics::ScreenWidth() || y/scale < 0 || y/scale > Graphics::ScreenHeight() ) - // return; - - glPushMatrix(); //NOT deprecated - - - glTranslatef(x/scale, y/scale,0); - - if (scale > surface->w || scale > surface->h) - { - Graphics::DrawPixel(0,0, Colour(255,255,255)); - } - else - { - glRotated(angle, 0, 0, 1); - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, texture); - glBegin(GL_QUADS); - - //scale /= 2; - if (drawCentred) - { - glTexCoord2i(0,0); glVertex3f(-0.5f/scale*surface->w ,-0.5f/scale*surface->h,0); //bottom left - glTexCoord2i(1,0); glVertex3f(0.5f/scale*surface->w,-0.5f/scale*surface->h,0); //bottom right - glTexCoord2i(1,1); glVertex3f(0.5f/scale*surface->w,0.5f/scale*surface->h,0); //top right - glTexCoord2i(0,1); glVertex3f(-0.5f/scale*surface->w,0.5f/scale*surface->h,0); //top left - } - else - { - glTexCoord2i(0,0); glVertex3f(0 ,0,0); //bottom left - glTexCoord2i(1,0); glVertex3f(1.0f/scale*surface->w,0,0); //bottom right - glTexCoord2i(1,1); glVertex3f(1.0f/scale*surface->w,1.0f/scale*surface->h,0); //top right - glTexCoord2i(0,1); glVertex3f(0,1.0f/scale*surface->h,0); //top left - } - - glEnd(); - glDisable(GL_TEXTURE_2D); - } - glPopMatrix(); - -} - - -Font::Font(const char * filename, int newWidth, int newHeight) : Texture(filename), width(newWidth), height(newHeight) -{ - -} - -Font::~Font() -{ - -} - -void Font::DrawText(const char * string, int x, int y, double angle, double scale) -{ - #ifdef DEBUG - printf("Font::DrawText - drawing \"%s\"\n", string); - #endif //DEBUG - glPushMatrix(); //NOT deprecated - glTranslatef(x, y,0); - glRotated(angle, 0, 0, 1); - - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, texture); - - - for (int ii=0; string[ii] != '\0'; ++ii) - { - if (string[ii] != ' ') - { - glPushMatrix(); - glTranslatef(ii*(float)(width)/(float)(scale),0,0); - - int index = (int)(string[ii]) - (int)('!'); - if (index < 0 || index > (int)('~') - (int)('!')) - index = (int)('~') - (int)('!') + 1; - - float start = (float)(((((float)(index))*((float)(width)))-3.0f)/((float)surface->w)); - float end = (float)(((((float)(index+1))*((float)(width)))-4.0f)/((float)surface->w)); - if (start < 0) {start = 0;} if (end > 1) {end = 1;} - glBegin(GL_QUADS); - glTexCoord2f(start,0); glVertex3f(-0.5f/scale*width ,-0.5f/scale*height,0); //bottom left - glTexCoord2f(end,0); glVertex3f(0.5f/scale*width,-0.5f/scale*height,0); //bottom right - glTexCoord2f(end,1); glVertex3f(0.5f/scale*width,0.5f/scale*height,0); //top right - glTexCoord2f(start,1); glVertex3f(-0.5f/scale*width,0.5f/scale*height,0); //top left - //printf("Index %d - Drawing %c - maps to %f->%f\n", index,string[ii],start,end); - - glEnd(); - glPopMatrix(); - } - } - - - glDisable(GL_TEXTURE_2D); - glPopMatrix(); - -} - - -void Graphics::Initialise(const char * caption, int newWidth, int newHeight) -{ - if (Initialised()) - { - std::cerr << "Graphics have already been initialised! Fatal Error\n"; - exit(EXIT_FAILURE); - } - screenWidth = newWidth; screenHeight = newHeight; - - if (SDL_Init(SDL_INIT_VIDEO) != 0) - { - std::cerr << "Couldn't init SDL!\n"; - exit(EXIT_FAILURE); - } - // atexit(Graphics::Destroy); BREAKS THINGS - - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); //According to sulix does not matter. (much) - - - - - screen = SDL_SetVideoMode(screenWidth,screenHeight, 32, SDL_OPENGL); - if ( screen == NULL ) - { - std::cerr << "Couldn't set " << screenWidth << "x" << screenHeight << "x32 video mode: " << SDL_GetError() << "\n"; - exit(EXIT_FAILURE); - } - - //COMES AFTER SETVIDEO MODE - glEnable(GL_TEXTURE_2D); - glClearColor(1,1,1,0); //Set clear colour (white) here - glViewport(0,0,screenWidth,screenHeight); //DOES matter - glClear(GL_COLOR_BUFFER_BIT); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0,screenWidth,screenHeight,0,-1,1); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glDisable(GL_DEPTH_TEST); - SDL_WM_SetCaption( caption, NULL); - - Graphics::initialised = true; - -} - -void Graphics::Destroy() -{ - list::iterator i(allTextures.begin()); - while (i != allTextures.end()) - { - SDL_FreeSurface((*i)); - ++i; - } - SDL_Quit(); -} - -SDL_Surface * Graphics::LoadTextureBMP(const char * file) -{ - SDL_Surface * tmp = SDL_LoadBMP(file); - if (tmp == NULL) - return NULL; - //assert(tmp != NULL); - - - if (Graphics::screen != NULL) - { - SDL_Surface * tex = SDL_DisplayFormat(tmp); - SDL_FreeSurface(tmp); - - allTextures.push_back(tex); - return tex; - } - return tmp; -} - -void Graphics::SaveTextureBMP(SDL_Surface * tex, const char * file) -{ - SDL_SaveBMP(tex, file); -} - - -void Graphics::DrawTexture(SDL_Surface * tex, int destX, int destY, int srcX, int srcY, int w, int h) -{ - if (w < 0) {w = tex->w - srcX;} - if (h < 0) {h = tex->h - srcY;} - Graphics::DrawTexture(screen, tex, destX, destY, srcX, srcY, w, h); -} - -void Graphics::DrawTexture(SDL_Surface * dest, SDL_Surface * tex, int destX, int destY, int srcX, int srcY, int width, int height) -{ - if ((destX < 0)||(destX >= dest->w)||(destY < 0)||(destY >= dest->h) - ||(srcX < 0)||(srcX >= tex->w)||(srcY < 0)||(srcY >= tex->h)) - return; - - assert(dest->format->BitsPerPixel == 32); - assert(tex->format->BitsPerPixel == 32); - - if (SDL_MUSTLOCK(tex)) - SDL_LockSurface(tex); - - if (SDL_MUSTLOCK(dest)) - SDL_LockSurface(dest); - - - - Colour transparent = Graphics::GetPixel(tex, srcX, srcY); - //printf("transparent from %d %d\n", srcX, srcY); - - for (int xOff = 0; xOff < width; xOff++) - - { - for (int yOff = 0; yOff < height; yOff++) - { - Colour nextColour = Graphics::GetPixel(tex, srcX+xOff, srcY+yOff); - if (nextColour != transparent) - { - Graphics::DrawPixel(dest, destX + xOff, destY + yOff, nextColour); - } - } - } - - if (SDL_MUSTLOCK(tex)) - SDL_UnlockSurface(tex); - - if (SDL_MUSTLOCK(dest)) - SDL_UnlockSurface(dest); - -} - -void Graphics::ClearScreen() -{ - //SDL_FillRect(screen, NULL ,Graphics::MakeColour(0,0,0)); - glClear(GL_COLOR_BUFFER_BIT); - -} - -void Graphics::UpdateScreen() -{ - SDL_GL_SwapBuffers(); - //SDL_Flip(screen); -} - -void Graphics::DrawPixel(int x, int y, Colour colour) -{ - DrawPixel(screen, x, y, colour); -} - -void Graphics::DrawPixel(SDL_Surface * dest, int x, int y, Colour colour) -{ - glBegin(GL_POINTS); - glColor4f(colour.r/255, colour.g/255, colour.b/255, colour.a); - glVertex2f(x, y); - glColor3f(1,1,1); - glEnd(); -} - -void Graphics::DrawGrid(int gridWidth, int gridHeight, Colour colour) -{ - for (int x = 0; x < screen->w; x+=gridWidth) - { - Graphics::DrawLine(x,0, x,screen->h - 1, colour); - } - for (int y = 0; y < screen->h; y+=gridHeight) - { - Graphics::DrawLine(0,y, screen->w - 1,y, colour); - } -} - -Uint8 Graphics::MakeColour(int R, int G, int B, int Alpha) -{ - return SDL_MapRGB(screen->format,R,G,B); -} - -Colour Graphics::GetPixel(int x, int y) -{ - return Graphics::GetPixel(screen, x, y); -} - -Colour Graphics::GetPixel(SDL_Surface * src, int x, int y) -{ - //Convert the pixels to 32 bit - Uint8 * pixels = (Uint8*)src->pixels; - //Get the requested pixel - - if (((y > 0)&&(y < src->h)) && ((x > 0)&&(x < src->w))) - return ConvertColour(pixels[ ( y * src->w ) + x ]); - return Colour(0,0,0,0); - -} - - - -void Graphics::DrawLine(int x1, int y1, int x2, int y2, Colour colour,double scale) -{ - //printf("DRAW LINE\n"); - glColor4f(colour.r/255,colour.g/255,colour.b/255,colour.a); - glBegin(GL_LINES); - glVertex2f(x1/scale, y1/scale); // origin of the line - glVertex2f(x2/scale, y2/scale); // ending point of the line - glColor3f(1,1,1); - glEnd(); - - -} - -void Graphics::DrawLineDashed(int x1, int y1, int x2, int y2, Colour colour, double scale) -{ - glLineStipple(8, 0xAAAA); - glEnable(GL_LINE_STIPPLE); - DrawLine(x1,y1,x2,y2,colour,scale); - glDisable(GL_LINE_STIPPLE); - glEnd(); -} - -void Graphics::DrawRectangle(int topX, int topY, int botX, int botY, Colour colour, double scale) -{ - glColor4f(colour.r/255,colour.g/255,colour.b/255,colour.a); - glBegin(GL_LINES); - glVertex2f(topX/scale, topY/scale); // origin of the rectangle - glVertex2f(botX/scale, topY/scale); // point1 - glVertex2f(botX/scale, botY/scale); // point2 - glVertex2f(topX/scale, botY/scale); // point3 - glVertex2f(topX/scale, topY/scale); // point4 - glEnd(); -} - -Colour Graphics::ConvertColour(Uint8 from) -{ - SDL_PixelFormat * fmt=screen->format; - Colour result; - - Uint8 temp; - - //Get red - temp=from&fmt->Rmask; /* Isolate red component */ - temp=temp>>fmt->Rshift;/* Shift it down to 8-bit */ - temp=temp<Rloss; /* Expand to a full 8-bit number */ - result.r = (float)(temp); - - //Get green - temp=from&fmt->Gmask; /* Isolate red component */ - temp=temp>>fmt->Gshift;/* Shift it down to 8-bit */ - temp=temp<Gloss; /* Expand to a full 8-bit number */ - result.g = (float)(temp); - - //Get blue - temp=from&fmt->Bmask; /* Isolate red component */ - temp=temp>>fmt->Bshift;/* Shift it down to 8-bit */ - temp=temp<Bloss; /* Expand to a full 8-bit number */ - result.b = (float)(temp); - - //Get alpha - temp=from&fmt->Amask; /* Isolate red component */ - temp=temp>>fmt->Ashift;/* Shift it down to 8-bit */ - temp=temp<Aloss; /* Expand to a full 8-bit number */ - result.a = (float)(temp); - return result; -} - - - - - - diff --git a/home/progcomp/judge/manager/graphics.h b/home/progcomp/judge/manager/graphics.h deleted file mode 100644 index f81bdb3..0000000 --- a/home/progcomp/judge/manager/graphics.h +++ /dev/null @@ -1,148 +0,0 @@ -#ifndef GRAPHICS_H -#define GRAPHICS_H - -#include -#include - - -#include -#include - - -typedef SDL_Surface Screen; -typedef SDL_Rect Rectangle; - - -typedef short unsigned int SUint; - -class Texture; -class Font; - - - -class Graphics -{ - public: - - class Colour - { - public: - Colour(float red=0, float green=0, float blue=0, float alpha=0) : r(red), g(green), b(blue), a(alpha) {} - Colour(const Colour & cpy) : r(cpy.r), g(cpy.g), b(cpy.b), a(cpy.a) {} - - Colour & operator=(const Colour & s) {r = s.r; g = s.g; b = s.b; a = s.a; return *this;} - bool operator==(const Colour & s) const {return (r == s.r && g == s.g && b == s.b && a == s.a);} - bool operator!=(const Colour & s) const {return !operator==(s);} - float r; - float g; - float b; - float a; - - }; - static int ScreenWidth() {return screenWidth;} - static int ScreenHeight() {return screenHeight;} - - static void Initialise(const char * caption, int width=640, int height=480); - static void Destroy(); - - static SDL_Surface * LoadTextureBMP(const char * file); - static void SaveTextureBMP(SDL_Surface * tex, const char * file); - - static void ClearScreen(); - static void UpdateScreen(); - static void DrawGrid(int gridWidth, int gridHeight, Colour colour); - static Uint8 MakeColour(int R, int G, int B, int Alpha = 0); - static Colour ConvertColour(Uint8 colour); - - static void DrawTexture(SDL_Surface * src, int destX, int destY, int srcX=0, int srcY=0, int width=-1, int height=-1); - static void DrawTexture(SDL_Surface * src, int destX, int destY, double rotate, double scale); - static void DrawPixel(int x, int y, Colour colour); - - static Colour GetPixel(int x, int y); - static void DrawLine(int x1, int y1, int x2, int y2, Colour colour, double scale=1.0); - static void DrawLineDashed(int x1, int y1, int x2, int y2, Colour colour, double scale=1.0); - static void DrawRectangle(int x1, int y1, int x2, int y2, Colour colour, double scale=1.0); - - static void GetColourData(SDL_Surface * src, std::vector * R, std::vector * G, std::vector * B, std::vector * A = NULL); - static void GetColourData(SDL_Surface * src, std::vector > * R, std::vector > * G, std::vector > * B, std::vector > * A = NULL); - - static void DrawColourData(int destX, int destY, std::vector * R, std::vector * G, std::vector * B, std::vector * A = NULL) {DrawColourData(screen, destX, destY, R, G, B, A);} - - static void DrawColourData(int destX, int destY, std::vector > * R, std::vector > * G, std::vector > * B, std::vector > * A = NULL) {DrawColourData(screen, destX, destY, R, G, B, A);} - - static SDL_Surface * TextureFromColours(std::vector * R, std::vector * G, std::vector * B, std::vector * A = NULL); - static SDL_Surface * TextureFromColours(std::vector > * R, std::vector > * G, std::vector > * B, std::vector > * A = NULL); - - static void Wait(int n) {SDL_Delay(n);} - - template - class TextureManager - { - public: - TextureManager() {} - virtual ~TextureManager() {} - - virtual Texture & operator[](const T & at) = 0; - }; - - static bool Initialised() {return initialised;} - - protected: - static void DrawColourData(SDL_Surface * dest, int destX, int destY, std::vector * R, std::vector * G, std::vector * B, std::vector * A = NULL); - static void DrawColourData(SDL_Surface * dest, int destX, int destY, std::vector > * R, std::vector > * G, std::vector > * B, std::vector > * A = NULL); - static void DrawTexture(SDL_Surface * dest, SDL_Surface * src, int srcX, int srcY, int destX, int destY, int width, int height); - static void DrawPixel(SDL_Surface * dest, int x, int y, Colour colour); - static Colour GetPixel(SDL_Surface * dest, int x, int y); - static void DrawLine(SDL_Surface * dest, int x1, int y1, int x2, int y2, Colour colour); - - private: - Graphics() {} - ~Graphics() {} - static std::list allTextures; - static Screen * screen; - - static int screenWidth; - static int screenHeight; - static bool initialised; - - -}; -typedef Graphics::Colour Colour; - -class Texture -{ - public: - Texture(const char * fileName, bool newDrawCentred = true); - virtual ~Texture(); - - void Draw(int x, int y, double angle=0, double scale=1); - void DrawColour(int x, int y, double angle, double scale, Colour colour); - - int width() const {return surface->w;} - int height() const {return surface->h;} - protected: - SDL_Surface * surface; - GLuint texture; - - private: - bool drawCentred; - -}; - -class Font : private Texture -{ - public: - Font(const char * fileName, int newWidth, int newHeight); - virtual ~Font(); - - void DrawTextColour(const char * string, int x, int y, double angle, double scale, Colour colour); - void DrawText(const char * string, int x, int y, double angle=0, double scale=1); - private: - int width; - int height; -}; - - -#endif //GRAPHICS_H - -//EOF diff --git a/home/progcomp/judge/manager/human_controller.cpp b/home/progcomp/judge/manager/human_controller.cpp deleted file mode 100644 index bf43e8d..0000000 --- a/home/progcomp/judge/manager/human_controller.cpp +++ /dev/null @@ -1,181 +0,0 @@ -#include "human_controller.h" - -#include "game.h" - -#include //Really I can't be bothered with fscanf any more - -using namespace std; - -MovementResult Human_Controller::QuerySetup(const char * opponentName, string setup[]) -{ - - static bool shownMessage = false; - if (!shownMessage) - { - if (graphicsEnabled) - fprintf(stdout, "WARNING: GUI not fully supported. You will be forced to use the default setup.\n"); - else - { - fprintf(stdout,"Enter %d x %d Setup grid\n", Game::theGame->theBoard.Width(), 4); - fprintf(stdout,"Please enter one line at a time.\n"); - fprintf(stdout, "You must place at least the Flag (%c). Use '%c' for empty squares.\n", Piece::tokens[(int)Piece::FLAG], Piece::tokens[(int)Piece::NOTHING]); - - switch (colour) - { - case Piece::RED: - fprintf(stdout, "You are RED and occupy the top 4 rows of the board.\n"); - fprintf(stdout, "NOTE: Enter \"DEFAULT\" to use the setup:\n"); - fprintf(stdout, "FB8sB479B8\nBB31555583\n6724898974\n967B669999\n"); - break; - case Piece::BLUE: - fprintf(stdout, "You are BLUE and occupy the bottom 4 rows of the board.\n"); - fprintf(stdout, "NOTE: Enter \"DEFAULT\" to use the setup:\n"); - fprintf(stdout, "967B669999\n6724898974\nBB31555583\nFB8sB479B8\n"); - break; - default: - fprintf(stdout, "WARNING: Unknown colour error! Please exit the game.\n"); - break; - } - } - - shownMessage = true; - } - - if (graphicsEnabled) - { - switch(colour) - { - case Piece::RED: - setup[0] = "FB8sB479B8"; - setup[1] = "BB31555583"; - setup[2] = "6724898974"; - setup[3] = "967B669999"; - break; - case Piece::BLUE: - setup[0] = "967B669999"; - setup[1] = "6724898974"; - setup[2] = "BB31555583"; - setup[3] = "FB8sB479B8"; - break; - default: - assert(false); - break; - } - return MovementResult::OK; - } - - for (int y = 0; y < 4; ++y) - { - cin >> setup[y]; - if (y == 0 && setup[0] == "DEFAULT") - { - switch(colour) - { - case Piece::RED: - setup[0] = "FB8sB479B8"; - setup[1] = "BB31555583"; - setup[2] = "6724898974"; - setup[3] = "967B669999"; - break; - case Piece::BLUE: - setup[0] = "967B669999"; - setup[1] = "6724898974"; - setup[2] = "BB31555583"; - setup[3] = "FB8sB479B8"; - break; - default: - assert(false); - break; - } - break; - } - } - assert(cin.get() == '\n'); - - return MovementResult::OK; -} - -MovementResult Human_Controller::QueryMove(string & buffer) -{ - static bool shownMessage = false; - if (!shownMessage) - { - if (!graphicsEnabled) - { - fprintf(stdout, "Please enter your move in the format:\n X Y DIRECTION [MULTIPLIER=1]\n"); - fprintf(stdout, "Where X and Y indicate the coordinates of the piece to move;\n DIRECTION is one of UP, DOWN, LEFT or RIGHT\n and MULTIPLIER is optional (and only valid for scouts (%c))\n", Piece::tokens[(int)(Piece::SCOUT)]); - - } - shownMessage = true; - } - - - - if (graphicsEnabled) - { - fprintf(stdout, "Click to move!\n"); - SDL_Event event; int mouseClick = 0; - - int x[] = {-1, -1}; int y[] = {-1, -1}; - while (mouseClick < 2) - { - - while (SDL_PollEvent(&event)) - { - switch (event.type) - { - case SDL_QUIT: - Game::theGame->logMessage("Exit called by human player!\n"); - exit(EXIT_SUCCESS); - break; - case SDL_MOUSEBUTTONDOWN: - switch (event.button.button) - { - case SDL_BUTTON_LEFT: - SDL_GetMouseState(&x[mouseClick], &y[mouseClick]); - x[mouseClick] /= 32; y[mouseClick] /= 32; //Adjust based on graphics grid size - if (mouseClick == 0) - { - stringstream s(""); - s << x[0] << " " << y[0] << " "; - buffer += s.str(); - } - else if (mouseClick == 1) - { - int xDist = x[1] - x[0]; - int yDist = y[1] - y[0]; - if (abs(xDist) > abs(yDist)) - { - if (xDist < 0) - buffer += "LEFT"; - else - buffer += "RIGHT"; - } - else if (yDist < 0) - buffer += "UP"; - else - buffer += "DOWN"; - } - mouseClick++; - break; - } - break; - } - } - } - fprintf(stdout, "Move complete!\n"); - } - else - { - buffer.clear(); - for (char in = fgetc(stdin); in != '\n'; in = fgetc(stdin)) - { - buffer += in; - } - } - - - - return MovementResult::OK; - -} diff --git a/home/progcomp/judge/manager/human_controller.h b/home/progcomp/judge/manager/human_controller.h deleted file mode 100644 index b2069fc..0000000 --- a/home/progcomp/judge/manager/human_controller.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef HUMAN_CONTROLLER_H -#define HUMAN_CONTROLLER_H - -#include "controller.h" - -/** - * Class to control a human player playing Stratego - */ -class Human_Controller : public Controller -{ - public: - Human_Controller(const Piece::Colour & newColour, const bool enableGraphics) : Controller(newColour, "human"), graphicsEnabled(enableGraphics) {} - virtual ~Human_Controller() {} - - virtual bool HumanController() const {return true;} - virtual MovementResult QuerySetup(const char * opponentName, std::string setup[]); - virtual MovementResult QueryMove(std::string & buffer); - virtual void Message(const char * message) {fprintf(stderr, "%s\n", message);} - - private: - const bool graphicsEnabled; - - -}; - -#endif //AI_CONTROLLER_H diff --git a/home/progcomp/judge/manager/images/piece0.bmp b/home/progcomp/judge/manager/images/piece0.bmp deleted file mode 100644 index bd67766..0000000 Binary files a/home/progcomp/judge/manager/images/piece0.bmp and /dev/null differ diff --git a/home/progcomp/judge/manager/images/piece1.bmp b/home/progcomp/judge/manager/images/piece1.bmp deleted file mode 100644 index 85fd5ba..0000000 Binary files a/home/progcomp/judge/manager/images/piece1.bmp and /dev/null differ diff --git a/home/progcomp/judge/manager/images/piece10.bmp b/home/progcomp/judge/manager/images/piece10.bmp deleted file mode 100644 index cf77ed4..0000000 Binary files a/home/progcomp/judge/manager/images/piece10.bmp and /dev/null differ diff --git a/home/progcomp/judge/manager/images/piece11.bmp b/home/progcomp/judge/manager/images/piece11.bmp deleted file mode 100644 index b006d3d..0000000 Binary files a/home/progcomp/judge/manager/images/piece11.bmp and /dev/null differ diff --git a/home/progcomp/judge/manager/images/piece12.bmp b/home/progcomp/judge/manager/images/piece12.bmp deleted file mode 100644 index 8c67a48..0000000 Binary files a/home/progcomp/judge/manager/images/piece12.bmp and /dev/null differ diff --git a/home/progcomp/judge/manager/images/piece13.bmp b/home/progcomp/judge/manager/images/piece13.bmp deleted file mode 100644 index f822c24..0000000 Binary files a/home/progcomp/judge/manager/images/piece13.bmp and /dev/null differ diff --git a/home/progcomp/judge/manager/images/piece14.bmp b/home/progcomp/judge/manager/images/piece14.bmp deleted file mode 100644 index 3145270..0000000 Binary files a/home/progcomp/judge/manager/images/piece14.bmp and /dev/null differ diff --git a/home/progcomp/judge/manager/images/piece2.bmp b/home/progcomp/judge/manager/images/piece2.bmp deleted file mode 100644 index 0a11b11..0000000 Binary files a/home/progcomp/judge/manager/images/piece2.bmp and /dev/null differ diff --git a/home/progcomp/judge/manager/images/piece3.bmp b/home/progcomp/judge/manager/images/piece3.bmp deleted file mode 100644 index 40ca3f8..0000000 Binary files a/home/progcomp/judge/manager/images/piece3.bmp and /dev/null differ diff --git a/home/progcomp/judge/manager/images/piece4.bmp b/home/progcomp/judge/manager/images/piece4.bmp deleted file mode 100644 index 0a27a46..0000000 Binary files a/home/progcomp/judge/manager/images/piece4.bmp and /dev/null differ diff --git a/home/progcomp/judge/manager/images/piece5.bmp b/home/progcomp/judge/manager/images/piece5.bmp deleted file mode 100644 index 051bd43..0000000 Binary files a/home/progcomp/judge/manager/images/piece5.bmp and /dev/null differ diff --git a/home/progcomp/judge/manager/images/piece6.bmp b/home/progcomp/judge/manager/images/piece6.bmp deleted file mode 100644 index 5ca389b..0000000 Binary files a/home/progcomp/judge/manager/images/piece6.bmp and /dev/null differ diff --git a/home/progcomp/judge/manager/images/piece7.bmp b/home/progcomp/judge/manager/images/piece7.bmp deleted file mode 100644 index aaf28a1..0000000 Binary files a/home/progcomp/judge/manager/images/piece7.bmp and /dev/null differ diff --git a/home/progcomp/judge/manager/images/piece8.bmp b/home/progcomp/judge/manager/images/piece8.bmp deleted file mode 100644 index b2ea5b7..0000000 Binary files a/home/progcomp/judge/manager/images/piece8.bmp and /dev/null differ diff --git a/home/progcomp/judge/manager/images/piece9.bmp b/home/progcomp/judge/manager/images/piece9.bmp deleted file mode 100644 index e37e251..0000000 Binary files a/home/progcomp/judge/manager/images/piece9.bmp and /dev/null differ diff --git a/home/progcomp/judge/manager/main.cpp b/home/progcomp/judge/manager/main.cpp deleted file mode 100644 index 1db8d42..0000000 --- a/home/progcomp/judge/manager/main.cpp +++ /dev/null @@ -1,275 +0,0 @@ -#include -#include - - - - - - -#include "game.h" - -using namespace std; - -Piece::Colour SetupGame(int argc, char ** argv); -void DestroyGame(); -void PrintResults(const MovementResult & result, string & buffer); - -int main(int argc, char ** argv) -{ - - - - if (argc == 1) - { - fprintf(stderr, "Usage: stratego [options] red blue\n"); - fprintf(stderr, " stratego --help\n"); - exit(EXIT_SUCCESS); - - } - - - Piece::Colour setupError = SetupGame(argc, argv); - MovementResult result = MovementResult::OK; - if (setupError == Piece::NONE) - { - result = Game::theGame->Play(); - } - else - { - result = MovementResult::BAD_SETUP; - Game::theGame->ForceTurn(setupError); - } - - Game::theGame->PrintEndMessage(result); - - string buffer = ""; - PrintResults(result, buffer); - - //Message the AI's the quit message - Game::theGame->red->Message("QUIT " + buffer); - Game::theGame->blue->Message("QUIT " + buffer); - - //Log the message - if (Game::theGame->GetLogFile() != stdout) - Game::theGame->logMessage("%s\n", buffer.c_str()); - - fprintf(stdout, "%s\n", buffer.c_str()); - - exit(EXIT_SUCCESS); - return 0; -} - -Piece::Colour SetupGame(int argc, char ** argv) -{ - char * red = NULL; char * blue = NULL; double timeout = 0.00001; bool graphics = false; bool allowIllegal = false; FILE * log = NULL; - Piece::Colour reveal = Piece::BOTH; char * inputFile = NULL; int maxTurns = 5000; bool printBoard = false; - for (int ii=1; ii < argc; ++ii) - { - if (argv[ii][0] == '-') - { - switch (argv[ii][1]) - { - case 't': - if (argc - ii <= 1) - { - fprintf(stderr, "ARGUMENT_ERROR - Expected timeout value after -t switch!\n"); - exit(EXIT_FAILURE); - } - if (strcmp(argv[ii+1], "inf") == 0) - timeout = -1; - else - timeout = atof(argv[ii+1]); - ++ii; - break; - case 'g': - graphics = !graphics; - break; - case 'p': - printBoard = !printBoard; - break; - case 'i': - allowIllegal = true; - break; - - case 'o': - if (argc - ii <= 1) - { - fprintf(stderr, "ARGUMENT_ERROR - Expected filename or \"stdout\" after -o switch!\n"); - exit(EXIT_FAILURE); - } - if (log != NULL) - { - fprintf(stderr, "ARGUMENT_ERROR - Expected at most ONE -o switch!\n"); - exit(EXIT_FAILURE); - } - if (strcmp(argv[ii+1], "stdout") == 0) - log = stdout; - else - log = fopen(argv[ii+1], "w"); - setbuf(log, NULL); - - ++ii; - break; - - case 'r': - if (reveal == Piece::BOTH) - reveal = Piece::BLUE; - else - reveal = Piece::NONE; - break; - case 'b': - if (reveal == Piece::BOTH) - reveal = Piece::RED; - else - reveal = Piece::NONE; - break; - case 'm': - if (argc - ii <= 1) - { - fprintf(stderr, "ARGUMENT_ERROR - Expected max_turns value after -m switch!\n"); - exit(EXIT_FAILURE); - } - if (strcmp(argv[ii+1], "inf") == 0) - maxTurns = -1; - else - maxTurns = atoi(argv[ii+1]); - ++ii; - break; - case 'f': - if (argc - ii <= 1) - { - fprintf(stderr, "ARGUMENT_ERROR - Expected filename after -f switch!\n"); - exit(EXIT_FAILURE); - } - if (log != NULL) - { - fprintf(stderr, "ARGUMENT_ERROR - Expected at most ONE -f switch!\n"); - exit(EXIT_FAILURE); - } - red = (char*)("file"); - blue = (char*)("file"); - inputFile = argv[ii+1]; - ++ii; - break; - case 'h': - system("clear"); - system("less manual.txt"); - exit(EXIT_SUCCESS); - break; - case '-': - if (strcmp(argv[ii]+2, "help") == 0) - { - system("clear"); - system("less manual.txt"); - exit(EXIT_SUCCESS); - } - else - { - fprintf(stderr, "ARGUMENT_ERROR - Unrecognised switch \"%s\"...\n", argv[ii]); - exit(EXIT_FAILURE); - } - } - - } - else - { - if (red == NULL) - red = argv[ii]; - else if (blue == NULL) - blue = argv[ii]; - else - { - fprintf(stderr, "ARGUMENT_ERROR - Unexpected argument \"%s\"...\n", argv[ii]); - exit(EXIT_FAILURE); - } - } - } - - - - if (inputFile == NULL) - { - if (red == NULL || blue == NULL) //Not enough arguments - { - fprintf(stderr, "ARGUMENT_ERROR - Did not recieve enough players (did you mean to use the -f switch?)\n"); - exit(EXIT_FAILURE); - } - Game::theGame = new Game(red,blue, graphics, timeout, allowIllegal,log, reveal,maxTurns, printBoard); - } - else - { - Game::theGame = new Game(inputFile, graphics, timeout, allowIllegal,log, reveal,maxTurns, printBoard); - } - - if (Game::theGame == NULL) - { - fprintf(stderr,"INTERNAL_ERROR - Error creating Game!\n"); - exit(EXIT_FAILURE); - } - atexit(DestroyGame); - - return Game::theGame->Setup(red, blue); - - -} - -void PrintResults(const MovementResult & result, string & buffer) -{ - stringstream s(""); - switch (Game::theGame->Turn()) - { - case Piece::RED: - s << Game::theGame->red->name << " RED "; - break; - case Piece::BLUE: - s << Game::theGame->blue->name << " BLUE "; - break; - case Piece::BOTH: - s << "neither BOTH "; - break; - case Piece::NONE: - s << "neither NONE "; - break; - } - - if (!Board::LegalResult(result) && result != MovementResult::BAD_SETUP) - s << "ILLEGAL "; - else if (!Board::HaltResult(result)) - s << "INTERNAL_ERROR "; - else - { - switch (result.type) - { - case MovementResult::VICTORY: - s << "VICTORY "; - break; - case MovementResult::SURRENDER: - s << "SURRENDER "; - break; - case MovementResult::DRAW: - s << "DRAW "; - break; - case MovementResult::DRAW_DEFAULT: - s << "DRAW_DEFAULT "; - break; - case MovementResult::BAD_SETUP: - s << "BOTH_ILLEGAL "; - break; - default: - s << "INTERNAL_ERROR "; - break; - } - } - - s << Game::theGame->TurnCount() << " " << Game::theGame->theBoard.TotalPieceValue(Piece::RED) << " " << Game::theGame->theBoard.TotalPieceValue(Piece::BLUE); - - buffer = s.str(); - - -} - -void DestroyGame() -{ - delete Game::theGame; - Game::theGame = NULL; -} diff --git a/home/progcomp/judge/manager/manual.txt b/home/progcomp/judge/manager/manual.txt deleted file mode 100644 index 3237241..0000000 --- a/home/progcomp/judge/manager/manual.txt +++ /dev/null @@ -1,230 +0,0 @@ -NAME - stratego - Interface to manage games of stratego between AI programs and/or human players - -WARNING - This program is still a work in progress. Consider it a Beta version. - -SYNOPSIS - stratego {[-gpirb] [-o output_file ] [-t stall_time] [-m max_turns] {red_player blue_player | -f input_file} | {-h | --help} } - -DESCRIPTION - stratego manages a game of Stratego. It stores the state of the board, and uses a simple protocol to interface with AI programs. - By itself, stratego does not "play" the game. An external AI program must be used. stratego is intended to be used for the testing of - various AI strategies, written in any programming language. It will be used for the UCC Programming Competition 2012. - - Unless the -h (--help) or -f switch is given, both red_player and blue_player must be supplied. - - red_player - Should be either a path to an executable file which will control the Red player, or "human". - If set to "human", stratego will request the user to make moves for the Red player using stdin. - NOTES - 1. There is no plan to support AI programs named "human". Deal with it. - 2. The graphical interface for human players is... basic. Deal with it. - - blue_player - As red_player, except for controlling the Blue player. - -OPTIONS - -g - By default, graphics are disabled. If the -g switch is present, stratego will draw the game as it is played using SDL/OpenGL - - -p - By default, even if graphics are disabled, the board state is not printed. If -p is present, the board will be printed to stdout. - If the system supports colour, the characters will be in colour. - Yes, If -p and -g are both present you will see both behaviours (overkill)! - -i - By default, stratego will exit if a move which is deemed "illegal" is made. If the -i switch is present, illegal moves will be ignored. - That is, the move will not be made (effectively the player making the illegal move loses a turn). - - NOTE: If -i is not given and a human player accidentally(?) makes an illegal move, they will be asked to make a different move. The game will continue. - This is intended to prevent fits of rage due to the horrible graphical interface causing humans to make illegal moves. - -r - By default, the identities of all pieces are shown. If the -r switch is present, and graphics are enabled, red pieces will be disguised. - If graphics are disabled, the -r switch has no effect. - - Pieces which have previously taken part in combat (and survived) will be revealed. - -b - As -r, except blue pieces will be disguised. - NOTE: Both -r and -b may be used together. - -o - By default, stratego does not log moves. If the -o switch is present, the result of each move is printed to a file. - If output_file is "stdout" then stdout will be used instead of a text file. - -t - By default, stratego executes moves as fast as they are recieved. If the -t switch is present, a delay of stall_time will be introduced - between each move. - - If stall_time is negative or "inf", stratego will wait for the user to press enter before moving to the next move. - - It is tentatively planned to allow the user to enter various commands to alter the game or proceed to specified turns. - However this is slightly complicated. So it might never be done. - -m - By default, the game is declared a Draw after 5000 turns have ellapsed. - Use this option to change the maximum number of turns. - To play for an infinite number of turns, supply "inf" as max_number. This is not recommended for obvious reasons. - - -f - By default, stratego requires red_player and blue_player to enact a game. - If this option is supplied, a file previously produced by using the -o switch is read, and the game reenacted. - All switches function as normal with -f. - NOTE: It is recommended that -g is used with -f. - - -h, --help - If the -h switch is used, this page will be printed and stratego will exit. - - - -GAME RULES - Each player controls up to 40 pieces on the Board. The pieces consist of the following: - - Piece Name Rank Number Abilities - 1 Marshal 1 1 Dies if attacked by Spy - 2 General 2 1 - 3 Colonel 3 2 - 4 Major 4 3 - 5 Captain 5 4 - 6 Lieutenant 6 4 - 7 Sergeant 7 4 - 8 Miner 8 5 Destroys Bombs without being killed - 9 Scout 9 8 May move more through multiple empty squares - s Spy 10 1 If the Spy attacks the Marshal, the Marshal dies - B Bomb NA 6 Immobile. If any piece (except a Miner) encounters an enemy Bomb, both pieces are destroyed - F Flag NA 1 Immobile. If any piece encounters the enemy Flag, the controlling player wins. - - Additional pieces, not controlled by the player: - Piece Name Number Notes - + Obstacle 8 Immobile. Do not belong to either player. Can't be passed through. - # Enemy Piece 0 - 40 Indicates that the position on the board is occupied by an enemy piece. - . Empty NA Indicates that the position on the board is empty. - - Players take turns to move their pieces. RED begins the game. - - Pieces may only move one square horizontally or vertically unless otherwise stated. - Pieces may not move through squares occupied by allied pieces, or Obstacle (+) pieces. - Pieces may move into squares occupied by Enemy Pieces (#), in which case the piece with the lower rank (higher number) is destroyed. - - Each player's pieces are hidden from the other player. When two pieces encounter each other, the ranks will be revealed. - - The objective is to destroy all Enemy Pieces (#) or capture the Enemy Flag (also #). - - -PROTOCOL - In order to interface with stratego, an AI program must satisfy the following protocol. - Each query is followed by a newline, and responses are expected to be followed with a newline. - The queries are recieved through stdin, and responses should be written to stdout. - - 1. SETUP - QUERY: YOUR_COLOUR OPPONENT_ID BOARD_WIDTH BOARD_HEIGHT - - RESPONSE: 4 lines, each of length BOARD_WIDTH, of characters. Each character represents a piece. The characters are shown above. - - RED's pieces are placed at the top of the board, and BLUE's pieces are placed at the bottom. - - An AI program does not have to place all 40 pieces, but must at least place the flag ('F'). - - 2. TURN - QUERY: START | CONFIRMATION - BOARD_STATE - - On the first turn, "START" is printed to the Red player. - On subsequent turns, the CONFIRMATION of the opponent's last turn is printed (see below). - - BOARD_STATE consists of a BOARD_HEIGHT lines of length BOARD_WIDTH characters, each of which represents a single piece - as described in the GAME_RULES section. Each line ends with the newline character. - - - RESPONSE: X Y DIRECTION [MULTIPLIER=1] | NO_MOVE - X and Y are the coords (starting from 0) of the piece to move - DIRECTION is either UP, DOWN, LEFT or RIGHT - MULTIPLIER is optional and only valid for units of type Scout. Scouts may move through any number of unblocked squares - in one direction. - - The AI program should print "NO_MOVE" if it is unable to determine a move. - This will typically occur when the only pieces belonging to the AI program are Bombs and the Flag. - - CONFIRMATION: X Y DIRECTION [MULTIPLIER=1] OUTCOME | NO_MOVE {OK | ILLEGAL} | QUIT [RESULT] - - OUTCOME may be either OK, ILLEGAL, KILLS or DIES - OK - Move was successful - ILLEGAL - Move was not allowed. If stratego was not started with the -i switch, the game will end. - KILLS ATTACKER_RANK DEFENDER_RANK - The piece moved into an occupied square and killed the defender. - DIES ATTACKER_RANK DEFENDER_RANK - The piece moved into an occupied square and was killed by the defender. - - Most turns will be confirmed with: "X Y DIRECTION [MULTIPLIER=1] OUTCOME" - - A confirmation of "NO_MOVE OK" occurs when the AI program made no move for a legitimate reason. - "NO_MOVE ILLEGAL" is printed if the AI program made no move for an illegitimate reason. - - If both AI programs successively make a "NO_MOVE" response, then the game will end. - The player with the highest piece value will win, or a draw will be declared if the values are equal. - - 3. END GAME - If the CONFIRMATION line is of the form: - QUIT [RESULT] - Then the game is about to end. - - If present, RESULT will be a direct copy of the message to stdout described in the EXIT/OUTPUT section below. - - - 4. TIMEOUTS - If a program fails to respond to a query within 2 (two) seconds, the game will end and that AI will be sent the ILLEGAL result. - Human players are not subject to the timeout restriction. - - - -EXIT/OUTPUT - If the game ends due to a player either winning, or making an illegal move, stratego will print one of the following result messages to stdout. - - NAME COLOUR OUTCOME TURN_NUMBER OUTCOME RED_PIECE_VALUE BLUE_PIECE_VALUE - - Where: - NAME is the name of the player on whose turn the game ended, - COLOUR is the colour of that player, - OUTCOME is one of the following: - VICTORY - The indicated player won - DEFEAT - The indicated player lost - SURRENDER - The indicated player surrendered - DRAW - The game ended in a draw because neither player moved - DRAW_DEFAULT - The game ended in a draw because the maximum number of moves was exceeded - ILLEGAL - The indicated player loses due to an Illegal move/response - DEFAULT - The indicated player wins by default due to the other player making an Illegal move/response - BOTH_ILLEGAL - Both players made an Illegal move/response. Usually occurs due to simultaneous setup errors, or bad executable paths. - INTERNAL_ERROR - The game ended, even though it shouldn't have. - - TURN_NUMBER is the number of turns that elapsed before the game ended - - RED_PIECE_VALUE and BLUE_PIECE_VALUE are the summed piece values of the pieces of RED and BLUE respectively. - Bombs and Flags are worth zero, and the ranked pieces (Spys -> Marshal) are worth (11 - rank). - So the Spy is worth 1 point, ... the Marshal is worth 10. - - (The initial piece values can be determined by running with -m 0) - - - stratego will then return exit code 0. - - If an error occurs within stratego itself, an error message will be printed to stderr and return exit code 1. - If possible, stratego will print the message "QUIT" to both AI programs, and they should exit as soon as possible. - - -BUGS - WARNING: - stratego has been observed to segfault occassionally after the end of a game. It is not yet known what is causing these errors. - They appear to occur most often when the result is a draw, however they have been observed to occur under all exit conditions except the Illegal case. The result is still printed to stdout. However this bug _must_ be fixed before stratego can be used by simulation scripts. - - stratego is still a work in progress. Report another bug to the AUTHOR (see below). - -AUTHORS - Sam Moore (for the UCC Programming Competition 2012) - -NOTES - 0. This program is still a work in progress and subject to changes. - - 1. UCC Programming Competition 2012 Description - http://matches.ucc.asn.au/stratego/ - - 2. UCC Programming Competition 2012 Git repository - git://git.ucc.asn.au/progcomp2012.git - - - 3. IRC Channel - irc://irc.ucc.asn.au #progcomp - diff --git a/home/progcomp/judge/manager/movementresult.h b/home/progcomp/judge/manager/movementresult.h deleted file mode 100644 index 5a2aed6..0000000 --- a/home/progcomp/judge/manager/movementresult.h +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Contains declaration for MovementResult class - */ -#ifndef MOVERESULT_H -#define MOVERESULT_H - -class Board; -class Piece; - -/** - * Class used to indicate the result of a move in stratego - */ -class MovementResult -{ - public: - typedef enum {OK, DIES, KILLS, BOTH_DIE, NO_BOARD, INVALID_POSITION, NO_SELECTION, NOT_YOUR_UNIT, IMMOBILE_UNIT, INVALID_DIRECTION, POSITION_FULL, VICTORY, SURRENDER, BAD_RESPONSE, NO_MOVE, COLOUR_ERROR, ERROR, DRAW_DEFAULT, DRAW, BAD_SETUP} Type; - - MovementResult(const Type & result = OK, const Piece::Type & newAttackerRank = Piece::NOTHING, const Piece::Type & newDefenderRank = Piece::NOTHING) - : type(result), attackerRank(newAttackerRank), defenderRank(newDefenderRank) {} - MovementResult(const MovementResult & cpy) : type(cpy.type), attackerRank(cpy.attackerRank), defenderRank(cpy.defenderRank) {} - virtual ~MovementResult() {} - - - bool operator==(const Type & equType) const {return type == equType;} - bool operator!=(const Type & equType) const {return type != equType;} - - Type type; - Piece::Type attackerRank; - Piece::Type defenderRank; -}; - -#endif //MOVERESULT_H - -//EOF diff --git a/home/progcomp/judge/manager/program.cpp b/home/progcomp/judge/manager/program.cpp deleted file mode 100644 index 660362a..0000000 --- a/home/progcomp/judge/manager/program.cpp +++ /dev/null @@ -1,193 +0,0 @@ -#include - -#include - -#include - -#include "thread_util.h" -#include "program.h" - - -using namespace std; - - -/** - * Constructor - * @param executablePath - path to the program that will be run - * - * Creates two pipes - one for each direction between the parent process and the AI program - * Forks the process. - * The child process closes unused sides of the pipe, and then calls exec to replace itself with the AI program - * The parent process closes unused sides of the pipe, and sets up member variables - associates streams with the pipe fd's for convenience. - */ -Program::Program(const char * executablePath) : input(NULL), output(NULL), pid(0) -{ - //See if file exists... - FILE * file = fopen(executablePath, "r"); - if (file != NULL) - { - fclose(file); - } - else - { - pid = -1; - return; - } - - int readPipe[2]; int writePipe[2]; - assert(pipe(readPipe) == 0); - assert(pipe(writePipe) == 0); - - pid = fork(); - if (pid == 0) - { - close(readPipe[0]); //close end that parent reads from - close(writePipe[1]); //close end that parent writes to - - //TODO: Fix possible bug here if the process is already a daemon - assert(writePipe[0] != 0 && readPipe[1] != 1); - dup2(writePipe[0],0); close(writePipe[0]); //pipe end child reads from goes to STDIN - dup2(readPipe[1], 1); close(readPipe[1]); //pipe end child writes to goes to STDOUT - - //TODO: Somehow force the exec'd process to be unbuffered - setbuf(stdin, NULL); //WARNING: These lines don't appear to have any affect - setbuf(stdout, NULL); //You should add them at the start of the wrapped program. - //If your wrapped program is not written in C/C++, you will probably have a problem - - - - execl(executablePath, executablePath, (char*)(NULL)); ///Replace process with desired executable - //fprintf(stderr, "Program::Program - Could not run program \"%s\"!\n", executablePath); - //exit(EXIT_FAILURE); //We will probably have to terminate the whole program if this happens - } - else - { - close(writePipe[0]); //close end that child writes to - close(readPipe[1]); //close end that child reads from - - input = fdopen(readPipe[0],"r"); output = fdopen(writePipe[1],"w"); - setbuf(input, NULL); - setbuf(output, NULL); - } - -} - -/** - * Destructor - * Writes EOF to the wrapped program and then closes all streams - * Kills the wrapped program if it does not exit within 1 second. - */ -Program::~Program() -{ - if (Running()) //Check if the process created is still running... - { - //fputc(EOF, output); //If it was, tell it to stop with EOF - - TimerThread timer(2); //Wait for 2 seconds - timer.Start(); - while (!timer.Finished()) - { - if (!Running()) - { - timer.Stop(); - break; - } - } - timer.Stop(); - kill(pid, SIGKILL); - } - if (pid > 0) - { - fclose(input); - fclose(output); - } - -} - - - - - -/** - * Sends a message to the wrapped AI program - * WARNING: Always prints a new line after the message (so don't include a new line) - * This is because everything is always line buffered. - * @returns true if the message was successfully sent; false if it was not (ie: the process was not running!) - */ -bool Program::SendMessage(const char * print, ...) -{ - if (!Running()) //Is the process running... - return false; - - va_list ap; - va_start(ap, print); - - if (vfprintf(output, print, ap) < 0 || fprintf(output, "\n") < 0) - { - va_end(ap); - return false; - } - va_end(ap); - - - - - return true; -} - - -/** - * Retrieves a message from the wrapped AI program, waiting a maximum amount of time - * @param buffer - C++ string to store the resultant message in - * @param timeout - Maximum amount of time to wait before failure. If timeout <= 0, then GetMessage will wait indefinately. - * @returns true if the response was recieved within the specified time, false if it was not, or an EOF was recieved, or the process was not running. - */ -bool Program::GetMessage(string & buffer, double timeout) -{ - if (!Running()) - return false; - - assert(&buffer != NULL); - GetterThread getterThread(input, buffer); - assert(&(getterThread.buffer) != NULL); - TimerThread timerThread(timeout*1000000); - - getterThread.Start(); - if (timeout > 0) - timerThread.Start(); - - - while (!getterThread.Finished()) - { - if (timeout > 0 && timerThread.Finished()) - { - getterThread.Stop(); - timerThread.Stop(); - return false; - } - } - - getterThread.Stop(); - timerThread.Stop(); - - - - if (buffer.size() == 1 && buffer[0] == EOF) - return false; - return true; - - -} - -/** - * Returns true iff the process is running - * @returns what I just said, fool - */ -bool Program::Running() const -{ - return (pid > 0 && kill(pid,0) == 0); -} - - - - diff --git a/home/progcomp/judge/manager/program.h b/home/progcomp/judge/manager/program.h deleted file mode 100644 index 8fef696..0000000 --- a/home/progcomp/judge/manager/program.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef PROGRAM_H -#define PROGRAM_H - -#include "thread_util.h" - -#include - -/** - * A wrapping class for an external program, which can exchange messages with the current process through stdin/stdout - * Useful for attaching control of an operation to an external process - for example, AI for a game - */ -class Program -{ - public: - Program(const char * executeablePath); //Constructor - virtual ~Program(); //Destructor - - - - - bool SendMessage(const char * print, ...); //Sends a formated message (NOTE: Prints a newline) - bool SendMessage(const std::string & buffer) {return SendMessage(buffer.c_str());} //Sends a C++ string message - bool GetMessage(std::string & buffer, double timeout=-1); //Retrieves a message, or waits for a timeout (if positive) - - bool Running() const; - - - - protected: - FILE * input; //Stream used for sending information TO the process - FILE * output; //Stream used for retrieving information FROM the process - - private: - pid_t pid; //Process ID of the program wrapped - -}; - -#endif //PROGRAM_H - - diff --git a/home/progcomp/judge/manager/stratego.cpp b/home/progcomp/judge/manager/stratego.cpp deleted file mode 100644 index f2df0ff..0000000 --- a/home/progcomp/judge/manager/stratego.cpp +++ /dev/null @@ -1,512 +0,0 @@ - - -#include "stratego.h" - -using namespace std; - -/** - * Static variables - */ - -//nothing, boulder, flag, spy, scout, miner, sergeant, lietenant, captain, major, colonel, general, marshal, bomb, error -char Piece::tokens[] = {'.','*','F','s','9','8','7','6','5','4','3','2','1','B','?'}; -int Piece::maxUnits[] = {0,0,1,1,8,5,4,4,4,3,2,1,1,6,0}; - - - - -Piece::TextureManager Piece::textures; - - - - -Piece::TextureManager::~TextureManager() -{ - Array::Iterator i(*this); - while (i.Good()) - { - delete (*i); - ++i; - } -} - -Texture & Piece::TextureManager::operator[](const LUint & at) -{ - while (Array::Size() <= at) - { - char buffer[BUFSIZ]; - sprintf(buffer, "images/piece%lu.bmp", Array::Size()); - Array::Add(new Texture(buffer, false)); - - } - return *(Array::operator[](at)); -} - - -/** - * Gets the type of a piece, based off a character token - * @param fromToken - character identifying the piece - * @returns The type of the piece - */ -Piece::Type Piece::GetType(char fromToken) -{ - for (int ii=0; ii <= (int)(Piece::BOMB); ++ii) - { - if (tokens[ii] == fromToken) - { - return Type(Piece::NOTHING + ii); - } - } - return Piece::BOULDER; -} - -/** - * Construct a new, empty board - * @param newWidth - the width of the board - * @param newHeight - the height of the board - */ -Board::Board(int newWidth, int newHeight) : winner(Piece::NONE), width(newWidth), height(newHeight), board(NULL), pieces() -{ - board = new Piece**[width]; - for (int x=0; x < width; ++x) - { - board[x] = new Piece*[height]; - for (int y=0; y < height; ++y) - board[x][y] = NULL; - } -} - -/** - * Cleanup a board - */ -Board::~Board() -{ - for (int x=0; x < width; ++x) - { - for (int y=0; y < height; ++y) - delete board[x][y]; - delete [] board[x]; - } -} - -/** - * Print textual representation of the board to a stream - * @param stream - the stream to print information to - * @param reveal - Pieces matching this colour will have their identify revealed, other pieces will be shown as '#' - */ -void Board::Print(FILE * stream, const Piece::Colour & reveal) -{ - for (int y=0; y < height; ++y) - { - for (int x=0; x < width; ++x) - { - Piece * piece = board[x][y]; - if (piece == NULL) - { - fprintf(stream, "."); - } - else if (piece->colour != Piece::NONE && (piece->colour == reveal || reveal == Piece::BOTH)) - { - - fprintf(stream, "%c", Piece::tokens[piece->type]); - - - } - else - { - switch (piece->colour) - { - case Piece::RED: - case Piece::BLUE: - fprintf(stream, "#"); - break; - case Piece::NONE: - fprintf(stream, "+"); - break; - case Piece::BOTH: - fprintf(stream, "$"); - break; - } - } - } - fprintf(stream, "\n"); - } - -} - -/** - * Print textual representation of the board to a stream - * @param stream - the stream to print information to - * @param reveal - Pieces matching this colour will have their identify revealed, other pieces will be shown as '#' - */ -void Board::PrintPretty(FILE * stream, const Piece::Colour & reveal) -{ - for (int y=0; y < height; ++y) - { - for (int x=0; x < width; ++x) - { - Piece * piece = board[x][y]; - if (piece == NULL) - { - fprintf(stream, "."); - } - else if (piece->colour != Piece::NONE && (piece->colour == reveal || reveal == Piece::BOTH)) - { - switch (piece->colour) - { - case Piece::RED: - fprintf(stream, "%c[%d;%d;%dm",0x1B,1,31,40); - break; - case Piece::BLUE: - fprintf(stream, "%c[%d;%d;%dm",0x1B,1,34,40); - break; - default: - break; - } - fprintf(stream, "%c", Piece::tokens[piece->type]); - - } - else - { - switch (piece->colour) - { - case Piece::RED: - fprintf(stream, "%c[%d;%d;%dm",0x1B,1,31,41); - - break; - case Piece::BLUE: - fprintf(stream, "%c[%d;%d;%dm",0x1B,1,34,44); - break; - case Piece::NONE: - fprintf(stream, "%c[%d;%d;%dm",0x1B,1,37,47); - break; - case Piece::BOTH: - //Should never see this - fprintf(stream, "%c[%d;%d;%dm",0x1B,1,33,43); - break; - - } - fprintf(stream, "#"); - - } - fprintf(stream, "%c[%d;%d;%dm",0x1B,0,7,0); - } - fprintf(stream, "\n"); - } - -} - - - -/** - * Draw the board state to graphics - * @param reveal - Pieces matching this colour will be revealed. All others will be shown as blank coloured squares. - */ -void Board::Draw(const Piece::Colour & reveal, bool showRevealed) -{ - if (!Graphics::Initialised()) - { - fprintf(stderr, "ERROR - Board::Draw called whilst graphics disabled!!!\n"); - exit(EXIT_FAILURE); - - } - - Graphics::ClearScreen(); - - for (int y=0; y < height; ++y) - { - for (int x=0; x < width; ++x) - { - Piece * piece = board[x][y]; - if (piece == NULL) - { - //Don't display anything - - } - else if ((piece->colour != Piece::NONE && (piece->colour == reveal || reveal == Piece::BOTH)) - || (piece->beenRevealed && showRevealed)) - { - //Display the piece - Piece::textures[(int)(piece->type)].DrawColour(x*32,y*32,0,1, Piece::GetGraphicsColour(piece->colour)); - - } - else - { - switch (piece->colour) - { - case Piece::RED: - Piece::textures[(int)(Piece::NOTHING)].DrawColour(x*32,y*32,0,1, Piece::GetGraphicsColour(piece->colour)); - break; - case Piece::BLUE: - Piece::textures[(int)(Piece::NOTHING)].DrawColour(x*32,y*32,0,1, Piece::GetGraphicsColour(piece->colour)); - break; - case Piece::NONE: - Piece::textures[(int)(Piece::BOULDER)].DrawColour(x*32,y*32,0,1, Piece::GetGraphicsColour(piece->colour)); - break; - case Piece::BOTH: - Piece::textures[(int)(Piece::BOULDER)].DrawColour(x*32,y*32,0,1, Piece::GetGraphicsColour(piece->colour)); - break; - } - } - } - - } - Graphics::UpdateScreen(); - -} - -/** - * Adds a piece to the board - * @param x - x-coord to place the piece at, starting at zero, must be less than board width - * @param y - y-coord to place the piece at, starting at zero, must be less than board height - * @param newType - the Type of the piece - * @param newColour - the Colour of the piece - * @returns true if and only if the piece could be successfully added. - */ -bool Board::AddPiece(int x, int y, const Piece::Type & newType, const Piece::Colour & newColour) -{ - if (board == NULL || x < 0 || y < 0 || x >= width || y >= width || board[x][y] != NULL) - return false; - - Piece * piece = new Piece(newType, newColour); - board[x][y] = piece; - - pieces.push_back(piece); - return true; -} - -/** - * Gets a pointer to a piece at a board location - * UNUSED - * @param x - x-coord of the piece - * @param y - y-coord of the piece - * @returns pointer to the piece, or NULL if the board location was empty - * @throws error if board is null or coords are invalid - */ -Piece * Board::GetPiece(int x, int y) -{ - assert(board != NULL); - assert(x >= 0 && x < width && y >= 0 && y < height); - return board[x][y]; -} - -/** - * Moves a piece at a specified position in the specified direction, handles combat if necessary - * @param x - x-coord of the piece - * @param y - y-coord of the piece - * @param direction - Direction in which to move (UP, DOWN, LEFT or RIGHT) - * @param colour - Colour which the piece must match for the move to be valid - * @returns A MovementResult which indicates the result of the move - OK is good, VICTORY means that a flag was captured, anything else is an error - */ -MovementResult Board::MovePiece(int x, int y, const Direction & direction, int multiplier,const Piece::Colour & colour) -{ - if (board == NULL) - { - return MovementResult(MovementResult::NO_BOARD); - } - if (!(x >= 0 && x < width && y >= 0 && y < height)) - { - return MovementResult(MovementResult::INVALID_POSITION); - } - Piece * target = board[x][y]; - if (target == NULL) - { - return MovementResult(MovementResult::NO_SELECTION); - } - if (!(colour == Piece::NONE || target->colour == colour)) - { - return MovementResult(MovementResult::NOT_YOUR_UNIT); - } - if (target->type == Piece::FLAG || target->type == Piece::BOMB || target->type == Piece::BOULDER) - { - return MovementResult(MovementResult::IMMOBILE_UNIT); - } - if (multiplier > 1 && target->type != Piece::SCOUT) - { - return MovementResult(MovementResult::INVALID_DIRECTION); //Can only move a scout multiple times. - } - int x2 = x; int y2 = y; - - for (int ii=0; ii < multiplier; ++ii) - { - switch (direction) - { - case UP: - --y2; - break; - case DOWN: - ++y2; - break; - case LEFT: - --x2; - break; - case RIGHT: - ++x2; - break; - } - if (!(x2 >= 0 && x2 < width && y2 >= 0 && y2 < height)) - { - return MovementResult(MovementResult::INVALID_DIRECTION); - } - if (ii < multiplier-1 && board[x2][y2] != NULL) - { - return MovementResult(MovementResult::POSITION_FULL); - } - } - Piece * defender = board[x2][y2]; - if (defender == NULL) - { - board[x][y] = NULL; - board[x2][y2] = target; - } - else if (defender->colour != target->colour) - { - defender->beenRevealed = true; - target->beenRevealed = true; - - Piece::Type defenderType = defender->type; - Piece::Type attackerType = target->type; - - if (defender->colour == Piece::NONE) - { - return MovementResult(MovementResult::POSITION_FULL); - } - if (defender->type == Piece::FLAG) - { - winner = target->colour; - return MovementResult(MovementResult::VICTORY); - } - else if (defender->type == Piece::BOMB) - { - if (target->type == Piece::MINER) - { - RemovePiece(defender); - delete defender; - board[x][y] = NULL; - board[x2][y2] = target; - return MovementResult(MovementResult::KILLS, attackerType, defenderType); - } - else - { - RemovePiece(defender); - RemovePiece(target); - delete defender; - delete target; - board[x][y] = NULL; - board[x2][y2] = NULL; - return MovementResult(MovementResult::BOTH_DIE, attackerType, defenderType); - } - } - else if (defender->type == Piece::MARSHAL && target->type == Piece::SPY) - { - RemovePiece(defender); - delete defender; - board[x][y] = NULL; - board[x2][y2] = target; - return MovementResult(MovementResult::KILLS, attackerType, defenderType); - } - else if (target->operator > (*defender)) - { - RemovePiece(defender); - delete defender; - board[x][y] = NULL; - board[x2][y2] = target; - return MovementResult(MovementResult::KILLS, attackerType, defenderType); - } - else if (target->operator==(*defender) && rand() % 2 == 0) - { - RemovePiece(defender); - delete defender; - board[x][y] = NULL; - board[x2][y2] = target; - return MovementResult(MovementResult::KILLS, attackerType, defenderType); - } - else - { - RemovePiece(target); - delete target; - board[x][y] = NULL; - return MovementResult(MovementResult::DIES, attackerType, defenderType); - } - } - else - { - return MovementResult(MovementResult::POSITION_FULL); - } - return MovementResult(MovementResult::OK); -} - -/** - * Removes a piece from the board - * @param piece The piece to remove - * @returns true iff the piece actually existed - */ -bool Board::RemovePiece(Piece * piece) -{ - bool result = false; - for (int x = 0; x < width; ++x) - { - for (int y = 0; y < height; ++y) - { - if (board[x][y] == piece) - { - result = true; - board[x][y] = NULL; - } - } - } - - vector::iterator i = pieces.begin(); - while (i != pieces.end()) - { - if ((*i) == piece) - { - i = pieces.erase(i); - result = true; - continue; - } - ++i; - } - return result; -} - -/** - * Returns the total value of pieces belonging to colour - * @param colour the colour - * @returns the total value of pieces belonging to colour. - * (Redundant repetition <3) - */ -int Board::TotalPieceValue(const Piece::Colour & colour) const -{ - int result = 0; - for (vector::const_iterator i = pieces.begin(); i != pieces.end(); ++i) - { - if ((*i)->colour == colour || colour == Piece::BOTH) - { - result += (*i)->PieceValue(); - } - } - return result; -} - -/** - * Returns the total number of mobile pieces belonging to colour - * @param colour the colour - * @returns the total value of mobile pieces belonging to colour. - * (Redundant repetition <3) - */ -int Board::MobilePieces(const Piece::Colour & colour) const -{ - int result = 0; - for (vector::const_iterator i = pieces.begin(); i != pieces.end(); ++i) - { - if ((*i)->colour == colour || colour == Piece::BOTH) - { - if ((*i)->type <= Piece::MARSHAL && (*i)->type >= Piece::SPY) - result++; - } - } - return result; -} - - diff --git a/home/progcomp/judge/manager/stratego.h b/home/progcomp/judge/manager/stratego.h deleted file mode 100644 index fe73aa1..0000000 --- a/home/progcomp/judge/manager/stratego.h +++ /dev/null @@ -1,156 +0,0 @@ -#ifndef STRATEGO_H -#define STRATEGO_H - -#include -#include - -#include - - - #include "graphics.h" - #include "array.h" - -#include - -/** - * Contains classes for a game of Stratego - */ - - -/** - * Class for a game piece - */ -class Piece -{ - public: - typedef enum {ERROR=14,BOMB=13,MARSHAL=12, GENERAL=11, COLONEL=10, MAJOR=9, CAPTAIN=8, LIEUTENANT=7, SERGEANT=6, MINER=5, SCOUT=4, SPY=3, FLAG=2,BOULDER=1, NOTHING=0} Type; //Type basically defines how strong the piece is - - - - typedef enum {RED=0, BLUE=1, NONE=2, BOTH=3} Colour; //Used for the allegiance of the pieces - terrain counts as NONE. - - Piece(const Type & newType, const Colour & newColour) : type(newType), colour(newColour), beenRevealed(false) {} - virtual ~Piece() {} - - - //Operators compare the strength of two pieces - bool operator==(const Piece & equ) const {return type == equ.type;} - bool operator<(const Piece & equ) const {return type < equ.type;} - bool operator>(const Piece & equ) const {return type > equ.type;} - - bool operator!=(const Piece & equ) const {return !operator==(equ);} - bool operator<=(const Piece & equ) const {return (operator<(equ) || operator==(equ));} - bool operator>=(const Piece & equ) const {return (operator>(equ) || operator==(equ));} - - //Contains the characters used to identify piece types when the board is printed to a stream - static char tokens[]; - static int maxUnits[]; - - static Type GetType(char fromToken); - - int PieceValue() const {if (type == BOMB || type == FLAG) {return 0;} return (int)(type) - (int)(SPY) + 1;} - - //Attributes of the piece - const Type type; - const Colour colour; - - bool beenRevealed; - - public: - - class TextureManager : public Graphics::TextureManager, private Array - { - public: - TextureManager() : Graphics::TextureManager(), Array() {} - virtual ~TextureManager(); - - virtual Texture & operator[](const LUint & at); - }; - static TextureManager textures; - - static Graphics::Colour GetGraphicsColour(const Piece::Colour & colour) - { - switch (colour) - { - case RED: - return Graphics::Colour(1,0,0); - break; - case BLUE: - return Graphics::Colour(0,0,1); - break; - case NONE: - return Graphics::Colour(0.5,0.5,0.5); - break; - case BOTH: - return Graphics::Colour(1,0,1); - break; - } - } - - - - - -}; - -#include "movementresult.h" - -/** - * A Stratego board - */ -class Board -{ - public: - Board(int newWidth, int newHeight); //Constructor - - virtual ~Board(); //Destructor - - void Print(FILE * stream, const Piece::Colour & reveal=Piece::BOTH); //Print board - void PrintPretty(FILE * stream, const Piece::Colour & reveal=Piece::BOTH); //Print board using colour - - void Draw(const Piece::Colour & reveal=Piece::BOTH, bool showRevealed = true); //Draw board - - - bool AddPiece(int x, int y, const Piece::Type & newType, const Piece::Colour & newColour); //Add piece to board - - - Piece * GetPiece(int x, int y); //Retrieve piece from a location on the board - - - typedef enum {UP, DOWN, LEFT, RIGHT} Direction; - - - - static bool LegalResult(const MovementResult & result) - { - return (result == MovementResult::OK || result == MovementResult::DIES || result == MovementResult::KILLS || result == MovementResult::BOTH_DIE || result == MovementResult::VICTORY || result == MovementResult::DRAW || result == MovementResult::DRAW_DEFAULT || result == MovementResult::SURRENDER); - } - - static bool HaltResult(const MovementResult & result) - { - return (result == MovementResult::VICTORY || result == MovementResult::DRAW || result == MovementResult::DRAW_DEFAULT || result == MovementResult::SURRENDER || !LegalResult(result)); - } - - MovementResult MovePiece(int x, int y, const Direction & direction, int multiplier=1,const Piece::Colour & colour=Piece::NONE); //Move piece from position in direction - - - Piece::Colour winner; - - int Width() const {return width;} - int Height() const {return height;} - - int MobilePieces(const Piece::Colour & colour) const; - int TotalPieceValue(const Piece::Colour & colour) const; - bool RemovePiece(Piece * piece); - private: - int width; - int height; - Piece ** * board; - std::vector pieces; -}; - -#endif //STRATEGO_H - -//EOF - - diff --git a/home/progcomp/judge/manager/thread_util.cpp b/home/progcomp/judge/manager/thread_util.cpp deleted file mode 100644 index 2d12438..0000000 --- a/home/progcomp/judge/manager/thread_util.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "thread_util.h" - -#include -#include - -using namespace std; - -pthread_mutex_t GetterThread::mutex = PTHREAD_MUTEX_INITIALIZER; - -void * GetterThread::GetMessage(void * p) -{ - - GetterThread * getter = (GetterThread*)(p); - - stringstream inputStream; - - char s = fgetc(getter->stream); - while (s != '\n' && s != EOF) - { - - inputStream << s; - s = fgetc(getter->stream); - } - if (s == EOF) - { - getter->buffer = ""; - getter->buffer += s; - return NULL; - } - - pthread_mutex_lock(&mutex); - getter->buffer = inputStream.str(); - pthread_mutex_unlock(&mutex); - - getter->finished = true; - - return NULL; -} - -void * TimerThread::Timeout(void * p) -{ - TimerThread * timer = (TimerThread*)(p); - usleep(timer->count); - timer->finished = true; - return NULL; -} - diff --git a/home/progcomp/judge/manager/thread_util.h b/home/progcomp/judge/manager/thread_util.h deleted file mode 100644 index 95a16e0..0000000 --- a/home/progcomp/judge/manager/thread_util.h +++ /dev/null @@ -1,115 +0,0 @@ -#ifndef THREAD_UTIL_H -#define THREAD_UTIL_H - -#include -#include - -#include -#include -#include //Needed for threading -#include //Needed for killing the threads (why not in pthread.h?) - -#include - - -class Thread -{ - public: - Thread() : finished(false), thread(0), started(false) - { - - } - - virtual void Start() = 0; - protected: - void Start(void* (ThreadFunction)(void*)) - { - assert(!started); - started = true; - pthread_create(&(thread), NULL, ThreadFunction, (void*)(this)); - pthread_setcancelstate(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - } - - public: - void Stop() - { - assert(started); - if (!finished) - pthread_cancel(thread); - - pthread_join(thread, NULL); - started = false; - } - - virtual ~Thread() - { - if (started) - Stop(); - } - - bool Finished() const {return finished;} - protected: - - bool finished; - - private: - pthread_t thread; - protected: - bool started; -}; - -class GetterThread : public Thread -{ - public: - GetterThread(FILE * newStream, std::string & newBuffer) : Thread(), stream(newStream), buffer(newBuffer) - { - - } - - virtual ~GetterThread() - { - - } - - virtual void Start() {assert(&buffer != NULL); Thread::Start(GetMessage);} - - private: - FILE * stream; - public: - std::string & buffer; - - pthread_t thread; - static pthread_mutex_t mutex; - static void * GetMessage(void * p); - -}; - - -class TimerThread : public Thread -{ - public: - TimerThread(int newCount) : Thread(), count(newCount) - { - - } - - virtual ~TimerThread() - { - - } - - virtual void Start() {Thread::Start(Timeout);} - - private: - int count; - - static void * Timeout(void * p); - -}; - -#endif //THREAD_UTIL_H - -//EOF - - - diff --git a/home/progcomp/judge/simulator/Makefile b/home/progcomp/judge/simulator/Makefile deleted file mode 100644 index 1d71ef5..0000000 --- a/home/progcomp/judge/simulator/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -#Makefile for simulations -# Not used for building simulate.py -# Used for building/removing results - -BASEDIR = /home/sam/Documents/progcomp2012/ -RESULTSDIR = /home/sam/Documents/progcomp2012/results -LOGDIR = /home/sam/Documents/progcomp2012/log -AGENTSDIR = /home/sam/Documents/progcomp2012/samples -MANAGER = /home/sam/Documents/progcomp2012/manager/stratego - - - -clean: - rm -r -f $(RESULTSDIR) - rm -r -f $(LOGDIR) diff --git a/home/progcomp/judge/simulator/simulate.py b/home/progcomp/judge/simulator/simulate.py deleted file mode 100755 index 32f2b42..0000000 --- a/home/progcomp/judge/simulator/simulate.py +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/python -u - -''' - simulate.py - simulation script for the 2012 UCC Programming Competition - NOTE: This is not the manager program for a stratego game - It merely calls the manager program as appropriate, and records results - Plays exactly ONE round, but does not overwrite previously played rounds - eg: run once to generate round1.results, twice to generate round2.results etc - Also generates total.scores based on results from every round. - - - author Sam Moore (matches) [SZM] - website http://matches.ucc.asn.au/stratego - email progcomp@ucc.asn.au or matches@ucc.asn.au - git git.ucc.asn.au/progcomp2012.git -''' - -import os -import sys - -baseDirectory = "/home/sam/Documents/progcomp2012/" -resultsDirectory = baseDirectory+"results/" #Where results will go (results are in the form of text files of agent names and scores) -agentsDirectory = baseDirectory+"samples/" #Where agents are found (each agent has its own directory) -logDirectory = baseDirectory+"log/" #Where log files go -nGames = 10 #Number of games played by each agent against each opponent. Half will be played as RED, half as BLUE -managerPath = baseDirectory+"manager/stratego" #Path to the manager program - - -scores = {"VICTORY":(3,1), "DEFEAT":(1,3), "SURRENDER":(0,3), "DRAW":(2,2), "DRAW_DEFAULT":(1,1), "ILLEGAL":(-1,2), "DEFAULT":(2,-1), "BOTH_ILLEGAL":(-1,-1), "INTERNAL_ERROR":(0,0)} #Score dictionary - -verbose = True - - -#Make necessary directories -if os.path.exists(resultsDirectory) == False: - os.mkdir(resultsDirectory) #Make the results directory if it didn't exist -#Identify the round number by reading the results directory -roundNumber = len(os.listdir(resultsDirectory)) + 1 -if roundNumber > 1: - roundNumber -= 1 - -if os.path.exists(logDirectory) == False: - os.mkdir(logDirectory) #Make the log directory if it didn't exist - - - -if os.path.exists(logDirectory + "round"+str(roundNumber)) == False: - os.mkdir(logDirectory + "round"+str(roundNumber)) #Check there is a directory for this round's logs - -print "Simulating ROUND " +str(roundNumber) -print "Identifying possible agents in \""+agentsDirectory+"\"" - -#Get all agent names from agentsDirectory -agentNames = os.listdir(agentsDirectory) -agents = [] -for name in agentNames: - #sys.stdout.write("\nLooking at Agent: \""+ str(name)+"\"... ") - if verbose: - sys.stdout.write("Scan \""+name+"\"... ") - if os.path.isdir(agentsDirectory+name) == False: #Remove non-directories - if verbose: - sys.stdout.write(" Invalid! (Not a directory)\n") - continue - - if os.path.exists(agentsDirectory+name+"/info") == False: #Try and find the special "info" file in each directory; ignore if it doesn't exist - if verbose: - sys.stdout.write(" Invalid! (No \"info\" file found)\n") - continue - if verbose: - sys.stdout.write(" Valid!") - #sys.stdout.write("OK") - #Convert the array of names to an array of triples - #agents[0] - The name of the agent (its directory) - #agents[1] - The path to the program for the agent (typically agentsDirectory/agent/agent). Read from agentsDirectory/agent/info file - #agents[2] - The score the agent achieved in _this_ round. Begins at zero - agentExecutable = agentsDirectory+name+"/"+(open(agentsDirectory+name+"/info").readline().strip()) - agents.append([name, agentExecutable, 0]) - if verbose: - sys.stdout.write(" (Run program \""+agentExecutable+"\")\n") - -if len(agents) == 0: - print "Couldn't find any agents! Check paths (Edit this script) or generate \"info\" files for agents." - sys.exit(0) -if verbose: - print "Total: " + str(len(agents)) + " valid agents found (From "+str(len(agentNames))+" possibilities)" - print "" - print "Commencing ROUND " + str(roundNumber) + " combat! This could take a while... ("+str(nGames)+" games per pairing * " + str(len(agents) * len(agents)-1) + " pairings = " + str((len(agents) * len(agents)-1) * nGames) + " games)" - - -normalGames = 0 -draws = 0 -aiErrors = 0 -managerErrors = 0 -#This double for loop simulates a round robin, with each agent getting the chance to play as both red and blue against every other agent. -for red in agents: #for each agent playing as red, - for blue in agents: #against each other agent, playing as blue - if red == blue: - continue #Exclude battles against self - for i in range(1, nGames/2 + 1): - #Play a game and read the result. Note the game is logged to a file based on the agent's names - if verbose: - sys.stdout.write("Agents: \""+red[0]+"\" and \""+blue[0]+"\" playing game " + str(i) + "/"+str(nGames/2) + "... ") - logFile = logDirectory + "round"+str(roundNumber) + "/"+red[0]+"_vs_"+blue[0]+"_"+str(i) - outline = os.popen(managerPath + " -o " + logFile + " " + red[1] + " " + blue[1], "r").read() - results = outline.split(' ') - - if len(results) != 6: - if verbose: - sys.stdout.write("Garbage output! " + outline) - else: - if results[1] == "RED": - red[2] += scores[results[2]][0] - blue[2] += scores[results[2]][1] - elif results[1] == "BLUE": - red[2] += scores[results[2]][1] - blue[2] += scores[results[2]][0] - elif results[1] == "BOTH": - red[2] += scores[results[2]][0] - blue[2] += scores[results[2]][1] - red[2] += scores[results[2]][1] - blue[2] += scores[results[2]][0] - - if verbose: - sys.stdout.write(" " + outline) - - - - - - -if verbose: - print "Completed combat. Total of " + str(normalGames + draws + aiErrors + managerErrors) + " games played. " -if managerErrors != 0: - print "WARNING: Recieved "+str(managerErrors)+" garbage outputs. Check the manager program." - -if verbose: - print "" -#We should now have complete score values. - -if verbose: - sys.stdout.write("Creating results files for ROUND " + str(roundNumber) + "... ") - -agents.sort(key = lambda e : e[2], reverse=True) #Sort the agents based on score - -resultsFile = open(resultsDirectory+"round"+str(roundNumber)+".results", "w") #Create a file to store all the scores for this round -for agent in agents: - resultsFile.write(agent[0] + " " + str(agent[2]) +"\n") #Write the agent names and scores into the file, in descending order - -if verbose: - sys.stdout.write(" Complete!\n") - sys.stdout.write("Updating total scores... "); - -#Now update the total scores -if os.path.exists(resultsDirectory+"total.scores"): - if verbose: - sys.stdout.write(" Reading from \""+resultsDirectory+"total.scores\" to update scores... ") - totalFile = open(resultsDirectory+"total.scores", "r") #Try to open the total.scores file - for line in totalFile: #For all entries, - data = line.split(' ') - for agent in agents: - if agent[0] == data[0]: - agent.append(agent[2]) #Store the score achieved this round at the end of the list - agent[2] += int(data[1]) #Simply increment the current score by the recorded total score of the matching file entry - break - totalFile.close() #Close the file, so we can delete it - os.remove(resultsDirectory+"total.scores") #Delete the file - #Sort the agents again - agents.sort(key = lambda e : e[2], reverse=True) - -else: - if verbose: - sys.stdout.write(" First round - creating \""+resultsDirectory+"total.scores\"... ") -if verbose: - sys.stdout.write(" Complete!\n") - print "Finished writing results for ROUND " + str(roundNumber) - print "" - - -print "RESULTS FOR ROUND " + str(roundNumber) -print "Agent: [name, path, total_score, recent_score]" - -totalFile = open(resultsDirectory+"total.scores", "w") #Recreate the file -for agent in agents: - totalFile.write(agent[0] + " " + str(agent[2]) +"\n") #Write the total scores in descending order - print "Agent: " + str(agent) - - -#I just want to say the even though I still think python is evil, it is much better than bash. Using bash makes me cry. - diff --git a/home/progcomp/web/index.html b/home/progcomp/web/index.html deleted file mode 100644 index d181a14..0000000 --- a/home/progcomp/web/index.html +++ /dev/null @@ -1,73 +0,0 @@ - - - Stratego Based Programming Competition - - - - -

Quick Details

-

git

-

The git repository is listed on The UCC git page as "progcomp2012.git"

-

Direct Link Here

- -

Mailing List

-

We will use the same mailing list as last year (progcomp).

- -

irc channel

-

There is a #progcomp irc channel on the ucc irc server (irc.ucc.asn.au) where you can ask questions or help with setting things up.

- -

Programming Competition VM

-

I am in the process of learning how to set up a VM for this competition. Please be patient.

- -

Stratego

-

This site explains what Stratego is.

- -

Short Version: It is a board game in which all pieces have a value that is, initially, unknown to the opponent player. The objective is to destroy all enemy pieces, or capture the enemy "Flag". Pieces with higher values destroy pieces with lower values. There are several special pieces/rules.

- - -

Programming Competition

-

Create an AI to play Stratego.

-

Programs are written independently and interface through stdin/stdout with a manager program, which queries them on setup and moves.

- -

The Manager Program

-

The manager program provides the protocol for two seperate AI to play a game of stratego. It has the imaginative name of 'stratego', but I will probably refer to it as 'the manager program' or 'stratego' with absolutely no consistency.

-

It also aims to assist with AI design by providing options for graphical or terminal output and saving/reading games from files

-

-

Human players are also supported, although the interface is minimal, as this feature is meant for testing.

-

If you just want to play a game, without having to write your own AI, try Probe

- -

Screenshot

-Graphical output of 'stratego' manager program. - -

Protocol

-

For the sake of simplicity and keeping things in one place, the protocol is now entirely described in the manual page of the manager program. All updates to the protocol will be reflected in that file.

- - -

Warning: The accuracy of the above file depends on how recently I pulled it from git. To ensure you get the latest version, find it under "manager/manual.txt" in the git repository

- -

Long Term Scoring

-

WARNING: Work in progress

-

It is currently planned to store all the AIs on a virtual machine, and periodically run a script to play a round robin

-

The scores for each round and the total scores will be recorded from the start to the end of the competition.

-

The competition will run over a period of weeks (depending on general enthusiasm...), and competitors will be able to alter their programs except during the periods in which the script is running.

-

The following categories will be used when determining the final winners:

-

    -
  1. Total score
  2. -
  3. Number of rounds won
  4. -

- -

Sample AI Programs

-

WARNING: Work in progress

-

The following sample programs are currently available (and in a working state - refer to the git repository):

- - - - - -
Name Language Moves Considers...
basic_python Python Randomised
basic_cpp C++ Randomised
asmodeus Python Scored Path finding, known combat results, piece values
-

It is planned to implement the equivelants of these samples in C++ and Python at least, possibly other languages later.

- -

Last webpage update: 7/12/11

- - - diff --git a/home/progcomp/web/screenshot.png b/home/progcomp/web/screenshot.png deleted file mode 100644 index 276d892..0000000 Binary files a/home/progcomp/web/screenshot.png and /dev/null differ diff --git a/progcomp/agents/asmodeus/asmodeus.py b/progcomp/agents/asmodeus/asmodeus.py new file mode 100644 index 0000000..29c50a2 --- /dev/null +++ b/progcomp/agents/asmodeus/asmodeus.py @@ -0,0 +1,81 @@ +#!/usr/bin/python -u + +#NOTE: The -u option is required for unbuffered stdin/stdout. +# If stdin/stdout are buffered, the manager program will not recieve any messages and assume that the agent has timed out. + +''' + asmodeus.py - A sample Stratego AI for the UCC Programming Competition 2012 + + Written in python, the slithery language + + author Sam Moore (matches) [SZM] + website http://matches.ucc.asn.au/stratego + email progcomp@ucc.asn.au or matches@ucc.asn.au + git git.ucc.asn.au/progcomp2012.git +''' + +from basic_python import * +from path import * + + + +class Asmodeus(BasicAI): + " A slightly more advanced python based AI who calculates the optimum score for each move " + def __init__(self): + #sys.stderr.write("Asmodeus initialised...\n") + BasicAI.__init__(self) + self.riskScores = {'1' : 0.01 , '2' : 0.05 , '3' : 0.15 , '4' : 0.2, '5' : 0.2, '6' : 0.25, '7' : 0.25, '8' : 0.01 , '9' : 0.4, 's' : 0.01} + self.bombScores = {'1' : 0.0 , '2' : 0.0 , '3' : 0.05 , '4' : 0.1, '5' : 0.3, '6' : 0.4, '7' : 0.5, '8' : 1 , '9' : 0.6, 's' : 0.1} + self.flagScores = {'1' : 1.0 , '2' : 1.0 , '3' : 1.0 , '4' : 1.0, '5' : 1.0, '6' : 1.0, '7' : 1, '8' : 1.0 , '9' : 1.0, 's' : 1.0} + self.suicideScores = {'1' : 0.0 , '2' : 0.0 , '3' : 0.0, '4' : 0.0, '5' : 0.0, '6' : 0.05, '7' : 0.1, '8' : 0.0 , '9' : 0.0, 's' : 0.0} + self.killScores = {'1' : 1.0 , '2' : 0.9 , '3' : 0.8 , '4' : 0.5, '5' : 0.5, '6' : 0.5, '7' : 0.4, '8' : 0.9 , '9' : 0.6, 's' : 0.9} + + def MakeMove(self): + #sys.stderr.write("Asmodeus MakingMove...\n") + "Over-rides the default BasicAI.MakeMove function" + + moveList = [] + + for unit in self.units: + if unit.mobile() == False: + continue + + for enemy in self.enemyUnits: + if enemy == unit: + continue + path = PathFinder().pathFind((unit.x, unit.y), (enemy.x, enemy.y), self.board) + + #sys.stderr.write("Computed path: " + str(path) + "\n") + if path == False or len(path) <= 0: + continue + score = self.CalculateScore(unit, enemy) + + score = float(score / float(len(path) + 1)) + moveList.append([unit, path, enemy, score]) + + + + if len(moveList) <= 0: + #sys.stderr.write("NO Moves!\n") + return BasicAI.MakeMove(self) + + moveList.sort(key = lambda e : e[len(e)-1], reverse=True) + + #sys.stderr.write("Chosen move is: " + str(moveList[0][0].x) + " " + str(moveList[0][0].y) + " " + moveList[0][1][0] + " (targeting enemy with rank " + moveList[0][2].rank + " at position " + str(moveList[0][2].x) + " " + str(moveList[0][2].y) + " (my rank " + moveList[0][0].rank+")\n") + print str(moveList[0][0].x) + " " + str(moveList[0][0].y) + " " + moveList[0][1][0] + return True + + def CalculateScore(self, attacker, defender): + if defender.rank == '?': + return self.riskScores[attacker.rank] + elif defender.rank == 'B': + return self.bombScores[attacker.rank] + elif defender.rank == 'F': + return self.flagScores[attacker.rank] + elif defender.valuedRank() < attacker.valuedRank() or defender.rank == '1' and attacker.rank == 's': + return self.killScores[defender.rank] + else: + return self.suicideScores[attacker.rank] + + + diff --git a/progcomp/agents/asmodeus/basic_python.py b/progcomp/agents/asmodeus/basic_python.py new file mode 120000 index 0000000..3d6b342 --- /dev/null +++ b/progcomp/agents/asmodeus/basic_python.py @@ -0,0 +1 @@ +../basic_python/basic_python.py \ No newline at end of file diff --git a/progcomp/agents/asmodeus/info b/progcomp/agents/asmodeus/info new file mode 100644 index 0000000..2ce2124 --- /dev/null +++ b/progcomp/agents/asmodeus/info @@ -0,0 +1 @@ +run.py diff --git a/progcomp/agents/asmodeus/path.py b/progcomp/agents/asmodeus/path.py new file mode 100644 index 0000000..3f08979 --- /dev/null +++ b/progcomp/agents/asmodeus/path.py @@ -0,0 +1,57 @@ + +import sys +import random + + +class PathFinder: + def __init__(self): + self.visited = [] + + pass + + def pathFind(self, start, end, board): + if start[0] == end[0] and start[1] == end[1]: + #sys.stderr.write("Got to destination!\n") + return [] + + if self.visited.count(start) > 0: + #sys.stderr.write("Back track!!\n") + return False + if start[0] < 0 or start[0] >= len(board) or start[1] < 0 or start[1] >= len(board[start[0]]): + #sys.stderr.write("Out of bounds!\n") + return False + if len(self.visited) > 0 and board[start[0]][start[1]] != None: + #sys.stderr.write("Full position!\n") + return False + + + + self.visited.append(start) + left = (start[0]-1, start[1]) + right = (start[0]+1, start[1]) + up = (start[0], start[1]-1) + down = (start[0], start[1]+1) + choices = [left, right, up, down] + choices.sort(key = lambda e : random.randint(0,5)) + options = [] + for point in choices: + option = [point, self.pathFind(point,end,board)] + if option[1] != False: + options.append(option) + + options.sort(key = lambda e : len(e[1])) + if len(options) == 0: + #sys.stderr.write("NO options!\n") + return False + else: + if options[0][0] == left: + options[0][1].insert(0,"LEFT") + elif options[0][0] == right: + options[0][1].insert(0,"RIGHT") + elif options[0][0] == up: + options[0][1].insert(0,"UP") + elif options[0][0] == down: + options[0][1].insert(0,"DOWN") + #sys.stderr.write("PathFind got path " + str(options[0]) + "\n") + return options[0][1] + diff --git a/progcomp/agents/asmodeus/run.py b/progcomp/agents/asmodeus/run.py new file mode 100755 index 0000000..b646d06 --- /dev/null +++ b/progcomp/agents/asmodeus/run.py @@ -0,0 +1,8 @@ +#!/usr/bin/python -u + +from asmodeus import * + +asmodeus = Asmodeus() +if asmodeus.Setup(): + while asmodeus.MoveCycle(): + pass diff --git a/progcomp/agents/basic_cpp/Makefile b/progcomp/agents/basic_cpp/Makefile new file mode 100644 index 0000000..0a249b3 --- /dev/null +++ b/progcomp/agents/basic_cpp/Makefile @@ -0,0 +1,30 @@ +#Makefile for basic_cpp +# Sample C++ Stratego AI +# UCC Programming Competition 2012 + +CPP = g++ -Wall -pedantic -lSDL -lGL -g +OBJ = basic_cpp.o + +BIN = basic_cpp + + + +$(BIN) : $(OBJ) + $(CPP) -o $(BIN) $(OBJ) + + + + +%.o : %.cpp %.h + $(CPP) -c $< + +clean : + $(RM) $(BIN) $(OBJ) $(LINKOBJ) + +#Cleans up all backup files +clean_full: + $(RM) $(BIN) $(OBJ) $(LINKOBJ) + $(RM) *.*~ + $(RM) *~ + + diff --git a/progcomp/agents/basic_cpp/basic_cpp.cpp b/progcomp/agents/basic_cpp/basic_cpp.cpp new file mode 100644 index 0000000..ab00826 --- /dev/null +++ b/progcomp/agents/basic_cpp/basic_cpp.cpp @@ -0,0 +1,560 @@ +/** + * "basic_cpp", a sample Stratego AI for the UCC Programming Competition 2012 + * Implementations of main function, and Helper functions + * + * @author Sam Moore (matches) [SZM] + * @website http://matches.ucc.asn.au/stratego + * @email progcomp@ucc.asn.au or matches@ucc.asn.au + * @git git.ucc.asn.au/progcomp2012.git + */ + +#include "basic_cpp.h" //Needs class Base_Cpp and the includes in this file + +using namespace std; + +/** + * The characters used to represent various pieces + * NOTHING, BOULDER, FLAG, SPY, SCOUT, MINER, SERGEANT, LIETENANT, CAPTAIN, MAJOR, COLONEL, GENERAL, MARSHAL, BOMB, UNKNOWN + */ +char Piece::tokens[] = {'.','*','F','s','9','8','7','6','5','4','3','2','1','B','?'}; + +/** + * Gets a rank from the character used to represent it + * Basic lookup of Piece::tokens + */ +Rank Piece::GetRank(char token) +{ + for (int ii=0; ii <= 14; ++ii) + { + if (tokens[ii] == token) + return (Rank)(ii); + } + return UNKNOWN; +} + +/** + * IMPLEMENTATION of Helper FOLLOWS + */ + +/** + * Convert string to direction + */ +Direction Helper::StrToDir(const string & dir) +{ + if (dir == "UP") + return UP; + else if (dir == "DOWN") + return DOWN; + else if (dir == "LEFT") + return LEFT; + else if (dir == "RIGHT") + return RIGHT; + else + return DIRECTION_ERROR; +} + +/** + * Direction to String + */ +void Helper::DirToStr(const Direction & dir, std::string & buffer) +{ + switch (dir) + { + case UP: + buffer = "UP"; + break; + case DOWN: + buffer = "DOWN"; + break; + case LEFT: + buffer = "LEFT"; + break; + case RIGHT: + buffer = "RIGHT"; + break; + default: + buffer = "DIRECTION_ERROR"; + break; + } +} + +/** + * Move a point in a direction + */ +void Helper::MoveInDirection(int & x, int & y, const Direction & dir, int multiplier) +{ + switch (dir) + { + case UP: + y -= multiplier; + break; + case DOWN: + y += multiplier; + break; + case LEFT: + x -= multiplier; + break; + case RIGHT: + x += multiplier; + break; + default: + break; + } + +} + +/** + * Tokenise a string + */ +int Helper::Tokenise(std::vector & buffer, std::string & str, char split) +{ + string token = ""; + for (unsigned int x = 0; x < str.size(); ++x) + { + if (str[x] == split && token.size() > 0) + { + buffer.push_back(token); + token = ""; + } + if (str[x] != split) + token += str[x]; + } + if (token.size() > 0) + buffer.push_back(token); + return buffer.size(); +} + +/** + * Convert string to integer + */ +int Helper::Integer(std::string & fromStr) +{ + stringstream s(fromStr); + int result = 0; + s >> result; + return result; +} + +/** + * Read in a line from stdin + */ +void Helper::ReadLine(std::string & buffer) +{ + buffer = ""; + for (char c = cin.get(); c != '\n' && cin.good(); c = cin.get()) + { + buffer += c; + } +} + +/** + * IMPLEMENTATION of Board FOLLOWS + */ + +/** + * Constructer for Board + */ +Board::Board(int w, int h) : width(w), height(h), board(NULL) +{ + //Construct 2D array of P*'s + board = new Piece**[width]; + for (int x=0; x < width; ++x) + { + board[x] = new Piece*[height]; + for (int y=0; y < height; ++y) + board[x][y] = NULL; + } +} + +/** + * Destructor for board + */ +Board::~Board() +{ + //Destroy the 2D array of P*'s + for (int x=0; x < width; ++x) + { + for (int y=0; y < height; ++y) + delete board[x][y]; + delete [] board[x]; + } +} + +/** + * Retrieves a piece on the Board + * @param x x coordinate + * @param y y coordinate + * @returns A Piece* for the piece, or NULL if there is no piece at the point given + */ +Piece * Board::Get(int x, int y) const +{ + if (ValidPosition(x, y)) + return board[x][y]; + return NULL; +} +/** + * Sets a piece on the Board + * @param x x coordinate + * @param y y coordinate + * @param newPiece + * @param returns newPiece if successful, NULL if not + */ +Piece * Board::Set(int x, int y, Piece * newPiece) +{ + if (!ValidPosition(x, y)) + return NULL; + board[x][y] = newPiece; + assert(Get(x,y) == newPiece); + return newPiece; +} + +/** + * IMPLEMENTATION of Base_Cpp FOLLOWS + */ + +/** + * Constructor for AI + */ +BasicAI::BasicAI() : turn(0), board(NULL), units(), enemyUnits(), colour(NONE), colourStr("") +{ + srand(time(NULL)); + cin.rdbuf()->pubsetbuf(NULL, 0); + cout.rdbuf()->pubsetbuf(NULL, 0); +} + +/** + * Destructor for AI + */ +BasicAI::~BasicAI() +{ + if (board != NULL) + delete board; +} + + +/** + * Setup the AI + * @returns true if successful, false on error + */ +bool BasicAI::Setup() +{ + + cin >> colourStr; + + + std::string opponentName(""); //opponentName is unused, just read it + cin >> opponentName; + + int width = 0; int height = 0; + cin >> width; cin >> height; + + while(cin.get() != '\n' && cin.good()); //trim newline + + board = new Board(width, height); + + if (colourStr == "RED") + { + colour = RED; + cout << "FB8sB479B8\nBB31555583\n6724898974\n967B669999\n"; + } + else if (colourStr == "BLUE") + { + colour = BLUE; + cout << "967B669999\n6724898974\nBB31555583\nFB8sB479B8\n"; + } + else + return false; + + return (board != NULL); +} + +/** + * Performs a move, including the saving states bits + * @returns true if the game is to continue, false if it is to end + */ +bool BasicAI::MoveCycle() +{ + //cerr << "BasicAI at MoveCycle()\n"; + if (!InterpretResult()) + return false; + if (!ReadBoard()) + return false; + if (!MakeMove()) + return false; + + turn++; + return InterpretResult(); +} + +/** + * Interprets the result of a move. Ignores the first move + * @returns true if successful, false if there was an error + */ +bool BasicAI::InterpretResult() +{ + //cerr << "BasicAI at InterpretResult()\n"; + if (turn == 0) + { + while (cin.get() != '\n' && cin.good()); + return true; + } + + + string resultLine; Helper::ReadLine(resultLine); + vector tokens; Helper::Tokenise(tokens, resultLine, ' '); + + if (tokens.size() <= 0) + { + //cerr << "No tokens!\n"; + return false; + } + + if (tokens[0] == "QUIT") + { + return false; + } + + if (tokens[0] == "NO_MOVE") + { + return true; + + } + + if (tokens.size() < 4) + { + //cerr << "Only got " << tokens.size() << " tokens\n"; + return false; + } + + + int x = Helper::Integer(tokens[0]); + int y = Helper::Integer(tokens[1]); + + + + Direction dir = Helper::StrToDir(tokens[2]); + string & outcome = tokens[3]; + + int x2 = x; int y2 = y; Helper::MoveInDirection(x2,y2,dir); + + Piece * attacker = board->Get(x,y); + if (attacker == NULL) + { + //cerr << "No attacker!\n"; + return false; + } + Piece * defender = board->Get(x2,y2); + if (outcome == "OK") + { + board->Set(x2,y2, attacker); + board->Set(x,y,NULL); + attacker->x = x2; attacker->y = y2; + } + else if (outcome == "KILLS") + { + if (defender == NULL) + { + //cerr << "No defender!\n"; + return false; + } + + board->Set(x2,y2, attacker); + board->Set(x,y,NULL); + attacker->x = x2; attacker->y = y2; + + attacker->rank = Piece::GetRank(tokens[4][0]); + ForgetUnit(defender); + } + else if (outcome == "DIES") + { + if (defender == NULL) + { + //cerr << "No defender!\n"; + return false; + } + + + board->Set(x,y,NULL); + defender->rank = Piece::GetRank(tokens[5][0]); + ForgetUnit(attacker); + + + } + else if (outcome == "BOTHDIE") + { + board->Set(x,y,NULL); + board->Set(x2,y2, NULL); + + ForgetUnit(attacker); + ForgetUnit(defender); + } + else if (outcome == "FLAG") + { + //cerr << "BasicAI - Flag was captured, exit!\n"; + return false; + } + else if (outcome == "ILLEGAL") + { + //cerr << "BasicAI - Illegal move, exit!\n"; + return false; + } + + //cerr << "BasicAI finished InterpretResult()\n"; + return true; +} + +/** + * Performs a random move + * TODO: Overwrite with custom move + * @returns true if a move could be made (including NO_MOVE), false on error + */ +bool BasicAI::MakeMove() +{ + //cerr << "BasicAI at MakeMove()\n"; + if (units.size() <= 0) + { + //cerr << " No units!\n"; + return false; + + } + + int index = rand() % units.size(); + int startIndex = index; + while (true) + { + + + Piece * piece = units[index]; + if (piece != NULL && piece->Mobile()) + { + int dirIndex = rand() % 4; + int startDirIndex = dirIndex; + while (true) + { + int x = piece->x; int y = piece->y; + assert(board->Get(x,y) == piece); + Helper::MoveInDirection(x,y,(Direction)(dirIndex)); + if (board->ValidPosition(x,y)) + { + Piece * target = board->Get(x,y); + if (target == NULL || (target->colour != piece->colour && target->colour != NONE)) + { + string dirStr; + Helper::DirToStr((Direction)(dirIndex), dirStr); + cout << piece->x << " " << piece->y << " " << dirStr << "\n"; + return true; + } + } + + dirIndex = (dirIndex + 1) % 4; + if (dirIndex == startDirIndex) + break; + } + } + + index = (index+1) % (units.size()); + if (index == startIndex) + { + cout << "NO_MOVE\n"; + return true; + } + } + return true; +} + +/** + * Reads in the board + * On first turn, sets up Board + * On subsquent turns, takes no action + * @returns true on success, false on error + */ +bool BasicAI::ReadBoard() +{ + //cerr << "BasicAI at ReadBoard()\n"; + for (int y = 0; y < board->Height(); ++y) + { + string row; + Helper::ReadLine(row); + for (unsigned int x = 0; x < row.size(); ++x) + { + if (turn == 0) + { + switch (row[x]) + { + case '.': + break; + case '#': + board->Set(x,y, new Piece(x,y,Piece::Opposite(colour), UNKNOWN)); + enemyUnits.push_back(board->Get(x,y)); + break; + case '+': + board->Set(x,y, new Piece(x,y,NONE, BOULDER)); + break; + default: + board->Set(x,y,new Piece(x,y,colour, Piece::GetRank(row[x]))); + units.push_back(board->Get(x,y)); + break; + } + } + } + } + return true; +} + +/** + * Removes a piece from memory + * @param piece The piece to delete + * @returns true if the piece was actually found + */ +bool BasicAI::ForgetUnit(Piece * piece) +{ + //cerr << "BasicAI at ForgetUnit()\n"; + bool result = false; + vector::iterator i = units.begin(); + while (i != units.end()) + { + if ((*i) == piece) + { + i = units.erase(i); result = true; + continue; + } + ++i; + } + + i = enemyUnits.begin(); + while (i != enemyUnits.end()) + { + if ((*i) == piece) + { + i = enemyUnits.erase(i); result = true; + continue; + } + ++i; + } + + + delete piece; + return result; +} + + +/** + * The main function + * @param argc + * @param argv + * @returns zero on success, non-zero on failure + */ +int main(int argc, char ** argv) +{ + + srand(time(NULL)); + + BasicAI * basicAI = new BasicAI(); + if (basicAI->Setup()) + { + while (basicAI->MoveCycle()); + } + delete basicAI; + exit(EXIT_SUCCESS); + return 0; +} diff --git a/progcomp/agents/basic_cpp/basic_cpp.h b/progcomp/agents/basic_cpp/basic_cpp.h new file mode 100644 index 0000000..733d9a7 --- /dev/null +++ b/progcomp/agents/basic_cpp/basic_cpp.h @@ -0,0 +1,145 @@ +/** + * "basic_cpp", a sample Stratego AI for the UCC Programming Competition 2012 + * Declarations for classes Piece, Board and Basic_Cpp + * @author Sam Moore (matches) [SZM] + * @website http://matches.ucc.asn.au/stratego + * @email progcomp@ucc.asn.au or matches@ucc.asn.au + * @git git.ucc.asn.au/progcomp2012.git + */ + +#ifndef BASIC_CPP_H +#define BASIC_CPP_H + +#include +#include +#include +#include +#include +#include + + + + + +/** + * enum for possible ranks of pieces + */ +typedef enum {UNKNOWN=14,BOMB=13,MARSHAL=12, GENERAL=11, COLONEL=10, MAJOR=9, CAPTAIN=8, LIEUTENANT=7, SERGEANT=6, MINER=5, SCOUT=4, SPY=3, FLAG=2,BOULDER=1, NOTHING=0} Rank; + +/** + * enum for possible colours of pieces and the AI + */ +typedef enum {RED=0, BLUE=1, NONE, BOTH} Colour; + + +/** + * Class to represent a piece on the board + * TODO: Add features required for Pieces as used by your AI + * For example, can replace the single member "rank" with two, "maxRank" and "minRank" + * Or add an array of probability values for EVERY rank! + */ +class Piece +{ + public: + static char tokens[]; //The tokens used to identify various pieces + + Piece(int newX, int newY,const Colour & newColour, const Rank & newRank = UNKNOWN) + : x(newX), y(newY), colour(newColour), rank(newRank) {} + virtual ~Piece() {} + + bool Mobile() const {return rank != BOMB && rank != FLAG;} + + static Colour Opposite(const Colour & colour) {return colour == RED ? BLUE : RED;} + + int x; int y; + const Colour colour; //The colour of the piece + Rank rank; //The rank of the piece + + static Rank GetRank(char token); //Helper to get rank from character + +}; + +/** + * enum for Directions that a piece can move in + */ +typedef enum {UP=0, DOWN=1, LEFT=2, RIGHT=3, DIRECTION_ERROR=4} Direction; + +/** + * Class to represent a board + */ +class Board +{ + public: + Board(int width, int height); //Construct a board with width and height + virtual ~Board(); //Destroy the board + + Piece * Get(int x, int y) const; //Retrieve single piece + Piece * Set(int x, int y, Piece * newPiece); //Add piece to board + + int Width() const {return width;} //getter for width + int Height() const {return height;} //getter for height + + bool ValidPosition(int x, int y) const {return (x >= 0 && x < width && y >= 0 && y < height);} //Helper - is position within board? + private: + + int width; //The width of the board + int height; //The height of the board + Piece ** * board; //The pieces on the board +}; + +/** + * Basic AI class + * TODO: Make sure that if Piece is changed, BasicAI is updated to be consistent + * TODO: More complex AI's should inherit off this class. + * It is recommended that only MakeMove is altered - hence the other functions are not virtual. + */ +class BasicAI +{ + public: + BasicAI(); //Constructor + virtual ~BasicAI(); //Destructor + + bool Setup(); //Implements setup protocol + bool MoveCycle(); //Implements the MoveCycle protocol + bool ReadBoard(); //Reads the board as part of the MoveCycle protocol + bool InterpretResult(); //Interprets the result of a move + virtual bool MakeMove(); //Makes a move - defaults to randomised moves + bool DebugPrintBoard(); //DEBUG - Prints the board to stderr + protected: + int turn; + Board * board; //The board + std::vector units; //Allied units + std::vector enemyUnits; //Enemy units + + bool ForgetUnit(Piece * forget); //Delete and forget about a unit + + Colour colour; + std::string colourStr; +}; + +/** + * Purely static class of Helper functions + */ +class Helper +{ + public: + static Direction StrToDir(const std::string & str); //Helper - Convert string to a Direction enum + static void DirToStr(const Direction & dir, std::string & buffer); //Helper - Convert Direction enum to a string + static void MoveInDirection(int & x, int & y, const Direction & dir, int multiplier = 1); //Helper - Move a point in a direction + static int Tokenise(std::vector & buffer, std::string & str, char split = ' '); //Helper - Split a string into tokens + static int Integer(std::string & fromStr); //Helper - convert a string to an integer + static void ReadLine(std::string & buffer); //Helper - read a line from stdin + private: + //By making these private we ensure that no one can create an instance of Helper + //Yes we could use namespaces instead, but I prefer this method because you can use private static member variables + //Not that I am. But you can. + Helper() {} + ~Helper() {} +}; + + + +#endif //BASIC_CPP_H + +//EOF + diff --git a/progcomp/agents/basic_cpp/info b/progcomp/agents/basic_cpp/info new file mode 100644 index 0000000..ee1b502 --- /dev/null +++ b/progcomp/agents/basic_cpp/info @@ -0,0 +1 @@ +basic_cpp diff --git a/progcomp/agents/basic_python/basic_python.py b/progcomp/agents/basic_python/basic_python.py new file mode 100644 index 0000000..cf024e8 --- /dev/null +++ b/progcomp/agents/basic_python/basic_python.py @@ -0,0 +1,294 @@ +#!/usr/bin/python -u + +#NOTE: The -u option is required for unbuffered stdin/stdout. +# If stdin/stdout are buffered, the manager program will not recieve any messages and assume that the agent has timed out. + +""" + basic_python.py - A sample Stratego AI for the UCC Programming Competition 2012 + + Written in python, the slithery language + Simply makes random moves, as long as possible + + author Sam Moore (matches) [SZM] + website http://matches.ucc.asn.au/stratego + email progcomp@ucc.asn.au or matches@ucc.asn.au + git git.ucc.asn.au/progcomp2012.git +""" + +import sys +import random + +ranks = ['B','1','2','3','4','5','6','7','8','9','s','F', '?', '+'] + +def move(x, y, direction): + """ Moves point (x,y) in direction, returns a pair """ + if direction == "UP": + return (x,y-1) + elif direction == "DOWN": + return (x,y+1) + elif direction == "LEFT": + return (x-1, y) + elif direction == "RIGHT": + return (x+1, y) + return (x,y) + + + +def oppositeColour(colour): + """ Returns the opposite colour to that given """ + if colour == "RED": + return "BLUE" + elif colour == "BLUE": + return "RED" + else: + return "NONE" + +class Piece: + """ Class representing a piece + Pieces have colour, rank and co-ordinates + """ + def __init__(self, colour, rank, x, y): + self.colour = colour + self.rank = rank + self.x = x + self.y = y + self.lastMoved = -1 + + def mobile(self): + return self.rank != 'F' and self.rank != 'B' and self.rank != '?' and self.rank != '+' + + def valuedRank(self): + if ranks.count(self.rank) > 0: + return len(ranks) - 2 - ranks.index(self.rank) + else: + return 0 + + + + +class BasicAI: + """ + BasicAI class to play a game of stratego + Implements the protocol correctly. Stores the state of the board in self.board + Only makes random moves. + Override method "MakeMove" for more complex moves + """ + def __init__(self): + """ Constructs the BasicAI agent, and starts it playing the game """ + #sys.stderr.write("BasicAI __init__ here...\n"); + self.turn = 0 + self.board = [] + self.units = [] + self.enemyUnits = [] + + + + def Setup(self): + """ Implements Setup part of protocol. Always uses the same setup. Override to create custom setups """ + #sys.stderr.write("BasicAI Setup here...\n"); + setup = sys.stdin.readline().split(' ') + if len(setup) != 4: + sys.stderr.write("BasicAI setup fails, expected 4 tokens, got " + str(len(setup)) + " "+str(setup) + "\n") + self.colour = setup[0] + self.opponentName = setup[1] + self.width = int(setup[2]) + self.height = int(setup[3]) + for x in range(0, self.width): + self.board.append([]) + for y in range(0, self.height): + self.board[x].append(None) + if self.colour == "RED": + print "FB8sB479B8\nBB31555583\n6724898974\n967B669999" + elif self.colour == "BLUE": + print "967B669999\n6724898974\nBB31555583\nFB8sB479B8" + return True + + def MoveCycle(self): + #sys.stderr.write("BasicAI MakeMove here...\n"); + if self.InterpretResult() == False or self.ReadBoard() == False or self.MakeMove() == False: + return False + self.turn += 1 + return self.InterpretResult() + + def MakeMove(self): + """ Randomly moves any moveable piece, or prints "NO_MOVE" if there are none """ + #TODO: Over-ride this function in base classes with more complex move behaviour + + #sys.stderr.write("BasicAI MakeMove here...\n") + #self.debugPrintBoard() + + if len(self.units) <= 0: + return False + + index = random.randint(0, len(self.units)-1) + startIndex = index + + directions = ("UP", "DOWN", "LEFT", "RIGHT") + while True: + piece = self.units[index] + if piece != None and piece.mobile(): + dirIndex = random.randint(0, len(directions)-1) + startDirIndex = dirIndex + + while True: + #sys.stderr.write("Trying index " + str(dirIndex) + "\n") + p = move(piece.x, piece.y, directions[dirIndex]) + if p[0] >= 0 and p[0] < self.width and p[1] >= 0 and p[1] < self.height: + target = self.board[p[0]][p[1]] + if target == None or (target.colour != piece.colour and target.colour != "NONE" and target.colour != "BOTH"): + print str(piece.x) + " " + str(piece.y) + " "+directions[dirIndex] + return True + dirIndex = (dirIndex + 1) % len(directions) + if startDirIndex == dirIndex: + break + + index = (index + 1) % len(self.units) + if startIndex == index: + print "NO_MOVE" + return True + + + def ReadBoard(self): + """ Reads in the board. + On the very first turn, sets up the self.board structure + On subsequent turns, the board is simply read, but the self.board structure is not updated here. + """ + #sys.stderr.write("BasicAI ReadBoard here...\n"); + for y in range(0,self.height): + row = sys.stdin.readline().strip() + if len(row) < self.width: + sys.stderr.write("Row has length " + str(len(row)) + " vs " + str(self.width) + "\n") + return False + for x in range(0,self.width): + if self.turn == 0: + if row[x] == '.': + pass + elif row[x] == '#': + self.board[x][y] = Piece(oppositeColour(self.colour), '?',x,y) + self.enemyUnits.append(self.board[x][y]) + elif row[x] == '+': + self.board[x][y] = Piece("NONE", '+', x, y) + else: + self.board[x][y] = Piece(self.colour, row[x],x,y) + self.units.append(self.board[x][y]) + else: + pass + return True + + + def InterpretResult(self): + """ Interprets the result of a move, and updates the board. + The very first move is ignored. + On subsequent moves, the self.board structure is updated + """ + #sys.stderr.write("BasicAI InterpretResult here...\n") + result = sys.stdin.readline().split(' ') + #sys.stderr.write(" Read status line \"" + str(result) + "\"\n") + if self.turn == 0: + return True + + if result[0].strip() == "QUIT": #Make sure we exit when the manager tells us to! + return False + + if result[0].strip() == "NO_MOVE": #No move was made, don't need to update anything + return True + + if len(result) < 4: #Should be at least 4 tokens (X Y DIRECTION OUTCOME) in any other case + return False + + x = int(result[0].strip()) + y = int(result[1].strip()) + + + #sys.stderr.write(" Board position " + str(x) + " " + str(y) + " is OK!\n") + + direction = result[2].strip() + outcome = result[3].strip() + + p = move(x,y,direction) + + + + if outcome == "OK": + self.board[p[0]][p[1]] = self.board[x][y] + self.board[x][y].x = p[0] + self.board[x][y].y = p[1] + + self.board[x][y] = None + elif outcome == "KILLS": + if self.board[p[0]][p[1]] == None: + return False + + if self.board[p[0]][p[1]].colour == self.colour: + self.units.remove(self.board[p[0]][p[1]]) + elif self.board[p[0]][p[1]].colour == oppositeColour(self.colour): + self.enemyUnits.remove(self.board[p[0]][p[1]]) + + self.board[x][y].x = p[0] + self.board[x][y].y = p[1] + + + self.board[p[0]][p[1]] = self.board[x][y] + self.board[x][y].rank = result[4].strip() + + self.board[x][y] = None + + elif outcome == "DIES": + if self.board[p[0]][p[1]] == None: + return False + + if self.board[x][y].colour == self.colour: + self.units.remove(self.board[x][y]) + elif self.board[x][y].colour == oppositeColour(self.colour): + self.enemyUnits.remove(self.board[x][y]) + + self.board[p[0]][p[1]].rank = result[5].strip() + self.board[x][y] = None + elif outcome == "BOTHDIE": + if self.board[p[0]][p[1]] == None: + return False + + + if self.board[x][y].colour == self.colour: + self.units.remove(self.board[x][y]) + elif self.board[x][y].colour == oppositeColour(self.colour): + self.enemyUnits.remove(self.board[x][y]) + if self.board[p[0]][p[1]].colour == self.colour: + self.units.remove(self.board[p[0]][p[1]]) + elif self.board[p[0]][p[1]].colour == oppositeColour(self.colour): + self.enemyUnits.remove(self.board[p[0]][p[1]]) + + + self.board[p[0]][p[1]] = None + self.board[x][y] = None + elif outcome == "FLAG": + #sys.stderr.write(" Game over!\n") + return False + elif outcome == "ILLEGAL": + #sys.stderr.write(" Illegal move!\n") + return False + else: + #sys.stderr.write(" Don't understand outcome \"" + outcome + "\"!\n"); + return False + + #sys.stderr.write(" Completed interpreting move!\n"); + return True + + def debugPrintBoard(self): + """ For debug purposes only. Prints the board to stderr. + Does not indicate difference between allied and enemy pieces + Unknown (enemy) pieces are shown as '?' + """ + for y in range(0, self.height): + for x in range(0, self.width): + if self.board[x][y] == None: + sys.stderr.write("."); + else: + sys.stderr.write(str(self.board[x][y].rank)); + sys.stderr.write("\n") + +#basicAI = BasicAI() +#if basicAI.Setup(): +# while basicAI.MoveCycle(): +# pass + diff --git a/progcomp/agents/basic_python/info b/progcomp/agents/basic_python/info new file mode 100644 index 0000000..9260726 --- /dev/null +++ b/progcomp/agents/basic_python/info @@ -0,0 +1 @@ +basic_python.py diff --git a/progcomp/agents/basic_python/run.py b/progcomp/agents/basic_python/run.py new file mode 100755 index 0000000..80b4cfe --- /dev/null +++ b/progcomp/agents/basic_python/run.py @@ -0,0 +1,8 @@ +#!/usr/bin/python -u + +from basic_python import * + +basicAI = BasicAI() +if basicAI.Setup(): + while basicAI.MoveCycle(): + pass diff --git a/progcomp/judge/manager/Makefile b/progcomp/judge/manager/Makefile new file mode 100644 index 0000000..fe319df --- /dev/null +++ b/progcomp/judge/manager/Makefile @@ -0,0 +1,27 @@ +#Makefile for Stratego + +CPP = g++ -Wall -pedantic -lSDL -lGL -g +OBJ = main.o controller.o ai_controller.o human_controller.o program.o thread_util.o stratego.o graphics.o game.o + +BIN = stratego + + + +$(BIN) : $(OBJ) + $(CPP) -o $(BIN) $(OBJ) + + + + +%.o : %.cpp %.h + $(CPP) -c $< + +clean : + $(RM) $(BIN) $(OBJ) $(LINKOBJ) + +clean_full: #cleans up all backup files + $(RM) $(BIN) $(OBJ) $(LINKOBJ) + $(RM) *.*~ + $(RM) *~ + + diff --git a/progcomp/judge/manager/ai_controller.cpp b/progcomp/judge/manager/ai_controller.cpp new file mode 100644 index 0000000..40ee3df --- /dev/null +++ b/progcomp/judge/manager/ai_controller.cpp @@ -0,0 +1,64 @@ +#include + +#include "game.h" +#include "stratego.h" + +#include "ai_controller.h" + +using namespace std; + + +/** + * Queries the AI program to setup its pieces. Stores the setup in a st + * @implements Controller::QuerySetup + * @param + * @returns A MovementResult + */ + +MovementResult AI_Controller::QuerySetup(const char * opponentName, std::string setup[]) +{ + switch (colour) + { + case Piece::RED: + if (!SendMessage("RED %s %d %d", opponentName, Game::theGame->theBoard.Width(), Game::theGame->theBoard.Height())) + return MovementResult::BAD_RESPONSE; + break; + case Piece::BLUE: + if (!SendMessage("BLUE %s %d %d", opponentName, Game::theGame->theBoard.Width(), Game::theGame->theBoard.Height())) + return MovementResult::BAD_RESPONSE; + break; + case Piece::NONE: + case Piece::BOTH: + return MovementResult::COLOUR_ERROR; + break; + } + + for (int y = 0; y < 4; ++y) + { + if (!GetMessage(setup[y], timeout)) + return MovementResult::BAD_RESPONSE; + } + + return MovementResult::OK; +} + + +/** + * Queries the AI program to make a move + * @implements Controller::QueryMove + * @param buffer String which stores the AI program's response + * @returns A MovementResult which will be MovementResult::OK if a move was made, or MovementResult::NO_MOVE if the AI did not respond + */ +MovementResult AI_Controller::QueryMove(string & buffer) +{ + if (!Running()) + return MovementResult::NO_MOVE; //AI has quit + Game::theGame->theBoard.Print(output, colour); + + if (!GetMessage(buffer,timeout)) + { + return MovementResult::NO_MOVE; //AI did not respond (within the timeout). It will lose by default. + } + return MovementResult::OK; //Got the message +} + diff --git a/progcomp/judge/manager/ai_controller.h b/progcomp/judge/manager/ai_controller.h new file mode 100644 index 0000000..7d62591 --- /dev/null +++ b/progcomp/judge/manager/ai_controller.h @@ -0,0 +1,32 @@ +#ifndef AI_CONTROLLER_H +#define AI_CONTROLLER_H + +#include "controller.h" +#include "program.h" + +/** + * Class to control an AI program playing Stratego + * Inherits mostly from Program + */ +class AI_Controller : public Controller, private Program +{ + public: + AI_Controller(const Piece::Colour & newColour, const char * executablePath, const double newTimeout = 2.0) : Controller(newColour, executablePath), Program(executablePath), timeout(newTimeout) {} + virtual ~AI_Controller() {} + + + + virtual MovementResult QuerySetup(const char * opponentName,std::string setup[]); + virtual MovementResult QueryMove(std::string & buffer); + + virtual void Message(const char * message) {Program::SendMessage(message);} + + virtual bool Valid() const {return Program::Running();} + + + private: + const double timeout; //Timeout in seconds for messages from the AI Program + +}; + +#endif //AI_CONTROLLER_H diff --git a/progcomp/judge/manager/array.h b/progcomp/judge/manager/array.h new file mode 100644 index 0000000..2a1d29f --- /dev/null +++ b/progcomp/judge/manager/array.h @@ -0,0 +1,128 @@ +#ifndef ARRAY_H +#define ARRAY_H + +typedef long unsigned int LUint; +#include + +template +class Array +{ + public: + Array() : start(NULL), size(0), reserved(0) {} + Array(LUint newSize) : start(new T[newSize]), size(newSize), reserved(newSize) {} + ~Array() {delete [] start;} + + void Empty() {size = 0;} + void Add(const T & add); + void Reserve(LUint reserve); + void Resize(LUint newSize); + void RemoveBack(); + + LUint Size() const {return size;} + LUint Capacity() const {return reserved;} + + void operator=(const Array & equ); + bool operator==(const Array & equ) const; + bool operator!=(const Array & equ) const {return !operator==(equ);} + + class Iterator + { + public: + Iterator(const Array & from) : parent(from), index(0) {} + Iterator(const Iterator & cpy) : parent(cpy.parent), index(cpy.index) {} + ~Iterator() {} + + bool Good() const {return index < parent.Size();} + + T & operator*() const {return parent.start[index];} + + void operator++() {++index;} + void operator--() {--index;} + void operator++(int) {operator++();} + void operator--(int) {operator--();} + Iterator & operator+=(int amount) {index += amount;} + Iterator & operator-=(int amount) {index -= amount;} + Iterator operator+(int amount) {return Iterator(*this) += amount;} + Iterator operator-(int amount) {return Iterator(*this) -= amount;} + + void operator=(const Iterator & set) {index = set.index;} + bool operator==(const Iterator & set) {return (&parent == &(set.parent) && index == set.index);} + private: + const Array & parent; + LUint index; + }; + + Iterator First() const {return Iterator(*this);} + Iterator Last() const {return Iterator(*this) -= (size-1);} + + + T & operator[](LUint at) const + { + #ifdef DEBUGALL + printf(" Array::operator[] - called for index %lu/%lu (reserved %lu)\n", at, size, reserved); + + #endif //DEBUG + assert(at < size); return start[at]; + } + + int Find(const T & find) + { + + LUint result; + for (result = 0; result < size; result++) + { + //printf("%p %lu/%lu\n", (void*)(start), result, size); + if (start[result] == find) + return (int)(result); + } + return -1; + } + + private: + T * start; + LUint size; LUint reserved; +}; + +template void Array::Add(const T & add) +{ + if (size >= reserved) + { + T * old = start; + reserved *= 2; ++reserved; + start = new T[reserved]; + for (LUint ii=0; ii < size; ++ii) + start[ii] = old[ii]; + delete [] old; + } + start[size++] = add; +} + +template void Array::RemoveBack() +{ + if (size > 0) + --size; +} + +template void Array::Resize(LUint newSize) +{ + T * old = start; + start = new T[newSize]; + for (LUint ii=0; ii < size; ++ii) + start[ii] = old[ii]; + size = newSize; reserved = newSize; + delete [] old; +} + +template void Array::Reserve(LUint newReserve) +{ + if (newReserve > reserved) + { + T * old = start; + start = new T[newReserve]; + for (LUint ii=0; ii < size; ++ii) + start[ii] = old[ii]; + reserved = newReserve; + } +} + +#endif //ARRAY_H diff --git a/progcomp/judge/manager/controller.cpp b/progcomp/judge/manager/controller.cpp new file mode 100644 index 0000000..1b0a35d --- /dev/null +++ b/progcomp/judge/manager/controller.cpp @@ -0,0 +1,184 @@ +#include "controller.h" + +#include +#include "game.h" + +using namespace std; + +/** + * Queries the player to setup their pieces + * + */ + +MovementResult Controller::Setup(const char * opponentName) +{ + string setup[4] = {"","","",""}; + MovementResult query = this->QuerySetup(opponentName, setup); + if (query != MovementResult::OK) + return query; + + + + int usedUnits[(int)(Piece::BOMB)]; + for (int ii = 0; ii <= (int)(Piece::BOMB); ++ii) + usedUnits[ii] = 0; + + int yStart = 0; + switch (colour) + { + case Piece::RED: + yStart = 0; + break; + case Piece::BLUE: + yStart = Game::theGame->theBoard.Height()-4; + break; + default: + return MovementResult::COLOUR_ERROR; + break; + } + + + for (int y = 0; y < 4; ++y) + { + if ((int)setup[y].length() != Game::theGame->theBoard.Width()) + return MovementResult::BAD_RESPONSE; + + for (int x = 0; x < Game::theGame->theBoard.Width(); ++x) + { + Piece::Type type = Piece::GetType(setup[y][x]); + if (type != Piece::NOTHING) + { + usedUnits[(int)(type)]++; + if (usedUnits[type] > Piece::maxUnits[(int)type]) + { + //fprintf(stderr, "Too many units of type %c\n", Piece::tokens[(int)(type)]); + return MovementResult::BAD_RESPONSE; + } + Game::theGame->theBoard.AddPiece(x, yStart+y, type, colour); + } + } + } + if (usedUnits[(int)Piece::FLAG] <= 0) + { + return MovementResult::BAD_RESPONSE; //You need to include a flag! + } + + return MovementResult::OK; + +} + + +/** + * Queries the player to respond to a state of Game::theGame->theBoard + * @param buffer String which is used to store the player's responses + * @returns The result of the response and/or move if made + */ +MovementResult Controller::MakeMove(string & buffer) +{ + buffer.clear(); + MovementResult query = this->QueryMove(buffer); + if (query != MovementResult::OK) + return query; + + if (buffer == "NO_MOVE") + { + buffer += " OK"; + return MovementResult::OK; + } + if (buffer == "SURRENDER") + { + buffer += " OK"; + return MovementResult::SURRENDER; + } + + int x; int y; string direction=""; + stringstream s(buffer); + s >> x; + s >> y; + + + s >> direction; + Board::Direction dir; + if (direction == "UP") + { + dir = Board::UP; + } + else if (direction == "DOWN") + { + dir = Board::DOWN; + } + else if (direction == "LEFT") + { + dir = Board::LEFT; + } + else if (direction == "RIGHT") + { + dir = Board::RIGHT; + } + else + { + if (Game::theGame->allowIllegalMoves) + return MovementResult::OK; + else + return MovementResult::BAD_RESPONSE; //Player gave bogus direction - it will lose by default. + } + + int multiplier = 1; + if (s.peek() != EOF) + s >> multiplier; + MovementResult moveResult = Game::theGame->theBoard.MovePiece(x, y, dir, multiplier, colour); + + s.clear(); s.str(""); + + //I stored the ranks in the wrong order; rank 1 is the marshal, 2 is the general etc... + //So I am reversing them in the output... great work + s << Piece::tokens[(int)(moveResult.attackerRank)] << " " << Piece::tokens[(int)(moveResult.defenderRank)]; + switch (moveResult.type) + { + case MovementResult::OK: + buffer += " OK"; + break; + case MovementResult::VICTORY: + buffer += " FLAG"; + break; + case MovementResult::KILLS: + buffer += " KILLS "; + buffer += s.str(); + + break; + case MovementResult::DIES: + buffer += " DIES "; + buffer += s.str(); + break; + case MovementResult::BOTH_DIE: + buffer += " BOTHDIE "; + buffer += s.str(); + break; + default: + buffer += " ILLEGAL"; + break; + + } + + + if (!Board::LegalResult(moveResult)) + { + + if (Game::theGame->allowIllegalMoves) + { + + return MovementResult::OK; //HACK - Illegal results returned as legal! (Move not made) + } + else if (this->HumanController()) //Cut human controllers some slack and let them try again... + { + //Yes, checking type of object is "not the C++ way" + // But sometimes its bloody useful to know!!! + Message("Bad move: \'" + buffer + "\' <- Please try again!"); + buffer = ""; + return this->MakeMove(buffer); + } + } + + return moveResult; + +} diff --git a/progcomp/judge/manager/controller.h b/progcomp/judge/manager/controller.h new file mode 100644 index 0000000..55c233d --- /dev/null +++ b/progcomp/judge/manager/controller.h @@ -0,0 +1,44 @@ +#ifndef CONTROLLER_H +#define CONTROLLER_H + +#include "stratego.h" +#include + +/** + * Class to control a player for Stratego + * Abstract base class + */ + +class Controller +{ + public: + Controller(const Piece::Colour & newColour, const char * newName = "no-name") : colour(newColour), name(newName) {} + virtual ~Controller() {} + + MovementResult Setup(const char * opponentName); + + MovementResult MakeMove(std::string & buffer); + + virtual bool HumanController() const {return false;} //Hacky... overrides in human_controller... avoids having to use run time type info + + void Message(const std::string & buffer) {Message(buffer.c_str());} + virtual void Message(const char * string) = 0; + + virtual MovementResult QuerySetup(const char * opponentName, std::string setup[]) = 0; + virtual MovementResult QueryMove(std::string & buffer) = 0; + virtual bool Valid() const {return true;} + + const Piece::Colour colour; + + std::string name; + + +}; + + + + + +#endif //CONTROLLER_H + + diff --git a/progcomp/judge/manager/game.cpp b/progcomp/judge/manager/game.cpp new file mode 100644 index 0000000..26369d3 --- /dev/null +++ b/progcomp/judge/manager/game.cpp @@ -0,0 +1,585 @@ +#include "game.h" + +using namespace std; + + + +Game* Game::theGame = NULL; +bool Game::gameCreated = false; + +Game::Game(const char * redPath, const char * bluePath, const bool enableGraphics, double newStallTime, const bool allowIllegal, FILE * newLog, const Piece::Colour & newReveal, int newMaxTurns, bool newPrintBoard) : red(NULL), blue(NULL), turn(Piece::RED), theBoard(10,10), graphicsEnabled(enableGraphics), stallTime(newStallTime), allowIllegalMoves(allowIllegal), log(newLog), reveal(newReveal), turnCount(0), input(NULL), maxTurns(newMaxTurns), printBoard(newPrintBoard) +{ + gameCreated = false; + if (gameCreated) + { + fprintf(stderr, "Game::Game - Error - Tried to create more than one Game!\n"); + exit(EXIT_FAILURE); + } + gameCreated = true; + Game::theGame = this; + signal(SIGPIPE, Game::HandleBrokenPipe); + + + if (graphicsEnabled && (!Graphics::Initialised())) + Graphics::Initialise("Stratego", theBoard.Width()*32, theBoard.Height()*32); + + if (strcmp(redPath, "human") == 0) + red = new Human_Controller(Piece::RED, graphicsEnabled); + else + red = new AI_Controller(Piece::RED, redPath); + + + if (strcmp(bluePath, "human") == 0) + blue = new Human_Controller(Piece::BLUE, graphicsEnabled); + else + blue = new AI_Controller(Piece::BLUE, bluePath); + + +} + +Game::Game(const char * fromFile, const bool enableGraphics, double newStallTime, const bool allowIllegal, FILE * newLog, const Piece::Colour & newReveal, int newMaxTurns, bool newPrintBoard) : red(NULL), blue(NULL), turn(Piece::RED), theBoard(10,10), graphicsEnabled(enableGraphics), stallTime(newStallTime), allowIllegalMoves(allowIllegal), log(newLog), reveal(newReveal), turnCount(0), input(NULL), maxTurns(newMaxTurns), printBoard(newPrintBoard) +{ + gameCreated = false; + if (gameCreated) + { + fprintf(stderr, "Game::Game - Error - Tried to create more than one Game!\n"); + exit(EXIT_FAILURE); + } + gameCreated = true; + Game::theGame = this; + signal(SIGPIPE, Game::HandleBrokenPipe); + + + if (graphicsEnabled && (!Graphics::Initialised())) + Graphics::Initialise("Stratego", theBoard.Width()*32, theBoard.Height()*32); + + input = fopen(fromFile, "r"); + + red = new FileController(Piece::RED, input); + blue = new FileController(Piece::BLUE, input); + + +} + +Game::~Game() +{ + + delete red; + delete blue; + + if (log != NULL && log != stdout && log != stderr) + fclose(log); + + if (input != NULL && input != stdin) + fclose(input); +} + +/** + * Attempts to setup the board and controllers + * @param redName the name of the red AI + * @param blueName the name of the blue AI + * @returns A colour, indicating if there were any errors + Piece::NONE indicates no errors + Piece::BOTH indicates errors with both AI + Piece::RED / Piece::BLUE indicates an error with only one of the two AI + */ +Piece::Colour Game::Setup(const char * redName, const char * blueName) +{ + + if (!red->Valid()) + { + logMessage("Controller for Player RED is invalid!\n"); + } + if (!blue->Valid()) + { + logMessage("Controller for Player BLUE is invalid!\n"); + } + if (!red->Valid()) + { + if (!blue->Valid()) + return Piece::BOTH; + return Piece::RED; + } + else if (!blue->Valid()) + { + return Piece::BLUE; + } + + for (int y = 4; y < 6; ++y) + { + for (int x = 2; x < 4; ++x) + { + theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE); + } + for (int x = 6; x < 8; ++x) + { + theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE); + } + } + + + MovementResult redSetup = red->Setup(blueName); + MovementResult blueSetup = blue->Setup(redName); + + + Piece::Colour result = Piece::NONE; + if (redSetup != MovementResult::OK) + { + if (blueSetup != MovementResult::OK) + { + logMessage("BOTH players give invalid setup!\n"); + result = Piece::BOTH; + } + else + { + logMessage("Player RED gave an invalid setup!\n"); + result = Piece::RED; + } + + } + else if (blueSetup != MovementResult::OK) + { + logMessage("Player BLUE gave an invalid setup!\n"); + result = Piece::BLUE; + } + + logMessage("%s RED SETUP\n", red->name.c_str()); + for (int y=0; y < 4; ++y) + { + for (int x=0; x < theBoard.Width(); ++x) + { + if (theBoard.GetPiece(x, y) != NULL) + logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, y)->type)]); + else + logMessage("."); + } + logMessage("\n"); + } + + logMessage("%s BLUE SETUP\n", blue->name.c_str()); + for (int y=0; y < 4; ++y) + { + for (int x=0; x < theBoard.Width(); ++x) + logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, theBoard.Height()-4 + y)->type)]); + logMessage("\n"); + } + + + return result; + +} + +void Game::Wait(double wait) +{ + if (wait <= 0) + return; + + TimerThread timer(wait*1000000); //Wait in seconds + timer.Start(); + + if (!graphicsEnabled) + { + while (!timer.Finished()); + timer.Stop(); + return; + } + + + while (!timer.Finished()) + { + SDL_Event event; + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_QUIT: + timer.Stop(); + exit(EXIT_SUCCESS); + break; + } + } + } + timer.Stop(); + +} + +void Game::HandleBrokenPipe(int sig) +{ + if (theGame == NULL) + { + fprintf(stderr, "ERROR - Recieved SIGPIPE during game exit!\n"); + exit(EXIT_FAILURE); + } + if (theGame->turn == Piece::RED) + { + theGame->logMessage("Game ends on RED's turn - REASON: "); + theGame->blue->Message("DEFAULT"); + } + else if (theGame->turn == Piece::BLUE) + { + + theGame->logMessage("Game ends on BLUE's turn - REASON: "); + theGame->red->Message("DEFAULT"); + } + else + { + theGame->logMessage("Game ends on ERROR's turn - REASON: "); + + } + + theGame->logMessage("SIGPIPE - Broken pipe (AI program may have segfaulted)\n"); + + if (Game::theGame->printBoard) + Game::theGame->theBoard.PrintPretty(stdout, Piece::BOTH); + + if (Game::theGame->graphicsEnabled && theGame->log == stdout) + { + theGame->logMessage("CLOSE WINDOW TO EXIT\n"); + Game::theGame->theBoard.Draw(Piece::BOTH); + while (true) + { + SDL_Event event; + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_QUIT: + exit(EXIT_SUCCESS); + break; + } + } + } + } + else + { + if (theGame->log == stdout) + { + theGame->logMessage( "PRESS ENTER TO EXIT\n"); + theGame->theBoard.Print(theGame->log); + while (fgetc(stdin) != '\n'); + } + } + + + exit(EXIT_SUCCESS); +} + +void Game::PrintEndMessage(const MovementResult & result) +{ + if (turnCount == 0) + { + logMessage("Game ends in the SETUP phase - REASON: "); + } + else + { + if (turn == Piece::RED) + { + logMessage("Game ends on RED's turn - REASON: "); + } + else if (turn == Piece::BLUE) + { + logMessage("Game ends on BLUE's turn - REASON: "); + } + else + { + logMessage("Game ends on ERROR's turn - REASON: "); + + } + } + switch (result.type) + { + case MovementResult::OK: + logMessage("Status returned OK, unsure why game halted...\n"); + break; + case MovementResult::DIES: + logMessage("Status returned DIES, unsure why game halted...\n"); + break; + case MovementResult::KILLS: + logMessage("Status returned KILLS, unsure why game halted...\n"); + break; + case MovementResult::BOTH_DIE: + logMessage("Status returned BOTH_DIE, unsure why game halted...\n"); + break; + case MovementResult::NO_BOARD: + logMessage("Board does not exit?!\n"); + break; + case MovementResult::INVALID_POSITION: + logMessage("Coords outside board\n"); + break; + case MovementResult::NO_SELECTION: + logMessage("Move does not select a piece\n"); + break; + case MovementResult::NOT_YOUR_UNIT: + logMessage("Selected piece belongs to other player\n"); + break; + case MovementResult::IMMOBILE_UNIT: + logMessage("Selected piece is not mobile (FLAG or BOMB)\n"); + break; + case MovementResult::INVALID_DIRECTION: + logMessage("Selected unit cannot move that way\n"); + break; + case MovementResult::POSITION_FULL: + logMessage("Attempted move into square occupied by neutral or allied piece\n"); + break; + case MovementResult::VICTORY: + logMessage("Captured the flag\n"); + break; + case MovementResult::BAD_RESPONSE: + logMessage("Unintelligable response\n"); + break; + case MovementResult::NO_MOVE: + logMessage("Did not make a move (may have exited)\n"); + break; + case MovementResult::COLOUR_ERROR: + logMessage("Internal controller error - COLOUR_ERROR\n"); + break; + case MovementResult::ERROR: + logMessage("Internal controller error - Unspecified ERROR\n"); + break; + case MovementResult::DRAW_DEFAULT: + logMessage("Game declared a draw after %d turns\n", turnCount); + break; + case MovementResult::DRAW: + logMessage("Game declared a draw because neither player has mobile pieces\n"); + break; + case MovementResult::SURRENDER: + logMessage("This player has surrendered!\n"); + break; + case MovementResult::BAD_SETUP: + switch (turn) + { + case Piece::RED: + logMessage("An illegal setup was made by RED\n"); + break; + case Piece::BLUE: + logMessage("An illegal setup was made by BLUE\n"); + break; + case Piece::BOTH: + logMessage("An illegal setup was made by BOTH players\n"); + break; + case Piece::NONE: + logMessage("Unknown internal error.\n"); + break; + } + break; + + } + + if (printBoard) + { + system("clear"); + fprintf(stdout, "%d Final State\n", turnCount); + theBoard.PrintPretty(stdout, Piece::BOTH); + fprintf(stdout, "\n"); + } + if (graphicsEnabled && log == stdout) + { + logMessage("CLOSE WINDOW TO EXIT\n"); + theBoard.Draw(Piece::BOTH); + while (true) + { + SDL_Event event; + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_QUIT: + exit(EXIT_SUCCESS); + break; + } + } + } + } + else + { + if (log == stdout) + { + logMessage("PRESS ENTER TO EXIT\n"); + while (fgetc(stdin) != '\n'); + } + } + +} + + + +MovementResult Game::Play() +{ + + MovementResult result = MovementResult::OK; + turnCount = 1; + string buffer; + + Piece::Colour toReveal = reveal; + + + + + + red->Message("START"); + + + + while (!Board::HaltResult(result) && (turnCount < maxTurns || maxTurns < 0)) + { + if (red->HumanController()) + toReveal = Piece::RED; + if (printBoard) + { + system("clear"); + if (turnCount == 0) + fprintf(stdout, "START:\n"); + else + fprintf(stdout, "%d BLUE:\n", turnCount); + theBoard.PrintPretty(stdout, toReveal); + fprintf(stdout, "\n\n"); + } + + if (graphicsEnabled) + theBoard.Draw(toReveal); + + turn = Piece::RED; + logMessage( "%d RED: ", turnCount); + result = red->MakeMove(buffer); + red->Message(buffer); + blue->Message(buffer); + logMessage( "%s\n", buffer.c_str()); + if (Board::HaltResult(result)) + break; + + if (stallTime > 0) + Wait(stallTime); + else + ReadUserCommand(); + + if (blue->HumanController()) + toReveal = Piece::BLUE; + if (printBoard) + { + system("clear"); + fprintf(stdout, "%d RED:\n", turnCount); + theBoard.PrintPretty(stdout, toReveal); + fprintf(stdout, "\n\n"); + } + if (graphicsEnabled) + theBoard.Draw(toReveal); + + + + turn = Piece::BLUE; + logMessage( "%d BLU: ", turnCount); + result = blue->MakeMove(buffer); + blue->Message(buffer); + red->Message(buffer); + logMessage( "%s\n", buffer.c_str()); + + if (Board::HaltResult(result)) + break; + + + + + + if (stallTime > 0) + Wait(stallTime); + else + ReadUserCommand(); + + if (theBoard.MobilePieces(Piece::BOTH) == 0) + result = MovementResult::DRAW; + + ++turnCount; + } + + if ((maxTurns >= 0 && turnCount >= maxTurns) && result == MovementResult::OK) + { + result = MovementResult::DRAW_DEFAULT; + } + + + return result; + + + +} + +/** + * Logs a message to the game's log file if it exists + * @param format the format string + * @param additional parameters - printed using va_args + * @returns the result of vfprintf or a negative number if the log file does not exist + */ +int Game::logMessage(const char * format, ...) +{ + if (log == NULL) + return -666; + va_list ap; + va_start(ap, format); + + int result = vfprintf(log, format, ap); + va_end(ap); + + return result; +} + +/** + * Waits for a user command + * Currently ignores the command. + */ +void Game::ReadUserCommand() +{ + fprintf(stdout, "Waiting for user to press enter...\n"); + string command(""); + for (char c = fgetc(stdin); c != '\n' && (int)(c) != EOF; c = fgetc(stdin)) + { + command += c; + } +} + +MovementResult FileController::QuerySetup(const char * opponentName, std::string setup[]) +{ + + char c = fgetc(file); + name = ""; + while (c != ' ') + { + name += c; + c = fgetc(file); + } + + while (fgetc(file) != '\n'); + + for (int y = 0; y < 4; ++y) + { + setup[y] = ""; + for (int x = 0; x < Game::theGame->theBoard.Width(); ++x) + { + setup[y] += fgetc(file); + } + + if (fgetc(file) != '\n') + { + return MovementResult::BAD_RESPONSE; + } + } + return MovementResult::OK; + + +} + +MovementResult FileController::QueryMove(std::string & buffer) +{ + char buf[BUFSIZ]; + + fgets(buf, sizeof(buf), file); + char * s = (char*)(buf); + while (*s != ':' && *s != '\0') + ++s; + + s += 2; + + buffer = string(s); + return MovementResult::OK; +} + + diff --git a/progcomp/judge/manager/game.h b/progcomp/judge/manager/game.h new file mode 100644 index 0000000..1212b89 --- /dev/null +++ b/progcomp/judge/manager/game.h @@ -0,0 +1,88 @@ +#ifndef MAIN_H +#define MAIN_H + +#include "stratego.h" +#include "ai_controller.h" +#include "human_controller.h" + + + +/** + * Class to manage the game + */ +class Game +{ + public: + Game(const char * redPath, const char * bluePath, const bool enableGraphics, double newStallTime = 1.0, const bool allowIllegal=false, FILE * newLog = NULL, const Piece::Colour & newRevealed = Piece::BOTH, int maxTurns = 5000, const bool printBoard = false); + Game(const char * fromFile, const bool enableGraphics, double newStallTime = 1.0, const bool allowIllegal=false, FILE * newLog = NULL, const Piece::Colour & newRevealed = Piece::BOTH, int maxTurns = 5000, const bool printBoard = false); + virtual ~Game(); + + + + void Wait(double wait); + + Piece::Colour Setup(const char * redName, const char * blueName); + MovementResult Play(); + void PrintEndMessage(const MovementResult & result); + + + static void HandleBrokenPipe(int signal); + void ReadUserCommand(); + + const Piece::Colour Turn() const {return turn;} + void ForceTurn(const Piece::Colour & newTurn) {turn = newTurn;} + int TurnCount() const {return turnCount;} + + static Game * theGame; + public: + int logMessage(const char * format, ...); + FILE * GetLogFile() const {return log;} + Controller * red; + Controller * blue; + private: + Piece::Colour turn; + + public: + Board theBoard; + private: + const bool graphicsEnabled; + double stallTime; + public: + const bool allowIllegalMoves; + + private: + FILE * log; + + public: + const Piece::Colour reveal; + int turnCount; + + static bool gameCreated; + + FILE * input; + + int maxTurns; + const bool printBoard; + +}; + +class FileController : public Controller +{ + public: + FileController(const Piece::Colour & newColour, FILE * newFile) : Controller(newColour, "file"), file(newFile) {} + virtual ~FileController() {} + + virtual void Message(const char * string) {} //Don't send messages + virtual MovementResult QuerySetup(const char * opponentName, std::string setup[]); + virtual MovementResult QueryMove(std::string & buffer); + virtual bool Valid() const {return file != NULL;} + + private: + FILE * file; + + +}; + + + +#endif //MAIN_H diff --git a/progcomp/judge/manager/graphics.cpp b/progcomp/judge/manager/graphics.cpp new file mode 100644 index 0000000..5b708df --- /dev/null +++ b/progcomp/judge/manager/graphics.cpp @@ -0,0 +1,449 @@ +#include "graphics.h" +#include +#include +#include + + +#undef DEBUG +//#define DEBUG + +std::list Graphics::allTextures = std::list(); +Screen * Graphics::screen = NULL; + +int Graphics::screenWidth = 0; +int Graphics::screenHeight = 0; +bool Graphics::initialised = false; + +using namespace std; + +Texture::Texture(const char * filename, bool newDrawCentred) : surface(NULL), texture(0), drawCentred(newDrawCentred) +{ + #ifdef DEBUG + printf("Texture::Texture - loading \"%s\".\n", filename); + #endif //DEBUG + + surface = Graphics::LoadTextureBMP(filename); + if (surface == NULL) + { + fprintf(stderr, "Texture::Texture - Could not open texture from file \"%s\"! ABORT\n", filename); + exit(EXIT_FAILURE); + } + + GLenum texture_format; + GLint nOfColours = surface->format->BytesPerPixel; + switch (nOfColours) + { + case 4: //contains alpha + texture_format = (surface->format->Rmask == 0x000000FF) ? GL_RGBA : GL_BGRA; + break; + case 3: //does not contain alpha + texture_format = (surface->format->Rmask == 0x000000FF) ? GL_RGB : GL_BGR; + break; + default: + fprintf(stderr,"Texture::Texture - Could not understand SDL_Surface format (%d colours)! ABORT\n", nOfColours); + exit(EXIT_FAILURE); + break; + } + + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexImage2D(GL_TEXTURE_2D, 0, nOfColours, surface->w, surface->h,0, texture_format, GL_UNSIGNED_BYTE, surface->pixels); + +} + +Texture::~Texture() +{ + #ifdef DEBUG + printf("Texture::~Texture - %p has been deleted. glDeleteTexture and SDL_FreeSurface here.\n", (void*)(this)); + #endif //DEBUG + glDeleteTextures(1, &texture); + //SDL_FreeSurface(surface); +} + +void Texture::DrawColour(int x, int y, double angle, double scale, Colour colour) +{ + if (scale > surface->w || scale > surface->h) + { + Graphics::DrawPixel(x/scale,y/scale,colour); + } + else + { + glColor3f(colour.r,colour.g,colour.b); + Draw(x,y,angle,scale); + glColor3f(1,1,1); + } +} + +void Texture::Draw(int x, int y, double angle , double scale ) +{ + //Draws the CENTRE of the texture at x, y, rotated by angle + + #ifdef DEBUG + printf(" Texture::Draw - Drawing %p at (%d, %d) ; angle %2f ; scale % 2f\n", (void*)(this), x, y, angle, scale); + #endif //DEBUG + + //if (x/scale < 0 || x/scale > Graphics::ScreenWidth() || y/scale < 0 || y/scale > Graphics::ScreenHeight() ) + // return; + + glPushMatrix(); //NOT deprecated + + + glTranslatef(x/scale, y/scale,0); + + if (scale > surface->w || scale > surface->h) + { + Graphics::DrawPixel(0,0, Colour(255,255,255)); + } + else + { + glRotated(angle, 0, 0, 1); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture); + glBegin(GL_QUADS); + + //scale /= 2; + if (drawCentred) + { + glTexCoord2i(0,0); glVertex3f(-0.5f/scale*surface->w ,-0.5f/scale*surface->h,0); //bottom left + glTexCoord2i(1,0); glVertex3f(0.5f/scale*surface->w,-0.5f/scale*surface->h,0); //bottom right + glTexCoord2i(1,1); glVertex3f(0.5f/scale*surface->w,0.5f/scale*surface->h,0); //top right + glTexCoord2i(0,1); glVertex3f(-0.5f/scale*surface->w,0.5f/scale*surface->h,0); //top left + } + else + { + glTexCoord2i(0,0); glVertex3f(0 ,0,0); //bottom left + glTexCoord2i(1,0); glVertex3f(1.0f/scale*surface->w,0,0); //bottom right + glTexCoord2i(1,1); glVertex3f(1.0f/scale*surface->w,1.0f/scale*surface->h,0); //top right + glTexCoord2i(0,1); glVertex3f(0,1.0f/scale*surface->h,0); //top left + } + + glEnd(); + glDisable(GL_TEXTURE_2D); + } + glPopMatrix(); + +} + + +Font::Font(const char * filename, int newWidth, int newHeight) : Texture(filename), width(newWidth), height(newHeight) +{ + +} + +Font::~Font() +{ + +} + +void Font::DrawText(const char * string, int x, int y, double angle, double scale) +{ + #ifdef DEBUG + printf("Font::DrawText - drawing \"%s\"\n", string); + #endif //DEBUG + glPushMatrix(); //NOT deprecated + glTranslatef(x, y,0); + glRotated(angle, 0, 0, 1); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture); + + + for (int ii=0; string[ii] != '\0'; ++ii) + { + if (string[ii] != ' ') + { + glPushMatrix(); + glTranslatef(ii*(float)(width)/(float)(scale),0,0); + + int index = (int)(string[ii]) - (int)('!'); + if (index < 0 || index > (int)('~') - (int)('!')) + index = (int)('~') - (int)('!') + 1; + + float start = (float)(((((float)(index))*((float)(width)))-3.0f)/((float)surface->w)); + float end = (float)(((((float)(index+1))*((float)(width)))-4.0f)/((float)surface->w)); + if (start < 0) {start = 0;} if (end > 1) {end = 1;} + glBegin(GL_QUADS); + glTexCoord2f(start,0); glVertex3f(-0.5f/scale*width ,-0.5f/scale*height,0); //bottom left + glTexCoord2f(end,0); glVertex3f(0.5f/scale*width,-0.5f/scale*height,0); //bottom right + glTexCoord2f(end,1); glVertex3f(0.5f/scale*width,0.5f/scale*height,0); //top right + glTexCoord2f(start,1); glVertex3f(-0.5f/scale*width,0.5f/scale*height,0); //top left + //printf("Index %d - Drawing %c - maps to %f->%f\n", index,string[ii],start,end); + + glEnd(); + glPopMatrix(); + } + } + + + glDisable(GL_TEXTURE_2D); + glPopMatrix(); + +} + + +void Graphics::Initialise(const char * caption, int newWidth, int newHeight) +{ + if (Initialised()) + { + std::cerr << "Graphics have already been initialised! Fatal Error\n"; + exit(EXIT_FAILURE); + } + screenWidth = newWidth; screenHeight = newHeight; + + if (SDL_Init(SDL_INIT_VIDEO) != 0) + { + std::cerr << "Couldn't init SDL!\n"; + exit(EXIT_FAILURE); + } + // atexit(Graphics::Destroy); BREAKS THINGS + + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); //According to sulix does not matter. (much) + + + + + screen = SDL_SetVideoMode(screenWidth,screenHeight, 32, SDL_OPENGL); + if ( screen == NULL ) + { + std::cerr << "Couldn't set " << screenWidth << "x" << screenHeight << "x32 video mode: " << SDL_GetError() << "\n"; + exit(EXIT_FAILURE); + } + + //COMES AFTER SETVIDEO MODE + glEnable(GL_TEXTURE_2D); + glClearColor(1,1,1,0); //Set clear colour (white) here + glViewport(0,0,screenWidth,screenHeight); //DOES matter + glClear(GL_COLOR_BUFFER_BIT); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0,screenWidth,screenHeight,0,-1,1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glDisable(GL_DEPTH_TEST); + SDL_WM_SetCaption( caption, NULL); + + Graphics::initialised = true; + +} + +void Graphics::Destroy() +{ + list::iterator i(allTextures.begin()); + while (i != allTextures.end()) + { + SDL_FreeSurface((*i)); + ++i; + } + SDL_Quit(); +} + +SDL_Surface * Graphics::LoadTextureBMP(const char * file) +{ + SDL_Surface * tmp = SDL_LoadBMP(file); + if (tmp == NULL) + return NULL; + //assert(tmp != NULL); + + + if (Graphics::screen != NULL) + { + SDL_Surface * tex = SDL_DisplayFormat(tmp); + SDL_FreeSurface(tmp); + + allTextures.push_back(tex); + return tex; + } + return tmp; +} + +void Graphics::SaveTextureBMP(SDL_Surface * tex, const char * file) +{ + SDL_SaveBMP(tex, file); +} + + +void Graphics::DrawTexture(SDL_Surface * tex, int destX, int destY, int srcX, int srcY, int w, int h) +{ + if (w < 0) {w = tex->w - srcX;} + if (h < 0) {h = tex->h - srcY;} + Graphics::DrawTexture(screen, tex, destX, destY, srcX, srcY, w, h); +} + +void Graphics::DrawTexture(SDL_Surface * dest, SDL_Surface * tex, int destX, int destY, int srcX, int srcY, int width, int height) +{ + if ((destX < 0)||(destX >= dest->w)||(destY < 0)||(destY >= dest->h) + ||(srcX < 0)||(srcX >= tex->w)||(srcY < 0)||(srcY >= tex->h)) + return; + + assert(dest->format->BitsPerPixel == 32); + assert(tex->format->BitsPerPixel == 32); + + if (SDL_MUSTLOCK(tex)) + SDL_LockSurface(tex); + + if (SDL_MUSTLOCK(dest)) + SDL_LockSurface(dest); + + + + Colour transparent = Graphics::GetPixel(tex, srcX, srcY); + //printf("transparent from %d %d\n", srcX, srcY); + + for (int xOff = 0; xOff < width; xOff++) + + { + for (int yOff = 0; yOff < height; yOff++) + { + Colour nextColour = Graphics::GetPixel(tex, srcX+xOff, srcY+yOff); + if (nextColour != transparent) + { + Graphics::DrawPixel(dest, destX + xOff, destY + yOff, nextColour); + } + } + } + + if (SDL_MUSTLOCK(tex)) + SDL_UnlockSurface(tex); + + if (SDL_MUSTLOCK(dest)) + SDL_UnlockSurface(dest); + +} + +void Graphics::ClearScreen() +{ + //SDL_FillRect(screen, NULL ,Graphics::MakeColour(0,0,0)); + glClear(GL_COLOR_BUFFER_BIT); + +} + +void Graphics::UpdateScreen() +{ + SDL_GL_SwapBuffers(); + //SDL_Flip(screen); +} + +void Graphics::DrawPixel(int x, int y, Colour colour) +{ + DrawPixel(screen, x, y, colour); +} + +void Graphics::DrawPixel(SDL_Surface * dest, int x, int y, Colour colour) +{ + glBegin(GL_POINTS); + glColor4f(colour.r/255, colour.g/255, colour.b/255, colour.a); + glVertex2f(x, y); + glColor3f(1,1,1); + glEnd(); +} + +void Graphics::DrawGrid(int gridWidth, int gridHeight, Colour colour) +{ + for (int x = 0; x < screen->w; x+=gridWidth) + { + Graphics::DrawLine(x,0, x,screen->h - 1, colour); + } + for (int y = 0; y < screen->h; y+=gridHeight) + { + Graphics::DrawLine(0,y, screen->w - 1,y, colour); + } +} + +Uint8 Graphics::MakeColour(int R, int G, int B, int Alpha) +{ + return SDL_MapRGB(screen->format,R,G,B); +} + +Colour Graphics::GetPixel(int x, int y) +{ + return Graphics::GetPixel(screen, x, y); +} + +Colour Graphics::GetPixel(SDL_Surface * src, int x, int y) +{ + //Convert the pixels to 32 bit + Uint8 * pixels = (Uint8*)src->pixels; + //Get the requested pixel + + if (((y > 0)&&(y < src->h)) && ((x > 0)&&(x < src->w))) + return ConvertColour(pixels[ ( y * src->w ) + x ]); + return Colour(0,0,0,0); + +} + + + +void Graphics::DrawLine(int x1, int y1, int x2, int y2, Colour colour,double scale) +{ + //printf("DRAW LINE\n"); + glColor4f(colour.r/255,colour.g/255,colour.b/255,colour.a); + glBegin(GL_LINES); + glVertex2f(x1/scale, y1/scale); // origin of the line + glVertex2f(x2/scale, y2/scale); // ending point of the line + glColor3f(1,1,1); + glEnd(); + + +} + +void Graphics::DrawLineDashed(int x1, int y1, int x2, int y2, Colour colour, double scale) +{ + glLineStipple(8, 0xAAAA); + glEnable(GL_LINE_STIPPLE); + DrawLine(x1,y1,x2,y2,colour,scale); + glDisable(GL_LINE_STIPPLE); + glEnd(); +} + +void Graphics::DrawRectangle(int topX, int topY, int botX, int botY, Colour colour, double scale) +{ + glColor4f(colour.r/255,colour.g/255,colour.b/255,colour.a); + glBegin(GL_LINES); + glVertex2f(topX/scale, topY/scale); // origin of the rectangle + glVertex2f(botX/scale, topY/scale); // point1 + glVertex2f(botX/scale, botY/scale); // point2 + glVertex2f(topX/scale, botY/scale); // point3 + glVertex2f(topX/scale, topY/scale); // point4 + glEnd(); +} + +Colour Graphics::ConvertColour(Uint8 from) +{ + SDL_PixelFormat * fmt=screen->format; + Colour result; + + Uint8 temp; + + //Get red + temp=from&fmt->Rmask; /* Isolate red component */ + temp=temp>>fmt->Rshift;/* Shift it down to 8-bit */ + temp=temp<Rloss; /* Expand to a full 8-bit number */ + result.r = (float)(temp); + + //Get green + temp=from&fmt->Gmask; /* Isolate red component */ + temp=temp>>fmt->Gshift;/* Shift it down to 8-bit */ + temp=temp<Gloss; /* Expand to a full 8-bit number */ + result.g = (float)(temp); + + //Get blue + temp=from&fmt->Bmask; /* Isolate red component */ + temp=temp>>fmt->Bshift;/* Shift it down to 8-bit */ + temp=temp<Bloss; /* Expand to a full 8-bit number */ + result.b = (float)(temp); + + //Get alpha + temp=from&fmt->Amask; /* Isolate red component */ + temp=temp>>fmt->Ashift;/* Shift it down to 8-bit */ + temp=temp<Aloss; /* Expand to a full 8-bit number */ + result.a = (float)(temp); + return result; +} + + + + + + diff --git a/progcomp/judge/manager/graphics.h b/progcomp/judge/manager/graphics.h new file mode 100644 index 0000000..f81bdb3 --- /dev/null +++ b/progcomp/judge/manager/graphics.h @@ -0,0 +1,148 @@ +#ifndef GRAPHICS_H +#define GRAPHICS_H + +#include +#include + + +#include +#include + + +typedef SDL_Surface Screen; +typedef SDL_Rect Rectangle; + + +typedef short unsigned int SUint; + +class Texture; +class Font; + + + +class Graphics +{ + public: + + class Colour + { + public: + Colour(float red=0, float green=0, float blue=0, float alpha=0) : r(red), g(green), b(blue), a(alpha) {} + Colour(const Colour & cpy) : r(cpy.r), g(cpy.g), b(cpy.b), a(cpy.a) {} + + Colour & operator=(const Colour & s) {r = s.r; g = s.g; b = s.b; a = s.a; return *this;} + bool operator==(const Colour & s) const {return (r == s.r && g == s.g && b == s.b && a == s.a);} + bool operator!=(const Colour & s) const {return !operator==(s);} + float r; + float g; + float b; + float a; + + }; + static int ScreenWidth() {return screenWidth;} + static int ScreenHeight() {return screenHeight;} + + static void Initialise(const char * caption, int width=640, int height=480); + static void Destroy(); + + static SDL_Surface * LoadTextureBMP(const char * file); + static void SaveTextureBMP(SDL_Surface * tex, const char * file); + + static void ClearScreen(); + static void UpdateScreen(); + static void DrawGrid(int gridWidth, int gridHeight, Colour colour); + static Uint8 MakeColour(int R, int G, int B, int Alpha = 0); + static Colour ConvertColour(Uint8 colour); + + static void DrawTexture(SDL_Surface * src, int destX, int destY, int srcX=0, int srcY=0, int width=-1, int height=-1); + static void DrawTexture(SDL_Surface * src, int destX, int destY, double rotate, double scale); + static void DrawPixel(int x, int y, Colour colour); + + static Colour GetPixel(int x, int y); + static void DrawLine(int x1, int y1, int x2, int y2, Colour colour, double scale=1.0); + static void DrawLineDashed(int x1, int y1, int x2, int y2, Colour colour, double scale=1.0); + static void DrawRectangle(int x1, int y1, int x2, int y2, Colour colour, double scale=1.0); + + static void GetColourData(SDL_Surface * src, std::vector * R, std::vector * G, std::vector * B, std::vector * A = NULL); + static void GetColourData(SDL_Surface * src, std::vector > * R, std::vector > * G, std::vector > * B, std::vector > * A = NULL); + + static void DrawColourData(int destX, int destY, std::vector * R, std::vector * G, std::vector * B, std::vector * A = NULL) {DrawColourData(screen, destX, destY, R, G, B, A);} + + static void DrawColourData(int destX, int destY, std::vector > * R, std::vector > * G, std::vector > * B, std::vector > * A = NULL) {DrawColourData(screen, destX, destY, R, G, B, A);} + + static SDL_Surface * TextureFromColours(std::vector * R, std::vector * G, std::vector * B, std::vector * A = NULL); + static SDL_Surface * TextureFromColours(std::vector > * R, std::vector > * G, std::vector > * B, std::vector > * A = NULL); + + static void Wait(int n) {SDL_Delay(n);} + + template + class TextureManager + { + public: + TextureManager() {} + virtual ~TextureManager() {} + + virtual Texture & operator[](const T & at) = 0; + }; + + static bool Initialised() {return initialised;} + + protected: + static void DrawColourData(SDL_Surface * dest, int destX, int destY, std::vector * R, std::vector * G, std::vector * B, std::vector * A = NULL); + static void DrawColourData(SDL_Surface * dest, int destX, int destY, std::vector > * R, std::vector > * G, std::vector > * B, std::vector > * A = NULL); + static void DrawTexture(SDL_Surface * dest, SDL_Surface * src, int srcX, int srcY, int destX, int destY, int width, int height); + static void DrawPixel(SDL_Surface * dest, int x, int y, Colour colour); + static Colour GetPixel(SDL_Surface * dest, int x, int y); + static void DrawLine(SDL_Surface * dest, int x1, int y1, int x2, int y2, Colour colour); + + private: + Graphics() {} + ~Graphics() {} + static std::list allTextures; + static Screen * screen; + + static int screenWidth; + static int screenHeight; + static bool initialised; + + +}; +typedef Graphics::Colour Colour; + +class Texture +{ + public: + Texture(const char * fileName, bool newDrawCentred = true); + virtual ~Texture(); + + void Draw(int x, int y, double angle=0, double scale=1); + void DrawColour(int x, int y, double angle, double scale, Colour colour); + + int width() const {return surface->w;} + int height() const {return surface->h;} + protected: + SDL_Surface * surface; + GLuint texture; + + private: + bool drawCentred; + +}; + +class Font : private Texture +{ + public: + Font(const char * fileName, int newWidth, int newHeight); + virtual ~Font(); + + void DrawTextColour(const char * string, int x, int y, double angle, double scale, Colour colour); + void DrawText(const char * string, int x, int y, double angle=0, double scale=1); + private: + int width; + int height; +}; + + +#endif //GRAPHICS_H + +//EOF diff --git a/progcomp/judge/manager/human_controller.cpp b/progcomp/judge/manager/human_controller.cpp new file mode 100644 index 0000000..bf43e8d --- /dev/null +++ b/progcomp/judge/manager/human_controller.cpp @@ -0,0 +1,181 @@ +#include "human_controller.h" + +#include "game.h" + +#include //Really I can't be bothered with fscanf any more + +using namespace std; + +MovementResult Human_Controller::QuerySetup(const char * opponentName, string setup[]) +{ + + static bool shownMessage = false; + if (!shownMessage) + { + if (graphicsEnabled) + fprintf(stdout, "WARNING: GUI not fully supported. You will be forced to use the default setup.\n"); + else + { + fprintf(stdout,"Enter %d x %d Setup grid\n", Game::theGame->theBoard.Width(), 4); + fprintf(stdout,"Please enter one line at a time.\n"); + fprintf(stdout, "You must place at least the Flag (%c). Use '%c' for empty squares.\n", Piece::tokens[(int)Piece::FLAG], Piece::tokens[(int)Piece::NOTHING]); + + switch (colour) + { + case Piece::RED: + fprintf(stdout, "You are RED and occupy the top 4 rows of the board.\n"); + fprintf(stdout, "NOTE: Enter \"DEFAULT\" to use the setup:\n"); + fprintf(stdout, "FB8sB479B8\nBB31555583\n6724898974\n967B669999\n"); + break; + case Piece::BLUE: + fprintf(stdout, "You are BLUE and occupy the bottom 4 rows of the board.\n"); + fprintf(stdout, "NOTE: Enter \"DEFAULT\" to use the setup:\n"); + fprintf(stdout, "967B669999\n6724898974\nBB31555583\nFB8sB479B8\n"); + break; + default: + fprintf(stdout, "WARNING: Unknown colour error! Please exit the game.\n"); + break; + } + } + + shownMessage = true; + } + + if (graphicsEnabled) + { + switch(colour) + { + case Piece::RED: + setup[0] = "FB8sB479B8"; + setup[1] = "BB31555583"; + setup[2] = "6724898974"; + setup[3] = "967B669999"; + break; + case Piece::BLUE: + setup[0] = "967B669999"; + setup[1] = "6724898974"; + setup[2] = "BB31555583"; + setup[3] = "FB8sB479B8"; + break; + default: + assert(false); + break; + } + return MovementResult::OK; + } + + for (int y = 0; y < 4; ++y) + { + cin >> setup[y]; + if (y == 0 && setup[0] == "DEFAULT") + { + switch(colour) + { + case Piece::RED: + setup[0] = "FB8sB479B8"; + setup[1] = "BB31555583"; + setup[2] = "6724898974"; + setup[3] = "967B669999"; + break; + case Piece::BLUE: + setup[0] = "967B669999"; + setup[1] = "6724898974"; + setup[2] = "BB31555583"; + setup[3] = "FB8sB479B8"; + break; + default: + assert(false); + break; + } + break; + } + } + assert(cin.get() == '\n'); + + return MovementResult::OK; +} + +MovementResult Human_Controller::QueryMove(string & buffer) +{ + static bool shownMessage = false; + if (!shownMessage) + { + if (!graphicsEnabled) + { + fprintf(stdout, "Please enter your move in the format:\n X Y DIRECTION [MULTIPLIER=1]\n"); + fprintf(stdout, "Where X and Y indicate the coordinates of the piece to move;\n DIRECTION is one of UP, DOWN, LEFT or RIGHT\n and MULTIPLIER is optional (and only valid for scouts (%c))\n", Piece::tokens[(int)(Piece::SCOUT)]); + + } + shownMessage = true; + } + + + + if (graphicsEnabled) + { + fprintf(stdout, "Click to move!\n"); + SDL_Event event; int mouseClick = 0; + + int x[] = {-1, -1}; int y[] = {-1, -1}; + while (mouseClick < 2) + { + + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_QUIT: + Game::theGame->logMessage("Exit called by human player!\n"); + exit(EXIT_SUCCESS); + break; + case SDL_MOUSEBUTTONDOWN: + switch (event.button.button) + { + case SDL_BUTTON_LEFT: + SDL_GetMouseState(&x[mouseClick], &y[mouseClick]); + x[mouseClick] /= 32; y[mouseClick] /= 32; //Adjust based on graphics grid size + if (mouseClick == 0) + { + stringstream s(""); + s << x[0] << " " << y[0] << " "; + buffer += s.str(); + } + else if (mouseClick == 1) + { + int xDist = x[1] - x[0]; + int yDist = y[1] - y[0]; + if (abs(xDist) > abs(yDist)) + { + if (xDist < 0) + buffer += "LEFT"; + else + buffer += "RIGHT"; + } + else if (yDist < 0) + buffer += "UP"; + else + buffer += "DOWN"; + } + mouseClick++; + break; + } + break; + } + } + } + fprintf(stdout, "Move complete!\n"); + } + else + { + buffer.clear(); + for (char in = fgetc(stdin); in != '\n'; in = fgetc(stdin)) + { + buffer += in; + } + } + + + + return MovementResult::OK; + +} diff --git a/progcomp/judge/manager/human_controller.h b/progcomp/judge/manager/human_controller.h new file mode 100644 index 0000000..b2069fc --- /dev/null +++ b/progcomp/judge/manager/human_controller.h @@ -0,0 +1,26 @@ +#ifndef HUMAN_CONTROLLER_H +#define HUMAN_CONTROLLER_H + +#include "controller.h" + +/** + * Class to control a human player playing Stratego + */ +class Human_Controller : public Controller +{ + public: + Human_Controller(const Piece::Colour & newColour, const bool enableGraphics) : Controller(newColour, "human"), graphicsEnabled(enableGraphics) {} + virtual ~Human_Controller() {} + + virtual bool HumanController() const {return true;} + virtual MovementResult QuerySetup(const char * opponentName, std::string setup[]); + virtual MovementResult QueryMove(std::string & buffer); + virtual void Message(const char * message) {fprintf(stderr, "%s\n", message);} + + private: + const bool graphicsEnabled; + + +}; + +#endif //AI_CONTROLLER_H diff --git a/progcomp/judge/manager/images/piece0.bmp b/progcomp/judge/manager/images/piece0.bmp new file mode 100644 index 0000000..bd67766 Binary files /dev/null and b/progcomp/judge/manager/images/piece0.bmp differ diff --git a/progcomp/judge/manager/images/piece1.bmp b/progcomp/judge/manager/images/piece1.bmp new file mode 100644 index 0000000..85fd5ba Binary files /dev/null and b/progcomp/judge/manager/images/piece1.bmp differ diff --git a/progcomp/judge/manager/images/piece10.bmp b/progcomp/judge/manager/images/piece10.bmp new file mode 100644 index 0000000..cf77ed4 Binary files /dev/null and b/progcomp/judge/manager/images/piece10.bmp differ diff --git a/progcomp/judge/manager/images/piece11.bmp b/progcomp/judge/manager/images/piece11.bmp new file mode 100644 index 0000000..b006d3d Binary files /dev/null and b/progcomp/judge/manager/images/piece11.bmp differ diff --git a/progcomp/judge/manager/images/piece12.bmp b/progcomp/judge/manager/images/piece12.bmp new file mode 100644 index 0000000..8c67a48 Binary files /dev/null and b/progcomp/judge/manager/images/piece12.bmp differ diff --git a/progcomp/judge/manager/images/piece13.bmp b/progcomp/judge/manager/images/piece13.bmp new file mode 100644 index 0000000..f822c24 Binary files /dev/null and b/progcomp/judge/manager/images/piece13.bmp differ diff --git a/progcomp/judge/manager/images/piece14.bmp b/progcomp/judge/manager/images/piece14.bmp new file mode 100644 index 0000000..3145270 Binary files /dev/null and b/progcomp/judge/manager/images/piece14.bmp differ diff --git a/progcomp/judge/manager/images/piece2.bmp b/progcomp/judge/manager/images/piece2.bmp new file mode 100644 index 0000000..0a11b11 Binary files /dev/null and b/progcomp/judge/manager/images/piece2.bmp differ diff --git a/progcomp/judge/manager/images/piece3.bmp b/progcomp/judge/manager/images/piece3.bmp new file mode 100644 index 0000000..40ca3f8 Binary files /dev/null and b/progcomp/judge/manager/images/piece3.bmp differ diff --git a/progcomp/judge/manager/images/piece4.bmp b/progcomp/judge/manager/images/piece4.bmp new file mode 100644 index 0000000..0a27a46 Binary files /dev/null and b/progcomp/judge/manager/images/piece4.bmp differ diff --git a/progcomp/judge/manager/images/piece5.bmp b/progcomp/judge/manager/images/piece5.bmp new file mode 100644 index 0000000..051bd43 Binary files /dev/null and b/progcomp/judge/manager/images/piece5.bmp differ diff --git a/progcomp/judge/manager/images/piece6.bmp b/progcomp/judge/manager/images/piece6.bmp new file mode 100644 index 0000000..5ca389b Binary files /dev/null and b/progcomp/judge/manager/images/piece6.bmp differ diff --git a/progcomp/judge/manager/images/piece7.bmp b/progcomp/judge/manager/images/piece7.bmp new file mode 100644 index 0000000..aaf28a1 Binary files /dev/null and b/progcomp/judge/manager/images/piece7.bmp differ diff --git a/progcomp/judge/manager/images/piece8.bmp b/progcomp/judge/manager/images/piece8.bmp new file mode 100644 index 0000000..b2ea5b7 Binary files /dev/null and b/progcomp/judge/manager/images/piece8.bmp differ diff --git a/progcomp/judge/manager/images/piece9.bmp b/progcomp/judge/manager/images/piece9.bmp new file mode 100644 index 0000000..e37e251 Binary files /dev/null and b/progcomp/judge/manager/images/piece9.bmp differ diff --git a/progcomp/judge/manager/main.cpp b/progcomp/judge/manager/main.cpp new file mode 100644 index 0000000..1db8d42 --- /dev/null +++ b/progcomp/judge/manager/main.cpp @@ -0,0 +1,275 @@ +#include +#include + + + + + + +#include "game.h" + +using namespace std; + +Piece::Colour SetupGame(int argc, char ** argv); +void DestroyGame(); +void PrintResults(const MovementResult & result, string & buffer); + +int main(int argc, char ** argv) +{ + + + + if (argc == 1) + { + fprintf(stderr, "Usage: stratego [options] red blue\n"); + fprintf(stderr, " stratego --help\n"); + exit(EXIT_SUCCESS); + + } + + + Piece::Colour setupError = SetupGame(argc, argv); + MovementResult result = MovementResult::OK; + if (setupError == Piece::NONE) + { + result = Game::theGame->Play(); + } + else + { + result = MovementResult::BAD_SETUP; + Game::theGame->ForceTurn(setupError); + } + + Game::theGame->PrintEndMessage(result); + + string buffer = ""; + PrintResults(result, buffer); + + //Message the AI's the quit message + Game::theGame->red->Message("QUIT " + buffer); + Game::theGame->blue->Message("QUIT " + buffer); + + //Log the message + if (Game::theGame->GetLogFile() != stdout) + Game::theGame->logMessage("%s\n", buffer.c_str()); + + fprintf(stdout, "%s\n", buffer.c_str()); + + exit(EXIT_SUCCESS); + return 0; +} + +Piece::Colour SetupGame(int argc, char ** argv) +{ + char * red = NULL; char * blue = NULL; double timeout = 0.00001; bool graphics = false; bool allowIllegal = false; FILE * log = NULL; + Piece::Colour reveal = Piece::BOTH; char * inputFile = NULL; int maxTurns = 5000; bool printBoard = false; + for (int ii=1; ii < argc; ++ii) + { + if (argv[ii][0] == '-') + { + switch (argv[ii][1]) + { + case 't': + if (argc - ii <= 1) + { + fprintf(stderr, "ARGUMENT_ERROR - Expected timeout value after -t switch!\n"); + exit(EXIT_FAILURE); + } + if (strcmp(argv[ii+1], "inf") == 0) + timeout = -1; + else + timeout = atof(argv[ii+1]); + ++ii; + break; + case 'g': + graphics = !graphics; + break; + case 'p': + printBoard = !printBoard; + break; + case 'i': + allowIllegal = true; + break; + + case 'o': + if (argc - ii <= 1) + { + fprintf(stderr, "ARGUMENT_ERROR - Expected filename or \"stdout\" after -o switch!\n"); + exit(EXIT_FAILURE); + } + if (log != NULL) + { + fprintf(stderr, "ARGUMENT_ERROR - Expected at most ONE -o switch!\n"); + exit(EXIT_FAILURE); + } + if (strcmp(argv[ii+1], "stdout") == 0) + log = stdout; + else + log = fopen(argv[ii+1], "w"); + setbuf(log, NULL); + + ++ii; + break; + + case 'r': + if (reveal == Piece::BOTH) + reveal = Piece::BLUE; + else + reveal = Piece::NONE; + break; + case 'b': + if (reveal == Piece::BOTH) + reveal = Piece::RED; + else + reveal = Piece::NONE; + break; + case 'm': + if (argc - ii <= 1) + { + fprintf(stderr, "ARGUMENT_ERROR - Expected max_turns value after -m switch!\n"); + exit(EXIT_FAILURE); + } + if (strcmp(argv[ii+1], "inf") == 0) + maxTurns = -1; + else + maxTurns = atoi(argv[ii+1]); + ++ii; + break; + case 'f': + if (argc - ii <= 1) + { + fprintf(stderr, "ARGUMENT_ERROR - Expected filename after -f switch!\n"); + exit(EXIT_FAILURE); + } + if (log != NULL) + { + fprintf(stderr, "ARGUMENT_ERROR - Expected at most ONE -f switch!\n"); + exit(EXIT_FAILURE); + } + red = (char*)("file"); + blue = (char*)("file"); + inputFile = argv[ii+1]; + ++ii; + break; + case 'h': + system("clear"); + system("less manual.txt"); + exit(EXIT_SUCCESS); + break; + case '-': + if (strcmp(argv[ii]+2, "help") == 0) + { + system("clear"); + system("less manual.txt"); + exit(EXIT_SUCCESS); + } + else + { + fprintf(stderr, "ARGUMENT_ERROR - Unrecognised switch \"%s\"...\n", argv[ii]); + exit(EXIT_FAILURE); + } + } + + } + else + { + if (red == NULL) + red = argv[ii]; + else if (blue == NULL) + blue = argv[ii]; + else + { + fprintf(stderr, "ARGUMENT_ERROR - Unexpected argument \"%s\"...\n", argv[ii]); + exit(EXIT_FAILURE); + } + } + } + + + + if (inputFile == NULL) + { + if (red == NULL || blue == NULL) //Not enough arguments + { + fprintf(stderr, "ARGUMENT_ERROR - Did not recieve enough players (did you mean to use the -f switch?)\n"); + exit(EXIT_FAILURE); + } + Game::theGame = new Game(red,blue, graphics, timeout, allowIllegal,log, reveal,maxTurns, printBoard); + } + else + { + Game::theGame = new Game(inputFile, graphics, timeout, allowIllegal,log, reveal,maxTurns, printBoard); + } + + if (Game::theGame == NULL) + { + fprintf(stderr,"INTERNAL_ERROR - Error creating Game!\n"); + exit(EXIT_FAILURE); + } + atexit(DestroyGame); + + return Game::theGame->Setup(red, blue); + + +} + +void PrintResults(const MovementResult & result, string & buffer) +{ + stringstream s(""); + switch (Game::theGame->Turn()) + { + case Piece::RED: + s << Game::theGame->red->name << " RED "; + break; + case Piece::BLUE: + s << Game::theGame->blue->name << " BLUE "; + break; + case Piece::BOTH: + s << "neither BOTH "; + break; + case Piece::NONE: + s << "neither NONE "; + break; + } + + if (!Board::LegalResult(result) && result != MovementResult::BAD_SETUP) + s << "ILLEGAL "; + else if (!Board::HaltResult(result)) + s << "INTERNAL_ERROR "; + else + { + switch (result.type) + { + case MovementResult::VICTORY: + s << "VICTORY "; + break; + case MovementResult::SURRENDER: + s << "SURRENDER "; + break; + case MovementResult::DRAW: + s << "DRAW "; + break; + case MovementResult::DRAW_DEFAULT: + s << "DRAW_DEFAULT "; + break; + case MovementResult::BAD_SETUP: + s << "BOTH_ILLEGAL "; + break; + default: + s << "INTERNAL_ERROR "; + break; + } + } + + s << Game::theGame->TurnCount() << " " << Game::theGame->theBoard.TotalPieceValue(Piece::RED) << " " << Game::theGame->theBoard.TotalPieceValue(Piece::BLUE); + + buffer = s.str(); + + +} + +void DestroyGame() +{ + delete Game::theGame; + Game::theGame = NULL; +} diff --git a/progcomp/judge/manager/manual.txt b/progcomp/judge/manager/manual.txt new file mode 100644 index 0000000..3237241 --- /dev/null +++ b/progcomp/judge/manager/manual.txt @@ -0,0 +1,230 @@ +NAME + stratego - Interface to manage games of stratego between AI programs and/or human players + +WARNING + This program is still a work in progress. Consider it a Beta version. + +SYNOPSIS + stratego {[-gpirb] [-o output_file ] [-t stall_time] [-m max_turns] {red_player blue_player | -f input_file} | {-h | --help} } + +DESCRIPTION + stratego manages a game of Stratego. It stores the state of the board, and uses a simple protocol to interface with AI programs. + By itself, stratego does not "play" the game. An external AI program must be used. stratego is intended to be used for the testing of + various AI strategies, written in any programming language. It will be used for the UCC Programming Competition 2012. + + Unless the -h (--help) or -f switch is given, both red_player and blue_player must be supplied. + + red_player + Should be either a path to an executable file which will control the Red player, or "human". + If set to "human", stratego will request the user to make moves for the Red player using stdin. + NOTES + 1. There is no plan to support AI programs named "human". Deal with it. + 2. The graphical interface for human players is... basic. Deal with it. + + blue_player + As red_player, except for controlling the Blue player. + +OPTIONS + -g + By default, graphics are disabled. If the -g switch is present, stratego will draw the game as it is played using SDL/OpenGL + + -p + By default, even if graphics are disabled, the board state is not printed. If -p is present, the board will be printed to stdout. + If the system supports colour, the characters will be in colour. + Yes, If -p and -g are both present you will see both behaviours (overkill)! + -i + By default, stratego will exit if a move which is deemed "illegal" is made. If the -i switch is present, illegal moves will be ignored. + That is, the move will not be made (effectively the player making the illegal move loses a turn). + + NOTE: If -i is not given and a human player accidentally(?) makes an illegal move, they will be asked to make a different move. The game will continue. + This is intended to prevent fits of rage due to the horrible graphical interface causing humans to make illegal moves. + -r + By default, the identities of all pieces are shown. If the -r switch is present, and graphics are enabled, red pieces will be disguised. + If graphics are disabled, the -r switch has no effect. + + Pieces which have previously taken part in combat (and survived) will be revealed. + -b + As -r, except blue pieces will be disguised. + NOTE: Both -r and -b may be used together. + -o + By default, stratego does not log moves. If the -o switch is present, the result of each move is printed to a file. + If output_file is "stdout" then stdout will be used instead of a text file. + -t + By default, stratego executes moves as fast as they are recieved. If the -t switch is present, a delay of stall_time will be introduced + between each move. + + If stall_time is negative or "inf", stratego will wait for the user to press enter before moving to the next move. + + It is tentatively planned to allow the user to enter various commands to alter the game or proceed to specified turns. + However this is slightly complicated. So it might never be done. + -m + By default, the game is declared a Draw after 5000 turns have ellapsed. + Use this option to change the maximum number of turns. + To play for an infinite number of turns, supply "inf" as max_number. This is not recommended for obvious reasons. + + -f + By default, stratego requires red_player and blue_player to enact a game. + If this option is supplied, a file previously produced by using the -o switch is read, and the game reenacted. + All switches function as normal with -f. + NOTE: It is recommended that -g is used with -f. + + -h, --help + If the -h switch is used, this page will be printed and stratego will exit. + + + +GAME RULES + Each player controls up to 40 pieces on the Board. The pieces consist of the following: + + Piece Name Rank Number Abilities + 1 Marshal 1 1 Dies if attacked by Spy + 2 General 2 1 + 3 Colonel 3 2 + 4 Major 4 3 + 5 Captain 5 4 + 6 Lieutenant 6 4 + 7 Sergeant 7 4 + 8 Miner 8 5 Destroys Bombs without being killed + 9 Scout 9 8 May move more through multiple empty squares + s Spy 10 1 If the Spy attacks the Marshal, the Marshal dies + B Bomb NA 6 Immobile. If any piece (except a Miner) encounters an enemy Bomb, both pieces are destroyed + F Flag NA 1 Immobile. If any piece encounters the enemy Flag, the controlling player wins. + + Additional pieces, not controlled by the player: + Piece Name Number Notes + + Obstacle 8 Immobile. Do not belong to either player. Can't be passed through. + # Enemy Piece 0 - 40 Indicates that the position on the board is occupied by an enemy piece. + . Empty NA Indicates that the position on the board is empty. + + Players take turns to move their pieces. RED begins the game. + + Pieces may only move one square horizontally or vertically unless otherwise stated. + Pieces may not move through squares occupied by allied pieces, or Obstacle (+) pieces. + Pieces may move into squares occupied by Enemy Pieces (#), in which case the piece with the lower rank (higher number) is destroyed. + + Each player's pieces are hidden from the other player. When two pieces encounter each other, the ranks will be revealed. + + The objective is to destroy all Enemy Pieces (#) or capture the Enemy Flag (also #). + + +PROTOCOL + In order to interface with stratego, an AI program must satisfy the following protocol. + Each query is followed by a newline, and responses are expected to be followed with a newline. + The queries are recieved through stdin, and responses should be written to stdout. + + 1. SETUP + QUERY: YOUR_COLOUR OPPONENT_ID BOARD_WIDTH BOARD_HEIGHT + + RESPONSE: 4 lines, each of length BOARD_WIDTH, of characters. Each character represents a piece. The characters are shown above. + + RED's pieces are placed at the top of the board, and BLUE's pieces are placed at the bottom. + + An AI program does not have to place all 40 pieces, but must at least place the flag ('F'). + + 2. TURN + QUERY: START | CONFIRMATION + BOARD_STATE + + On the first turn, "START" is printed to the Red player. + On subsequent turns, the CONFIRMATION of the opponent's last turn is printed (see below). + + BOARD_STATE consists of a BOARD_HEIGHT lines of length BOARD_WIDTH characters, each of which represents a single piece + as described in the GAME_RULES section. Each line ends with the newline character. + + + RESPONSE: X Y DIRECTION [MULTIPLIER=1] | NO_MOVE + X and Y are the coords (starting from 0) of the piece to move + DIRECTION is either UP, DOWN, LEFT or RIGHT + MULTIPLIER is optional and only valid for units of type Scout. Scouts may move through any number of unblocked squares + in one direction. + + The AI program should print "NO_MOVE" if it is unable to determine a move. + This will typically occur when the only pieces belonging to the AI program are Bombs and the Flag. + + CONFIRMATION: X Y DIRECTION [MULTIPLIER=1] OUTCOME | NO_MOVE {OK | ILLEGAL} | QUIT [RESULT] + + OUTCOME may be either OK, ILLEGAL, KILLS or DIES + OK - Move was successful + ILLEGAL - Move was not allowed. If stratego was not started with the -i switch, the game will end. + KILLS ATTACKER_RANK DEFENDER_RANK - The piece moved into an occupied square and killed the defender. + DIES ATTACKER_RANK DEFENDER_RANK - The piece moved into an occupied square and was killed by the defender. + + Most turns will be confirmed with: "X Y DIRECTION [MULTIPLIER=1] OUTCOME" + + A confirmation of "NO_MOVE OK" occurs when the AI program made no move for a legitimate reason. + "NO_MOVE ILLEGAL" is printed if the AI program made no move for an illegitimate reason. + + If both AI programs successively make a "NO_MOVE" response, then the game will end. + The player with the highest piece value will win, or a draw will be declared if the values are equal. + + 3. END GAME + If the CONFIRMATION line is of the form: + QUIT [RESULT] + Then the game is about to end. + + If present, RESULT will be a direct copy of the message to stdout described in the EXIT/OUTPUT section below. + + + 4. TIMEOUTS + If a program fails to respond to a query within 2 (two) seconds, the game will end and that AI will be sent the ILLEGAL result. + Human players are not subject to the timeout restriction. + + + +EXIT/OUTPUT + If the game ends due to a player either winning, or making an illegal move, stratego will print one of the following result messages to stdout. + + NAME COLOUR OUTCOME TURN_NUMBER OUTCOME RED_PIECE_VALUE BLUE_PIECE_VALUE + + Where: + NAME is the name of the player on whose turn the game ended, + COLOUR is the colour of that player, + OUTCOME is one of the following: + VICTORY - The indicated player won + DEFEAT - The indicated player lost + SURRENDER - The indicated player surrendered + DRAW - The game ended in a draw because neither player moved + DRAW_DEFAULT - The game ended in a draw because the maximum number of moves was exceeded + ILLEGAL - The indicated player loses due to an Illegal move/response + DEFAULT - The indicated player wins by default due to the other player making an Illegal move/response + BOTH_ILLEGAL - Both players made an Illegal move/response. Usually occurs due to simultaneous setup errors, or bad executable paths. + INTERNAL_ERROR - The game ended, even though it shouldn't have. + + TURN_NUMBER is the number of turns that elapsed before the game ended + + RED_PIECE_VALUE and BLUE_PIECE_VALUE are the summed piece values of the pieces of RED and BLUE respectively. + Bombs and Flags are worth zero, and the ranked pieces (Spys -> Marshal) are worth (11 - rank). + So the Spy is worth 1 point, ... the Marshal is worth 10. + + (The initial piece values can be determined by running with -m 0) + + + stratego will then return exit code 0. + + If an error occurs within stratego itself, an error message will be printed to stderr and return exit code 1. + If possible, stratego will print the message "QUIT" to both AI programs, and they should exit as soon as possible. + + +BUGS + WARNING: + stratego has been observed to segfault occassionally after the end of a game. It is not yet known what is causing these errors. + They appear to occur most often when the result is a draw, however they have been observed to occur under all exit conditions except the Illegal case. The result is still printed to stdout. However this bug _must_ be fixed before stratego can be used by simulation scripts. + + stratego is still a work in progress. Report another bug to the AUTHOR (see below). + +AUTHORS + Sam Moore (for the UCC Programming Competition 2012) + +NOTES + 0. This program is still a work in progress and subject to changes. + + 1. UCC Programming Competition 2012 Description + http://matches.ucc.asn.au/stratego/ + + 2. UCC Programming Competition 2012 Git repository + git://git.ucc.asn.au/progcomp2012.git + + + 3. IRC Channel + irc://irc.ucc.asn.au #progcomp + diff --git a/progcomp/judge/manager/movementresult.h b/progcomp/judge/manager/movementresult.h new file mode 100644 index 0000000..5a2aed6 --- /dev/null +++ b/progcomp/judge/manager/movementresult.h @@ -0,0 +1,34 @@ +/** + * Contains declaration for MovementResult class + */ +#ifndef MOVERESULT_H +#define MOVERESULT_H + +class Board; +class Piece; + +/** + * Class used to indicate the result of a move in stratego + */ +class MovementResult +{ + public: + typedef enum {OK, DIES, KILLS, BOTH_DIE, NO_BOARD, INVALID_POSITION, NO_SELECTION, NOT_YOUR_UNIT, IMMOBILE_UNIT, INVALID_DIRECTION, POSITION_FULL, VICTORY, SURRENDER, BAD_RESPONSE, NO_MOVE, COLOUR_ERROR, ERROR, DRAW_DEFAULT, DRAW, BAD_SETUP} Type; + + MovementResult(const Type & result = OK, const Piece::Type & newAttackerRank = Piece::NOTHING, const Piece::Type & newDefenderRank = Piece::NOTHING) + : type(result), attackerRank(newAttackerRank), defenderRank(newDefenderRank) {} + MovementResult(const MovementResult & cpy) : type(cpy.type), attackerRank(cpy.attackerRank), defenderRank(cpy.defenderRank) {} + virtual ~MovementResult() {} + + + bool operator==(const Type & equType) const {return type == equType;} + bool operator!=(const Type & equType) const {return type != equType;} + + Type type; + Piece::Type attackerRank; + Piece::Type defenderRank; +}; + +#endif //MOVERESULT_H + +//EOF diff --git a/progcomp/judge/manager/program.cpp b/progcomp/judge/manager/program.cpp new file mode 100644 index 0000000..660362a --- /dev/null +++ b/progcomp/judge/manager/program.cpp @@ -0,0 +1,193 @@ +#include + +#include + +#include + +#include "thread_util.h" +#include "program.h" + + +using namespace std; + + +/** + * Constructor + * @param executablePath - path to the program that will be run + * + * Creates two pipes - one for each direction between the parent process and the AI program + * Forks the process. + * The child process closes unused sides of the pipe, and then calls exec to replace itself with the AI program + * The parent process closes unused sides of the pipe, and sets up member variables - associates streams with the pipe fd's for convenience. + */ +Program::Program(const char * executablePath) : input(NULL), output(NULL), pid(0) +{ + //See if file exists... + FILE * file = fopen(executablePath, "r"); + if (file != NULL) + { + fclose(file); + } + else + { + pid = -1; + return; + } + + int readPipe[2]; int writePipe[2]; + assert(pipe(readPipe) == 0); + assert(pipe(writePipe) == 0); + + pid = fork(); + if (pid == 0) + { + close(readPipe[0]); //close end that parent reads from + close(writePipe[1]); //close end that parent writes to + + //TODO: Fix possible bug here if the process is already a daemon + assert(writePipe[0] != 0 && readPipe[1] != 1); + dup2(writePipe[0],0); close(writePipe[0]); //pipe end child reads from goes to STDIN + dup2(readPipe[1], 1); close(readPipe[1]); //pipe end child writes to goes to STDOUT + + //TODO: Somehow force the exec'd process to be unbuffered + setbuf(stdin, NULL); //WARNING: These lines don't appear to have any affect + setbuf(stdout, NULL); //You should add them at the start of the wrapped program. + //If your wrapped program is not written in C/C++, you will probably have a problem + + + + execl(executablePath, executablePath, (char*)(NULL)); ///Replace process with desired executable + //fprintf(stderr, "Program::Program - Could not run program \"%s\"!\n", executablePath); + //exit(EXIT_FAILURE); //We will probably have to terminate the whole program if this happens + } + else + { + close(writePipe[0]); //close end that child writes to + close(readPipe[1]); //close end that child reads from + + input = fdopen(readPipe[0],"r"); output = fdopen(writePipe[1],"w"); + setbuf(input, NULL); + setbuf(output, NULL); + } + +} + +/** + * Destructor + * Writes EOF to the wrapped program and then closes all streams + * Kills the wrapped program if it does not exit within 1 second. + */ +Program::~Program() +{ + if (Running()) //Check if the process created is still running... + { + //fputc(EOF, output); //If it was, tell it to stop with EOF + + TimerThread timer(2); //Wait for 2 seconds + timer.Start(); + while (!timer.Finished()) + { + if (!Running()) + { + timer.Stop(); + break; + } + } + timer.Stop(); + kill(pid, SIGKILL); + } + if (pid > 0) + { + fclose(input); + fclose(output); + } + +} + + + + + +/** + * Sends a message to the wrapped AI program + * WARNING: Always prints a new line after the message (so don't include a new line) + * This is because everything is always line buffered. + * @returns true if the message was successfully sent; false if it was not (ie: the process was not running!) + */ +bool Program::SendMessage(const char * print, ...) +{ + if (!Running()) //Is the process running... + return false; + + va_list ap; + va_start(ap, print); + + if (vfprintf(output, print, ap) < 0 || fprintf(output, "\n") < 0) + { + va_end(ap); + return false; + } + va_end(ap); + + + + + return true; +} + + +/** + * Retrieves a message from the wrapped AI program, waiting a maximum amount of time + * @param buffer - C++ string to store the resultant message in + * @param timeout - Maximum amount of time to wait before failure. If timeout <= 0, then GetMessage will wait indefinately. + * @returns true if the response was recieved within the specified time, false if it was not, or an EOF was recieved, or the process was not running. + */ +bool Program::GetMessage(string & buffer, double timeout) +{ + if (!Running()) + return false; + + assert(&buffer != NULL); + GetterThread getterThread(input, buffer); + assert(&(getterThread.buffer) != NULL); + TimerThread timerThread(timeout*1000000); + + getterThread.Start(); + if (timeout > 0) + timerThread.Start(); + + + while (!getterThread.Finished()) + { + if (timeout > 0 && timerThread.Finished()) + { + getterThread.Stop(); + timerThread.Stop(); + return false; + } + } + + getterThread.Stop(); + timerThread.Stop(); + + + + if (buffer.size() == 1 && buffer[0] == EOF) + return false; + return true; + + +} + +/** + * Returns true iff the process is running + * @returns what I just said, fool + */ +bool Program::Running() const +{ + return (pid > 0 && kill(pid,0) == 0); +} + + + + diff --git a/progcomp/judge/manager/program.h b/progcomp/judge/manager/program.h new file mode 100644 index 0000000..8fef696 --- /dev/null +++ b/progcomp/judge/manager/program.h @@ -0,0 +1,40 @@ +#ifndef PROGRAM_H +#define PROGRAM_H + +#include "thread_util.h" + +#include + +/** + * A wrapping class for an external program, which can exchange messages with the current process through stdin/stdout + * Useful for attaching control of an operation to an external process - for example, AI for a game + */ +class Program +{ + public: + Program(const char * executeablePath); //Constructor + virtual ~Program(); //Destructor + + + + + bool SendMessage(const char * print, ...); //Sends a formated message (NOTE: Prints a newline) + bool SendMessage(const std::string & buffer) {return SendMessage(buffer.c_str());} //Sends a C++ string message + bool GetMessage(std::string & buffer, double timeout=-1); //Retrieves a message, or waits for a timeout (if positive) + + bool Running() const; + + + + protected: + FILE * input; //Stream used for sending information TO the process + FILE * output; //Stream used for retrieving information FROM the process + + private: + pid_t pid; //Process ID of the program wrapped + +}; + +#endif //PROGRAM_H + + diff --git a/progcomp/judge/manager/stratego.cpp b/progcomp/judge/manager/stratego.cpp new file mode 100644 index 0000000..f2df0ff --- /dev/null +++ b/progcomp/judge/manager/stratego.cpp @@ -0,0 +1,512 @@ + + +#include "stratego.h" + +using namespace std; + +/** + * Static variables + */ + +//nothing, boulder, flag, spy, scout, miner, sergeant, lietenant, captain, major, colonel, general, marshal, bomb, error +char Piece::tokens[] = {'.','*','F','s','9','8','7','6','5','4','3','2','1','B','?'}; +int Piece::maxUnits[] = {0,0,1,1,8,5,4,4,4,3,2,1,1,6,0}; + + + + +Piece::TextureManager Piece::textures; + + + + +Piece::TextureManager::~TextureManager() +{ + Array::Iterator i(*this); + while (i.Good()) + { + delete (*i); + ++i; + } +} + +Texture & Piece::TextureManager::operator[](const LUint & at) +{ + while (Array::Size() <= at) + { + char buffer[BUFSIZ]; + sprintf(buffer, "images/piece%lu.bmp", Array::Size()); + Array::Add(new Texture(buffer, false)); + + } + return *(Array::operator[](at)); +} + + +/** + * Gets the type of a piece, based off a character token + * @param fromToken - character identifying the piece + * @returns The type of the piece + */ +Piece::Type Piece::GetType(char fromToken) +{ + for (int ii=0; ii <= (int)(Piece::BOMB); ++ii) + { + if (tokens[ii] == fromToken) + { + return Type(Piece::NOTHING + ii); + } + } + return Piece::BOULDER; +} + +/** + * Construct a new, empty board + * @param newWidth - the width of the board + * @param newHeight - the height of the board + */ +Board::Board(int newWidth, int newHeight) : winner(Piece::NONE), width(newWidth), height(newHeight), board(NULL), pieces() +{ + board = new Piece**[width]; + for (int x=0; x < width; ++x) + { + board[x] = new Piece*[height]; + for (int y=0; y < height; ++y) + board[x][y] = NULL; + } +} + +/** + * Cleanup a board + */ +Board::~Board() +{ + for (int x=0; x < width; ++x) + { + for (int y=0; y < height; ++y) + delete board[x][y]; + delete [] board[x]; + } +} + +/** + * Print textual representation of the board to a stream + * @param stream - the stream to print information to + * @param reveal - Pieces matching this colour will have their identify revealed, other pieces will be shown as '#' + */ +void Board::Print(FILE * stream, const Piece::Colour & reveal) +{ + for (int y=0; y < height; ++y) + { + for (int x=0; x < width; ++x) + { + Piece * piece = board[x][y]; + if (piece == NULL) + { + fprintf(stream, "."); + } + else if (piece->colour != Piece::NONE && (piece->colour == reveal || reveal == Piece::BOTH)) + { + + fprintf(stream, "%c", Piece::tokens[piece->type]); + + + } + else + { + switch (piece->colour) + { + case Piece::RED: + case Piece::BLUE: + fprintf(stream, "#"); + break; + case Piece::NONE: + fprintf(stream, "+"); + break; + case Piece::BOTH: + fprintf(stream, "$"); + break; + } + } + } + fprintf(stream, "\n"); + } + +} + +/** + * Print textual representation of the board to a stream + * @param stream - the stream to print information to + * @param reveal - Pieces matching this colour will have their identify revealed, other pieces will be shown as '#' + */ +void Board::PrintPretty(FILE * stream, const Piece::Colour & reveal) +{ + for (int y=0; y < height; ++y) + { + for (int x=0; x < width; ++x) + { + Piece * piece = board[x][y]; + if (piece == NULL) + { + fprintf(stream, "."); + } + else if (piece->colour != Piece::NONE && (piece->colour == reveal || reveal == Piece::BOTH)) + { + switch (piece->colour) + { + case Piece::RED: + fprintf(stream, "%c[%d;%d;%dm",0x1B,1,31,40); + break; + case Piece::BLUE: + fprintf(stream, "%c[%d;%d;%dm",0x1B,1,34,40); + break; + default: + break; + } + fprintf(stream, "%c", Piece::tokens[piece->type]); + + } + else + { + switch (piece->colour) + { + case Piece::RED: + fprintf(stream, "%c[%d;%d;%dm",0x1B,1,31,41); + + break; + case Piece::BLUE: + fprintf(stream, "%c[%d;%d;%dm",0x1B,1,34,44); + break; + case Piece::NONE: + fprintf(stream, "%c[%d;%d;%dm",0x1B,1,37,47); + break; + case Piece::BOTH: + //Should never see this + fprintf(stream, "%c[%d;%d;%dm",0x1B,1,33,43); + break; + + } + fprintf(stream, "#"); + + } + fprintf(stream, "%c[%d;%d;%dm",0x1B,0,7,0); + } + fprintf(stream, "\n"); + } + +} + + + +/** + * Draw the board state to graphics + * @param reveal - Pieces matching this colour will be revealed. All others will be shown as blank coloured squares. + */ +void Board::Draw(const Piece::Colour & reveal, bool showRevealed) +{ + if (!Graphics::Initialised()) + { + fprintf(stderr, "ERROR - Board::Draw called whilst graphics disabled!!!\n"); + exit(EXIT_FAILURE); + + } + + Graphics::ClearScreen(); + + for (int y=0; y < height; ++y) + { + for (int x=0; x < width; ++x) + { + Piece * piece = board[x][y]; + if (piece == NULL) + { + //Don't display anything + + } + else if ((piece->colour != Piece::NONE && (piece->colour == reveal || reveal == Piece::BOTH)) + || (piece->beenRevealed && showRevealed)) + { + //Display the piece + Piece::textures[(int)(piece->type)].DrawColour(x*32,y*32,0,1, Piece::GetGraphicsColour(piece->colour)); + + } + else + { + switch (piece->colour) + { + case Piece::RED: + Piece::textures[(int)(Piece::NOTHING)].DrawColour(x*32,y*32,0,1, Piece::GetGraphicsColour(piece->colour)); + break; + case Piece::BLUE: + Piece::textures[(int)(Piece::NOTHING)].DrawColour(x*32,y*32,0,1, Piece::GetGraphicsColour(piece->colour)); + break; + case Piece::NONE: + Piece::textures[(int)(Piece::BOULDER)].DrawColour(x*32,y*32,0,1, Piece::GetGraphicsColour(piece->colour)); + break; + case Piece::BOTH: + Piece::textures[(int)(Piece::BOULDER)].DrawColour(x*32,y*32,0,1, Piece::GetGraphicsColour(piece->colour)); + break; + } + } + } + + } + Graphics::UpdateScreen(); + +} + +/** + * Adds a piece to the board + * @param x - x-coord to place the piece at, starting at zero, must be less than board width + * @param y - y-coord to place the piece at, starting at zero, must be less than board height + * @param newType - the Type of the piece + * @param newColour - the Colour of the piece + * @returns true if and only if the piece could be successfully added. + */ +bool Board::AddPiece(int x, int y, const Piece::Type & newType, const Piece::Colour & newColour) +{ + if (board == NULL || x < 0 || y < 0 || x >= width || y >= width || board[x][y] != NULL) + return false; + + Piece * piece = new Piece(newType, newColour); + board[x][y] = piece; + + pieces.push_back(piece); + return true; +} + +/** + * Gets a pointer to a piece at a board location + * UNUSED + * @param x - x-coord of the piece + * @param y - y-coord of the piece + * @returns pointer to the piece, or NULL if the board location was empty + * @throws error if board is null or coords are invalid + */ +Piece * Board::GetPiece(int x, int y) +{ + assert(board != NULL); + assert(x >= 0 && x < width && y >= 0 && y < height); + return board[x][y]; +} + +/** + * Moves a piece at a specified position in the specified direction, handles combat if necessary + * @param x - x-coord of the piece + * @param y - y-coord of the piece + * @param direction - Direction in which to move (UP, DOWN, LEFT or RIGHT) + * @param colour - Colour which the piece must match for the move to be valid + * @returns A MovementResult which indicates the result of the move - OK is good, VICTORY means that a flag was captured, anything else is an error + */ +MovementResult Board::MovePiece(int x, int y, const Direction & direction, int multiplier,const Piece::Colour & colour) +{ + if (board == NULL) + { + return MovementResult(MovementResult::NO_BOARD); + } + if (!(x >= 0 && x < width && y >= 0 && y < height)) + { + return MovementResult(MovementResult::INVALID_POSITION); + } + Piece * target = board[x][y]; + if (target == NULL) + { + return MovementResult(MovementResult::NO_SELECTION); + } + if (!(colour == Piece::NONE || target->colour == colour)) + { + return MovementResult(MovementResult::NOT_YOUR_UNIT); + } + if (target->type == Piece::FLAG || target->type == Piece::BOMB || target->type == Piece::BOULDER) + { + return MovementResult(MovementResult::IMMOBILE_UNIT); + } + if (multiplier > 1 && target->type != Piece::SCOUT) + { + return MovementResult(MovementResult::INVALID_DIRECTION); //Can only move a scout multiple times. + } + int x2 = x; int y2 = y; + + for (int ii=0; ii < multiplier; ++ii) + { + switch (direction) + { + case UP: + --y2; + break; + case DOWN: + ++y2; + break; + case LEFT: + --x2; + break; + case RIGHT: + ++x2; + break; + } + if (!(x2 >= 0 && x2 < width && y2 >= 0 && y2 < height)) + { + return MovementResult(MovementResult::INVALID_DIRECTION); + } + if (ii < multiplier-1 && board[x2][y2] != NULL) + { + return MovementResult(MovementResult::POSITION_FULL); + } + } + Piece * defender = board[x2][y2]; + if (defender == NULL) + { + board[x][y] = NULL; + board[x2][y2] = target; + } + else if (defender->colour != target->colour) + { + defender->beenRevealed = true; + target->beenRevealed = true; + + Piece::Type defenderType = defender->type; + Piece::Type attackerType = target->type; + + if (defender->colour == Piece::NONE) + { + return MovementResult(MovementResult::POSITION_FULL); + } + if (defender->type == Piece::FLAG) + { + winner = target->colour; + return MovementResult(MovementResult::VICTORY); + } + else if (defender->type == Piece::BOMB) + { + if (target->type == Piece::MINER) + { + RemovePiece(defender); + delete defender; + board[x][y] = NULL; + board[x2][y2] = target; + return MovementResult(MovementResult::KILLS, attackerType, defenderType); + } + else + { + RemovePiece(defender); + RemovePiece(target); + delete defender; + delete target; + board[x][y] = NULL; + board[x2][y2] = NULL; + return MovementResult(MovementResult::BOTH_DIE, attackerType, defenderType); + } + } + else if (defender->type == Piece::MARSHAL && target->type == Piece::SPY) + { + RemovePiece(defender); + delete defender; + board[x][y] = NULL; + board[x2][y2] = target; + return MovementResult(MovementResult::KILLS, attackerType, defenderType); + } + else if (target->operator > (*defender)) + { + RemovePiece(defender); + delete defender; + board[x][y] = NULL; + board[x2][y2] = target; + return MovementResult(MovementResult::KILLS, attackerType, defenderType); + } + else if (target->operator==(*defender) && rand() % 2 == 0) + { + RemovePiece(defender); + delete defender; + board[x][y] = NULL; + board[x2][y2] = target; + return MovementResult(MovementResult::KILLS, attackerType, defenderType); + } + else + { + RemovePiece(target); + delete target; + board[x][y] = NULL; + return MovementResult(MovementResult::DIES, attackerType, defenderType); + } + } + else + { + return MovementResult(MovementResult::POSITION_FULL); + } + return MovementResult(MovementResult::OK); +} + +/** + * Removes a piece from the board + * @param piece The piece to remove + * @returns true iff the piece actually existed + */ +bool Board::RemovePiece(Piece * piece) +{ + bool result = false; + for (int x = 0; x < width; ++x) + { + for (int y = 0; y < height; ++y) + { + if (board[x][y] == piece) + { + result = true; + board[x][y] = NULL; + } + } + } + + vector::iterator i = pieces.begin(); + while (i != pieces.end()) + { + if ((*i) == piece) + { + i = pieces.erase(i); + result = true; + continue; + } + ++i; + } + return result; +} + +/** + * Returns the total value of pieces belonging to colour + * @param colour the colour + * @returns the total value of pieces belonging to colour. + * (Redundant repetition <3) + */ +int Board::TotalPieceValue(const Piece::Colour & colour) const +{ + int result = 0; + for (vector::const_iterator i = pieces.begin(); i != pieces.end(); ++i) + { + if ((*i)->colour == colour || colour == Piece::BOTH) + { + result += (*i)->PieceValue(); + } + } + return result; +} + +/** + * Returns the total number of mobile pieces belonging to colour + * @param colour the colour + * @returns the total value of mobile pieces belonging to colour. + * (Redundant repetition <3) + */ +int Board::MobilePieces(const Piece::Colour & colour) const +{ + int result = 0; + for (vector::const_iterator i = pieces.begin(); i != pieces.end(); ++i) + { + if ((*i)->colour == colour || colour == Piece::BOTH) + { + if ((*i)->type <= Piece::MARSHAL && (*i)->type >= Piece::SPY) + result++; + } + } + return result; +} + + diff --git a/progcomp/judge/manager/stratego.h b/progcomp/judge/manager/stratego.h new file mode 100644 index 0000000..fe73aa1 --- /dev/null +++ b/progcomp/judge/manager/stratego.h @@ -0,0 +1,156 @@ +#ifndef STRATEGO_H +#define STRATEGO_H + +#include +#include + +#include + + + #include "graphics.h" + #include "array.h" + +#include + +/** + * Contains classes for a game of Stratego + */ + + +/** + * Class for a game piece + */ +class Piece +{ + public: + typedef enum {ERROR=14,BOMB=13,MARSHAL=12, GENERAL=11, COLONEL=10, MAJOR=9, CAPTAIN=8, LIEUTENANT=7, SERGEANT=6, MINER=5, SCOUT=4, SPY=3, FLAG=2,BOULDER=1, NOTHING=0} Type; //Type basically defines how strong the piece is + + + + typedef enum {RED=0, BLUE=1, NONE=2, BOTH=3} Colour; //Used for the allegiance of the pieces - terrain counts as NONE. + + Piece(const Type & newType, const Colour & newColour) : type(newType), colour(newColour), beenRevealed(false) {} + virtual ~Piece() {} + + + //Operators compare the strength of two pieces + bool operator==(const Piece & equ) const {return type == equ.type;} + bool operator<(const Piece & equ) const {return type < equ.type;} + bool operator>(const Piece & equ) const {return type > equ.type;} + + bool operator!=(const Piece & equ) const {return !operator==(equ);} + bool operator<=(const Piece & equ) const {return (operator<(equ) || operator==(equ));} + bool operator>=(const Piece & equ) const {return (operator>(equ) || operator==(equ));} + + //Contains the characters used to identify piece types when the board is printed to a stream + static char tokens[]; + static int maxUnits[]; + + static Type GetType(char fromToken); + + int PieceValue() const {if (type == BOMB || type == FLAG) {return 0;} return (int)(type) - (int)(SPY) + 1;} + + //Attributes of the piece + const Type type; + const Colour colour; + + bool beenRevealed; + + public: + + class TextureManager : public Graphics::TextureManager, private Array + { + public: + TextureManager() : Graphics::TextureManager(), Array() {} + virtual ~TextureManager(); + + virtual Texture & operator[](const LUint & at); + }; + static TextureManager textures; + + static Graphics::Colour GetGraphicsColour(const Piece::Colour & colour) + { + switch (colour) + { + case RED: + return Graphics::Colour(1,0,0); + break; + case BLUE: + return Graphics::Colour(0,0,1); + break; + case NONE: + return Graphics::Colour(0.5,0.5,0.5); + break; + case BOTH: + return Graphics::Colour(1,0,1); + break; + } + } + + + + + +}; + +#include "movementresult.h" + +/** + * A Stratego board + */ +class Board +{ + public: + Board(int newWidth, int newHeight); //Constructor + + virtual ~Board(); //Destructor + + void Print(FILE * stream, const Piece::Colour & reveal=Piece::BOTH); //Print board + void PrintPretty(FILE * stream, const Piece::Colour & reveal=Piece::BOTH); //Print board using colour + + void Draw(const Piece::Colour & reveal=Piece::BOTH, bool showRevealed = true); //Draw board + + + bool AddPiece(int x, int y, const Piece::Type & newType, const Piece::Colour & newColour); //Add piece to board + + + Piece * GetPiece(int x, int y); //Retrieve piece from a location on the board + + + typedef enum {UP, DOWN, LEFT, RIGHT} Direction; + + + + static bool LegalResult(const MovementResult & result) + { + return (result == MovementResult::OK || result == MovementResult::DIES || result == MovementResult::KILLS || result == MovementResult::BOTH_DIE || result == MovementResult::VICTORY || result == MovementResult::DRAW || result == MovementResult::DRAW_DEFAULT || result == MovementResult::SURRENDER); + } + + static bool HaltResult(const MovementResult & result) + { + return (result == MovementResult::VICTORY || result == MovementResult::DRAW || result == MovementResult::DRAW_DEFAULT || result == MovementResult::SURRENDER || !LegalResult(result)); + } + + MovementResult MovePiece(int x, int y, const Direction & direction, int multiplier=1,const Piece::Colour & colour=Piece::NONE); //Move piece from position in direction + + + Piece::Colour winner; + + int Width() const {return width;} + int Height() const {return height;} + + int MobilePieces(const Piece::Colour & colour) const; + int TotalPieceValue(const Piece::Colour & colour) const; + bool RemovePiece(Piece * piece); + private: + int width; + int height; + Piece ** * board; + std::vector pieces; +}; + +#endif //STRATEGO_H + +//EOF + + diff --git a/progcomp/judge/manager/thread_util.cpp b/progcomp/judge/manager/thread_util.cpp new file mode 100644 index 0000000..2d12438 --- /dev/null +++ b/progcomp/judge/manager/thread_util.cpp @@ -0,0 +1,47 @@ +#include "thread_util.h" + +#include +#include + +using namespace std; + +pthread_mutex_t GetterThread::mutex = PTHREAD_MUTEX_INITIALIZER; + +void * GetterThread::GetMessage(void * p) +{ + + GetterThread * getter = (GetterThread*)(p); + + stringstream inputStream; + + char s = fgetc(getter->stream); + while (s != '\n' && s != EOF) + { + + inputStream << s; + s = fgetc(getter->stream); + } + if (s == EOF) + { + getter->buffer = ""; + getter->buffer += s; + return NULL; + } + + pthread_mutex_lock(&mutex); + getter->buffer = inputStream.str(); + pthread_mutex_unlock(&mutex); + + getter->finished = true; + + return NULL; +} + +void * TimerThread::Timeout(void * p) +{ + TimerThread * timer = (TimerThread*)(p); + usleep(timer->count); + timer->finished = true; + return NULL; +} + diff --git a/progcomp/judge/manager/thread_util.h b/progcomp/judge/manager/thread_util.h new file mode 100644 index 0000000..95a16e0 --- /dev/null +++ b/progcomp/judge/manager/thread_util.h @@ -0,0 +1,115 @@ +#ifndef THREAD_UTIL_H +#define THREAD_UTIL_H + +#include +#include + +#include +#include +#include //Needed for threading +#include //Needed for killing the threads (why not in pthread.h?) + +#include + + +class Thread +{ + public: + Thread() : finished(false), thread(0), started(false) + { + + } + + virtual void Start() = 0; + protected: + void Start(void* (ThreadFunction)(void*)) + { + assert(!started); + started = true; + pthread_create(&(thread), NULL, ThreadFunction, (void*)(this)); + pthread_setcancelstate(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + } + + public: + void Stop() + { + assert(started); + if (!finished) + pthread_cancel(thread); + + pthread_join(thread, NULL); + started = false; + } + + virtual ~Thread() + { + if (started) + Stop(); + } + + bool Finished() const {return finished;} + protected: + + bool finished; + + private: + pthread_t thread; + protected: + bool started; +}; + +class GetterThread : public Thread +{ + public: + GetterThread(FILE * newStream, std::string & newBuffer) : Thread(), stream(newStream), buffer(newBuffer) + { + + } + + virtual ~GetterThread() + { + + } + + virtual void Start() {assert(&buffer != NULL); Thread::Start(GetMessage);} + + private: + FILE * stream; + public: + std::string & buffer; + + pthread_t thread; + static pthread_mutex_t mutex; + static void * GetMessage(void * p); + +}; + + +class TimerThread : public Thread +{ + public: + TimerThread(int newCount) : Thread(), count(newCount) + { + + } + + virtual ~TimerThread() + { + + } + + virtual void Start() {Thread::Start(Timeout);} + + private: + int count; + + static void * Timeout(void * p); + +}; + +#endif //THREAD_UTIL_H + +//EOF + + + diff --git a/progcomp/judge/simulator/Makefile b/progcomp/judge/simulator/Makefile new file mode 100644 index 0000000..1d71ef5 --- /dev/null +++ b/progcomp/judge/simulator/Makefile @@ -0,0 +1,15 @@ +#Makefile for simulations +# Not used for building simulate.py +# Used for building/removing results + +BASEDIR = /home/sam/Documents/progcomp2012/ +RESULTSDIR = /home/sam/Documents/progcomp2012/results +LOGDIR = /home/sam/Documents/progcomp2012/log +AGENTSDIR = /home/sam/Documents/progcomp2012/samples +MANAGER = /home/sam/Documents/progcomp2012/manager/stratego + + + +clean: + rm -r -f $(RESULTSDIR) + rm -r -f $(LOGDIR) diff --git a/progcomp/judge/simulator/simulate.py b/progcomp/judge/simulator/simulate.py new file mode 100755 index 0000000..32f2b42 --- /dev/null +++ b/progcomp/judge/simulator/simulate.py @@ -0,0 +1,189 @@ +#!/usr/bin/python -u + +''' + simulate.py - simulation script for the 2012 UCC Programming Competition + NOTE: This is not the manager program for a stratego game + It merely calls the manager program as appropriate, and records results + Plays exactly ONE round, but does not overwrite previously played rounds + eg: run once to generate round1.results, twice to generate round2.results etc + Also generates total.scores based on results from every round. + + + author Sam Moore (matches) [SZM] + website http://matches.ucc.asn.au/stratego + email progcomp@ucc.asn.au or matches@ucc.asn.au + git git.ucc.asn.au/progcomp2012.git +''' + +import os +import sys + +baseDirectory = "/home/sam/Documents/progcomp2012/" +resultsDirectory = baseDirectory+"results/" #Where results will go (results are in the form of text files of agent names and scores) +agentsDirectory = baseDirectory+"samples/" #Where agents are found (each agent has its own directory) +logDirectory = baseDirectory+"log/" #Where log files go +nGames = 10 #Number of games played by each agent against each opponent. Half will be played as RED, half as BLUE +managerPath = baseDirectory+"manager/stratego" #Path to the manager program + + +scores = {"VICTORY":(3,1), "DEFEAT":(1,3), "SURRENDER":(0,3), "DRAW":(2,2), "DRAW_DEFAULT":(1,1), "ILLEGAL":(-1,2), "DEFAULT":(2,-1), "BOTH_ILLEGAL":(-1,-1), "INTERNAL_ERROR":(0,0)} #Score dictionary + +verbose = True + + +#Make necessary directories +if os.path.exists(resultsDirectory) == False: + os.mkdir(resultsDirectory) #Make the results directory if it didn't exist +#Identify the round number by reading the results directory +roundNumber = len(os.listdir(resultsDirectory)) + 1 +if roundNumber > 1: + roundNumber -= 1 + +if os.path.exists(logDirectory) == False: + os.mkdir(logDirectory) #Make the log directory if it didn't exist + + + +if os.path.exists(logDirectory + "round"+str(roundNumber)) == False: + os.mkdir(logDirectory + "round"+str(roundNumber)) #Check there is a directory for this round's logs + +print "Simulating ROUND " +str(roundNumber) +print "Identifying possible agents in \""+agentsDirectory+"\"" + +#Get all agent names from agentsDirectory +agentNames = os.listdir(agentsDirectory) +agents = [] +for name in agentNames: + #sys.stdout.write("\nLooking at Agent: \""+ str(name)+"\"... ") + if verbose: + sys.stdout.write("Scan \""+name+"\"... ") + if os.path.isdir(agentsDirectory+name) == False: #Remove non-directories + if verbose: + sys.stdout.write(" Invalid! (Not a directory)\n") + continue + + if os.path.exists(agentsDirectory+name+"/info") == False: #Try and find the special "info" file in each directory; ignore if it doesn't exist + if verbose: + sys.stdout.write(" Invalid! (No \"info\" file found)\n") + continue + if verbose: + sys.stdout.write(" Valid!") + #sys.stdout.write("OK") + #Convert the array of names to an array of triples + #agents[0] - The name of the agent (its directory) + #agents[1] - The path to the program for the agent (typically agentsDirectory/agent/agent). Read from agentsDirectory/agent/info file + #agents[2] - The score the agent achieved in _this_ round. Begins at zero + agentExecutable = agentsDirectory+name+"/"+(open(agentsDirectory+name+"/info").readline().strip()) + agents.append([name, agentExecutable, 0]) + if verbose: + sys.stdout.write(" (Run program \""+agentExecutable+"\")\n") + +if len(agents) == 0: + print "Couldn't find any agents! Check paths (Edit this script) or generate \"info\" files for agents." + sys.exit(0) +if verbose: + print "Total: " + str(len(agents)) + " valid agents found (From "+str(len(agentNames))+" possibilities)" + print "" + print "Commencing ROUND " + str(roundNumber) + " combat! This could take a while... ("+str(nGames)+" games per pairing * " + str(len(agents) * len(agents)-1) + " pairings = " + str((len(agents) * len(agents)-1) * nGames) + " games)" + + +normalGames = 0 +draws = 0 +aiErrors = 0 +managerErrors = 0 +#This double for loop simulates a round robin, with each agent getting the chance to play as both red and blue against every other agent. +for red in agents: #for each agent playing as red, + for blue in agents: #against each other agent, playing as blue + if red == blue: + continue #Exclude battles against self + for i in range(1, nGames/2 + 1): + #Play a game and read the result. Note the game is logged to a file based on the agent's names + if verbose: + sys.stdout.write("Agents: \""+red[0]+"\" and \""+blue[0]+"\" playing game " + str(i) + "/"+str(nGames/2) + "... ") + logFile = logDirectory + "round"+str(roundNumber) + "/"+red[0]+"_vs_"+blue[0]+"_"+str(i) + outline = os.popen(managerPath + " -o " + logFile + " " + red[1] + " " + blue[1], "r").read() + results = outline.split(' ') + + if len(results) != 6: + if verbose: + sys.stdout.write("Garbage output! " + outline) + else: + if results[1] == "RED": + red[2] += scores[results[2]][0] + blue[2] += scores[results[2]][1] + elif results[1] == "BLUE": + red[2] += scores[results[2]][1] + blue[2] += scores[results[2]][0] + elif results[1] == "BOTH": + red[2] += scores[results[2]][0] + blue[2] += scores[results[2]][1] + red[2] += scores[results[2]][1] + blue[2] += scores[results[2]][0] + + if verbose: + sys.stdout.write(" " + outline) + + + + + + +if verbose: + print "Completed combat. Total of " + str(normalGames + draws + aiErrors + managerErrors) + " games played. " +if managerErrors != 0: + print "WARNING: Recieved "+str(managerErrors)+" garbage outputs. Check the manager program." + +if verbose: + print "" +#We should now have complete score values. + +if verbose: + sys.stdout.write("Creating results files for ROUND " + str(roundNumber) + "... ") + +agents.sort(key = lambda e : e[2], reverse=True) #Sort the agents based on score + +resultsFile = open(resultsDirectory+"round"+str(roundNumber)+".results", "w") #Create a file to store all the scores for this round +for agent in agents: + resultsFile.write(agent[0] + " " + str(agent[2]) +"\n") #Write the agent names and scores into the file, in descending order + +if verbose: + sys.stdout.write(" Complete!\n") + sys.stdout.write("Updating total scores... "); + +#Now update the total scores +if os.path.exists(resultsDirectory+"total.scores"): + if verbose: + sys.stdout.write(" Reading from \""+resultsDirectory+"total.scores\" to update scores... ") + totalFile = open(resultsDirectory+"total.scores", "r") #Try to open the total.scores file + for line in totalFile: #For all entries, + data = line.split(' ') + for agent in agents: + if agent[0] == data[0]: + agent.append(agent[2]) #Store the score achieved this round at the end of the list + agent[2] += int(data[1]) #Simply increment the current score by the recorded total score of the matching file entry + break + totalFile.close() #Close the file, so we can delete it + os.remove(resultsDirectory+"total.scores") #Delete the file + #Sort the agents again + agents.sort(key = lambda e : e[2], reverse=True) + +else: + if verbose: + sys.stdout.write(" First round - creating \""+resultsDirectory+"total.scores\"... ") +if verbose: + sys.stdout.write(" Complete!\n") + print "Finished writing results for ROUND " + str(roundNumber) + print "" + + +print "RESULTS FOR ROUND " + str(roundNumber) +print "Agent: [name, path, total_score, recent_score]" + +totalFile = open(resultsDirectory+"total.scores", "w") #Recreate the file +for agent in agents: + totalFile.write(agent[0] + " " + str(agent[2]) +"\n") #Write the total scores in descending order + print "Agent: " + str(agent) + + +#I just want to say the even though I still think python is evil, it is much better than bash. Using bash makes me cry. + diff --git a/progcomp/web/index.html b/progcomp/web/index.html new file mode 100644 index 0000000..d181a14 --- /dev/null +++ b/progcomp/web/index.html @@ -0,0 +1,73 @@ + + + Stratego Based Programming Competition + + + + +

Quick Details

+

git

+

The git repository is listed on The UCC git page as "progcomp2012.git"

+

Direct Link Here

+ +

Mailing List

+

We will use the same mailing list as last year (progcomp).

+ +

irc channel

+

There is a #progcomp irc channel on the ucc irc server (irc.ucc.asn.au) where you can ask questions or help with setting things up.

+ +

Programming Competition VM

+

I am in the process of learning how to set up a VM for this competition. Please be patient.

+ +

Stratego

+

This site explains what Stratego is.

+ +

Short Version: It is a board game in which all pieces have a value that is, initially, unknown to the opponent player. The objective is to destroy all enemy pieces, or capture the enemy "Flag". Pieces with higher values destroy pieces with lower values. There are several special pieces/rules.

+ + +

Programming Competition

+

Create an AI to play Stratego.

+

Programs are written independently and interface through stdin/stdout with a manager program, which queries them on setup and moves.

+ +

The Manager Program

+

The manager program provides the protocol for two seperate AI to play a game of stratego. It has the imaginative name of 'stratego', but I will probably refer to it as 'the manager program' or 'stratego' with absolutely no consistency.

+

It also aims to assist with AI design by providing options for graphical or terminal output and saving/reading games from files

+

+

Human players are also supported, although the interface is minimal, as this feature is meant for testing.

+

If you just want to play a game, without having to write your own AI, try Probe

+ +

Screenshot

+Graphical output of 'stratego' manager program. + +

Protocol

+

For the sake of simplicity and keeping things in one place, the protocol is now entirely described in the manual page of the manager program. All updates to the protocol will be reflected in that file.

+ + +

Warning: The accuracy of the above file depends on how recently I pulled it from git. To ensure you get the latest version, find it under "manager/manual.txt" in the git repository

+ +

Long Term Scoring

+

WARNING: Work in progress

+

It is currently planned to store all the AIs on a virtual machine, and periodically run a script to play a round robin

+

The scores for each round and the total scores will be recorded from the start to the end of the competition.

+

The competition will run over a period of weeks (depending on general enthusiasm...), and competitors will be able to alter their programs except during the periods in which the script is running.

+

The following categories will be used when determining the final winners:

+

    +
  1. Total score
  2. +
  3. Number of rounds won
  4. +

+ +

Sample AI Programs

+

WARNING: Work in progress

+

The following sample programs are currently available (and in a working state - refer to the git repository):

+ + + + + +
Name Language Moves Considers...
basic_python Python Randomised
basic_cpp C++ Randomised
asmodeus Python Scored Path finding, known combat results, piece values
+

It is planned to implement the equivelants of these samples in C++ and Python at least, possibly other languages later.

+ +

Last webpage update: 7/12/11

+ + + diff --git a/progcomp/web/screenshot.png b/progcomp/web/screenshot.png new file mode 100644 index 0000000..276d892 Binary files /dev/null and b/progcomp/web/screenshot.png differ