From: Sam Moore Date: Thu, 22 Dec 2011 06:06:03 +0000 (+0800) Subject: Changed directory structure (again) X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=1a03b2543b67f0551e62babec4cd119f1e0e4640;p=progcomp2012.git Changed directory structure (again) I got fed up of typing progcomp2012/progcomp Obsessive compulsive urges rising... --- diff --git a/agents/asmodeus/asmodeus.py b/agents/asmodeus/asmodeus.py new file mode 100755 index 0000000..0371dcd --- /dev/null +++ b/agents/asmodeus/asmodeus.py @@ -0,0 +1,85 @@ +#!/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] + +if __name__ == "__main__": + asmodeus = Asmodeus() + if asmodeus.Setup(): + while asmodeus.MoveCycle(): + pass + diff --git a/agents/asmodeus/basic_python.py b/agents/asmodeus/basic_python.py new file mode 120000 index 0000000..3d6b342 --- /dev/null +++ b/agents/asmodeus/basic_python.py @@ -0,0 +1 @@ +../basic_python/basic_python.py \ No newline at end of file diff --git a/agents/asmodeus/info b/agents/asmodeus/info new file mode 100644 index 0000000..62e9c51 --- /dev/null +++ b/agents/asmodeus/info @@ -0,0 +1,4 @@ +asmodeus.py +Sam Moore +python +Sample AI - Improves basic_python Scores moves based on paths towards enemy units and known combat outcomes, chooses highest scoring move. diff --git a/agents/asmodeus/path.py b/agents/asmodeus/path.py new file mode 100644 index 0000000..3f08979 --- /dev/null +++ b/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/agents/basic_cpp/Makefile b/agents/basic_cpp/Makefile new file mode 100644 index 0000000..0a249b3 --- /dev/null +++ b/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/agents/basic_cpp/basic_cpp.cpp b/agents/basic_cpp/basic_cpp.cpp new file mode 100644 index 0000000..4312489 --- /dev/null +++ b/agents/basic_cpp/basic_cpp.cpp @@ -0,0 +1,576 @@ +/** + * "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]); + + //We might want to actually check for the multiplier in the sample agents! 20/12/11 + unsigned int outIndex = 3; + int multiplier = atoi(tokens[outIndex].c_str()); + if (multiplier == 0) + multiplier = 1; + else + outIndex += 1; + + + + string & outcome = tokens[outIndex]; + + + int x2 = x; int y2 = y; Helper::MoveInDirection(x2,y2,dir, multiplier); + + 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; + } + if (tokens.size() < outIndex+2) + return false; + + + board->Set(x2,y2, attacker); + board->Set(x,y,NULL); + attacker->x = x2; attacker->y = y2; + attacker->rank = Piece::GetRank(tokens[outIndex+1][0]); + ForgetUnit(defender); + } + else if (outcome == "DIES") + { + if (defender == NULL) + { + //cerr << "No defender!\n"; + return false; + } + if (tokens.size() < outIndex+3) + return false; + + + board->Set(x,y,NULL); + defender->rank = Piece::GetRank(tokens[outIndex+2][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/agents/basic_cpp/basic_cpp.h b/agents/basic_cpp/basic_cpp.h new file mode 100644 index 0000000..733d9a7 --- /dev/null +++ b/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/agents/basic_cpp/info b/agents/basic_cpp/info new file mode 100644 index 0000000..dd61e4a --- /dev/null +++ b/agents/basic_cpp/info @@ -0,0 +1,4 @@ +basic_cpp +Sam Moore +C++ +Sample AI - Provides classes that obey the manager program's protocol, and stores the state of the board and pieces, but only makes randomised moves. diff --git a/agents/basic_python/basic_python.py b/agents/basic_python/basic_python.py new file mode 100755 index 0000000..b384838 --- /dev/null +++ b/agents/basic_python/basic_python.py @@ -0,0 +1,348 @@ +#!/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 is_integer(s): + """ Using exceptions for this feels... wrong...""" + try: + int(s) + return True + except ValueError: + return False + +def move(x, y, direction, multiplier): + """ Moves point (x,y) in direction, returns a pair """ + if direction == "UP": + return (x,y-multiplier) + elif direction == "DOWN": + return (x,y+multiplier) + elif direction == "LEFT": + return (x-multiplier, y) + elif direction == "RIGHT": + return (x+multiplier, 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 + self.beenRevealed = False + self.positions = [(x, y)] + + 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 + + +def valuedRank(rank): + if ranks.count(rank) > 0: + return len(ranks) - 2 - ranks.index(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 = [] + + self.totalAllies = {'B':6,'1':1,'2':1,'3':2,'4':3,'5':4,'6':4,'7':4,'8':5,'9':8,'s':1,'F':1} + self.totalEnemies = {'B':6,'1':1,'2':1,'3':2,'4':3,'5':4,'6':4,'7':4,'8':5,'9':8,'s':1,'F':1} + self.hiddenEnemies = {'B':6,'1':1,'2':1,'3':2,'4':3,'5':4,'6':4,'7':4,'8':5,'9':8,'s':1,'F':1} + self.hiddenAllies = {'B':6,'1':1,'2':1,'3':2,'4':3,'5':4,'6':4,'7':4,'8':5,'9':8,'s':1,'F':1} + self.lastMoved = None + + + + 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],1) + 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() + + multiplier = 1 + outcome = result[3].strip() + outIndex = 3 + if is_integer(outcome): + multiplier = int(outcome) + outcome = result[4].strip() + outIndex = 4 + + p = move(x,y,direction, multiplier) + + #Determine attacking piece + attacker = self.board[x][y] + self.board[x][y] = None + + if attacker == None: + return False + + lastMoved = attacker + + defender = self.board[p[0]][p[1]] + + #Update attacker's position (Don't overwrite the board yet though) + + attacker.x = p[0] + attacker.y = p[1] + attacker.positions.insert(0, (attacker.x, attacker.y)) + + + #Determine ranks of pieces if supplied + if len(result) >= outIndex + 3: + if defender == None: + return False + attacker.rank = result[outIndex+1].strip() + if attacker.beenRevealed == False: + if attacker.colour == self.colour: + self.hiddenAllies[attacker.rank] -= 1 + elif attacker.colour == oppositeColour(self.colour): + self.hiddenEnemies[attacker.rank] -= 1 + attacker.beenRevealed = True + defender.rank = result[outIndex+2].strip() + if defender.beenRevealed == False: + if defender.colour == self.colour: + self.hiddenAllies[defender.rank] -= 1 + elif defender.colour == oppositeColour(self.colour): + self.hiddenEnemies[defender.rank] -= 1 + + defender.beenRevealed = True + + + + if outcome == "OK": + self.board[p[0]][p[1]] = attacker + + elif outcome == "KILLS": + self.board[p[0]][p[1]] = attacker + + if defender.colour == self.colour: + self.totalAllies[defender.rank] -= 1 + self.units.remove(defender) + elif defender.colour == oppositeColour(self.colour): + self.totalEnemies[defender.rank] -= 1 + self.enemyUnits.remove(defender) + + elif outcome == "DIES": + if attacker.colour == self.colour: + self.totalAllies[attacker.rank] -= 1 + self.units.remove(attacker) + elif attacker.colour == oppositeColour(self.colour): + self.totalEnemies[attacker.rank] -= 1 + self.enemyUnits.remove(attacker) + + elif outcome == "BOTHDIE": + self.board[p[0]][p[1]] = None + + if defender.colour == self.colour: + self.totalAllies[defender.rank] -= 1 + self.units.remove(defender) + elif defender.colour == oppositeColour(self.colour): + self.totalEnemies[defender.rank] -= 1 + self.enemyUnits.remove(defender) + + if attacker.colour == self.colour: + self.totalAllies[attacker.rank] -= 1 + self.units.remove(attacker) + elif attacker.colour == oppositeColour(self.colour): + self.totalEnemies[attacker.rank] -= 1 + self.enemyUnits.remove(attacker) + + 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") + +if __name__ == "__main__": + basicAI = BasicAI() + if basicAI.Setup(): + while basicAI.MoveCycle(): + pass + diff --git a/agents/basic_python/info b/agents/basic_python/info new file mode 100644 index 0000000..93fbaa2 --- /dev/null +++ b/agents/basic_python/info @@ -0,0 +1,4 @@ +basic_python.py +Sam Moore +python +Sample AI - Provides classes that obey the manager program's protocol, and stores the state of the board and pieces, but only makes randomised moves. diff --git a/agents/vixen/asmodeus.py b/agents/vixen/asmodeus.py new file mode 120000 index 0000000..1b1739c --- /dev/null +++ b/agents/vixen/asmodeus.py @@ -0,0 +1 @@ +../asmodeus/asmodeus.py \ No newline at end of file diff --git a/agents/vixen/basic_python.py b/agents/vixen/basic_python.py new file mode 120000 index 0000000..3d6b342 --- /dev/null +++ b/agents/vixen/basic_python.py @@ -0,0 +1 @@ +../basic_python/basic_python.py \ No newline at end of file diff --git a/agents/vixen/info b/agents/vixen/info new file mode 100644 index 0000000..56945f6 --- /dev/null +++ b/agents/vixen/info @@ -0,0 +1,4 @@ +vixen.py +Sam Moore +python +Sample AI - An improvement on asmodeus' score optimisation. Considers probabilities for unknown enemy units, and sums scores for paths with common first move. diff --git a/agents/vixen/path.py b/agents/vixen/path.py new file mode 120000 index 0000000..1d82284 --- /dev/null +++ b/agents/vixen/path.py @@ -0,0 +1 @@ +../asmodeus/path.py \ No newline at end of file diff --git a/agents/vixen/vixen.py b/agents/vixen/vixen.py new file mode 100755 index 0000000..3aa47cb --- /dev/null +++ b/agents/vixen/vixen.py @@ -0,0 +1,176 @@ +#!/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. + +''' + vixen.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 Vixen(BasicAI): + " Python based AI, improves upon Asmodeus by taking into account probabilities, and common paths " + def __init__(self): + #sys.stderr.write("Vixen initialised...\n") + BasicAI.__init__(self) + + + #self.bombScores = {'1' : -0.9 , '2' : -0.8 , '3' : -0.5 , '4' : 0.1, '5' : 0.1, '6' : 0.3, '7' : 0.7, '8' : 1 , '9' : 0.6, 's' : 0} + #self.bombScores = {'1' : -0.9 , '2' : -0.8 , '3' : -0.5 , '4' : -0.5, '5' : -0.4, '6' : -0.5, '7' : -0.2, '8' : 1.0 , '9' : -0.1, 's' : -0.2} + self.suicideScores = {'1' : -0.5 , '2' : -0.4 , '3' : -0.35, '4' : -0.25, '5' : -0.2, '6' : 0.0, '7' : 0.1, '8' : -1.0 , '9' : 0.0, 's' : -0.4} + self.killScores = {'1' : 1.0 , '2' : 0.9 , '3' : 0.9 , '4' : 0.8, '5' : 0.8, '6' : 0.8, '7' : 0.8, '8' : 0.9 , '9' : 0.7, 's' : 1.0} + self.riskScores = {'1' : 0.0, '2' : 0.1, '3' : 0.2, '4': 0.4, '5': 0.6, '6': 0.7, '7':0.8, '8': 0.0, '9' : 1.0, 's' : 0.1} + + + + + + def MakeMove(self): + #sys.stderr.write("Vixen MakingMove...\n") + " Over-rides the default BasicAI.MakeMove function " + + moveList = [] + for unit in self.units: + if unit.mobile() == False: + continue + + scores = {"LEFT":0, "RIGHT":0, "UP":0, "DOWN":0} + + + for target in self.enemyUnits: + if target == unit: + continue + path = PathFinder().pathFind((unit.x, unit.y), (target.x, target.y), self.board) + if path == False or len(path) == 0: + continue + #moveList.append({"unit":unit, "direction":path[0], "score":self.CalculateScore(unit, target, path)}) + scores[path[0]] += self.CalculateScore(unit, target, path) + + bestScore = sorted(scores.items(), key = lambda e : e[1], reverse=True)[0] + moveList.append({"unit":unit, "direction":bestScore[0], "score":bestScore[1]}) + + + if len(moveList) == 0: + print "NO_MOVE" + return True + + moveList.sort(key = lambda e : e["score"], reverse=True) + #sys.stderr.write("vixen - best move: " + str(moveList[0]["unit"].x) + " " + str(moveList[0]["unit"].y) + " " + moveList[0]["direction"] + " [ score = " + str(moveList[0]["score"]) + " ]\n") + if moveList[0]["score"] == 0: + print "NO_MOVE" + return True + + + print str(moveList[0]["unit"].x) + " " + str(moveList[0]["unit"].y) + " " + moveList[0]["direction"] + return True + + + def tailFactor(self, pathLength): + #if pathLength >= len(self.tailFactors) or pathLength <= 0: + # return 0.0 + #return self.tailFactors[pathLength] + #return 0.5 * (1.0 + pow(pathLength, 0.75)) + return 1.0 / pathLength + + + def CalculateScore(self, attacker, defender, path): + p = move(attacker.x, attacker.y, path[0], 1) + + + total = 0.0 + count = 0.0 + for rank in ranks: + prob = self.rankProbability(defender, rank) + if prob > 0.0: + #sys.stderr.write(" " + str(attacker.rank) + " vs. " + str(rank) + " [" + str(prob) + "] score " + str(self.combatScore(attacker.rank, rank, len(path))) + "\n") + total += prob * self.combatScore(attacker.rank, rank, len(path)) + count += 1 + + + #if count > 1: + # total = total / count + self.riskScore(attacker.rank) + + + total = total * self.tailFactor(len(path)) + #HACK - Prevent "oscillating" by decreasing the value of backtracks + if len(path) > 1 and len(attacker.positions) > 1 and attacker.positions[1][0] == p[0] and attacker.positions[1][1] == p[1]: + total = total / 100 + #sys.stderr.write("Total score for " + str(attacker) + " vs. " + str(defender) + " is " + str(total) + "\n") + return total + + def combatScore(self, attackerRank, defenderRank, pathLength): + if defenderRank == 'F': + return 1.0 + elif defenderRank == 'B': + return self.bombScore(attackerRank) + elif defenderRank == 's' and attackerRank == '1' and pathLength == 2: + return self.suicideScore(attackerRank) + elif defenderRank == '1' and attackerRank == 's' and pathLength != 2: + return self.killScore(attackerRank) + + if valuedRank(attackerRank) > valuedRank(defenderRank): + return self.killScore(defenderRank) + elif valuedRank(attackerRank) < valuedRank(defenderRank): + return self.suicideScore(attackerRank) + return self.killScore(defenderRank) + self.suicideScore(attackerRank) + + def killScore(self, defenderRank): + return self.killScores[defenderRank] + + def bombScore(self, attackerRank): + if attackerRank == '8': + return 1.0 + else: + return 0.0 + + def suicideScore(self, attackerRank): + return self.suicideScores[attackerRank] + + def riskScore(self, attackerRank): + return self.riskScores[attackerRank] + + def rankProbability(self, target, targetRank): + if targetRank == '+' or targetRank == '?': + return 0.0 + if target.rank == targetRank: + return 1.0 + elif target.rank != '?': + return 0.0 + + total = 0.0 + for rank in ranks: + if rank == '+' or rank == '?': + continue + elif rank == 'F' or rank == 'B': + if target.lastMoved < 0: + total += self.hiddenEnemies[rank] + else: + total += self.hiddenEnemies[rank] + + if total == 0.0: + return 0.0 + return float(float(self.hiddenEnemies[targetRank]) / float(total)) + + + + + + + +if __name__ == "__main__": + vixen = Vixen() + if vixen.Setup(): + while vixen.MoveCycle(): + pass + diff --git a/judge/manager/Makefile b/judge/manager/Makefile new file mode 100644 index 0000000..fe319df --- /dev/null +++ b/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/judge/manager/ai_controller.cpp b/judge/manager/ai_controller.cpp new file mode 100644 index 0000000..40ee3df --- /dev/null +++ b/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/judge/manager/ai_controller.h b/judge/manager/ai_controller.h new file mode 100644 index 0000000..7d62591 --- /dev/null +++ b/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/judge/manager/array.h b/judge/manager/array.h new file mode 100644 index 0000000..2a1d29f --- /dev/null +++ b/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/judge/manager/controller.cpp b/judge/manager/controller.cpp new file mode 100644 index 0000000..1b0a35d --- /dev/null +++ b/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/judge/manager/controller.h b/judge/manager/controller.h new file mode 100644 index 0000000..55c233d --- /dev/null +++ b/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/judge/manager/game.cpp b/judge/manager/game.cpp new file mode 100644 index 0000000..3d6dcc8 --- /dev/null +++ b/judge/manager/game.cpp @@ -0,0 +1,609 @@ +#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 (!red->HumanController()) + logMessage("Check that program \"%s\" exists and has executable permissions set.\n", redName); + } + if (!blue->Valid()) + { + logMessage("Controller for Player BLUE is invalid!\n"); + if (!blue->HumanController()) + logMessage("Check that program \"%s\" exists and has executable permissions set.\n", blueName); + } + 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()); + if (redSetup == MovementResult::OK) + { + 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"); + } + } + else + { + logMessage("INVALID!\n"); + } + + logMessage("%s BLUE SETUP\n", blue->name.c_str()); + if (blueSetup == MovementResult::OK) + { + for (int y=0; y < 4; ++y) + { + for (int x=0; x < theBoard.Width(); ++x) + { + if (theBoard.GetPiece(x, theBoard.Height()-4+y) != NULL) + logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, theBoard.Height()-4+y)->type)]); + else + logMessage("."); + } + logMessage("\n"); + } + } + else + { + logMessage("INVALID!\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() && blue->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() && red->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/judge/manager/game.h b/judge/manager/game.h new file mode 100644 index 0000000..1212b89 --- /dev/null +++ b/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/judge/manager/graphics.cpp b/judge/manager/graphics.cpp new file mode 100644 index 0000000..5b708df --- /dev/null +++ b/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/judge/manager/graphics.h b/judge/manager/graphics.h new file mode 100644 index 0000000..f81bdb3 --- /dev/null +++ b/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/judge/manager/human_controller.cpp b/judge/manager/human_controller.cpp new file mode 100644 index 0000000..bf43e8d --- /dev/null +++ b/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/judge/manager/human_controller.h b/judge/manager/human_controller.h new file mode 100644 index 0000000..b2069fc --- /dev/null +++ b/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/judge/manager/images/piece0.bmp b/judge/manager/images/piece0.bmp new file mode 100644 index 0000000..bd67766 Binary files /dev/null and b/judge/manager/images/piece0.bmp differ diff --git a/judge/manager/images/piece1.bmp b/judge/manager/images/piece1.bmp new file mode 100644 index 0000000..85fd5ba Binary files /dev/null and b/judge/manager/images/piece1.bmp differ diff --git a/judge/manager/images/piece10.bmp b/judge/manager/images/piece10.bmp new file mode 100644 index 0000000..cf77ed4 Binary files /dev/null and b/judge/manager/images/piece10.bmp differ diff --git a/judge/manager/images/piece11.bmp b/judge/manager/images/piece11.bmp new file mode 100644 index 0000000..b006d3d Binary files /dev/null and b/judge/manager/images/piece11.bmp differ diff --git a/judge/manager/images/piece12.bmp b/judge/manager/images/piece12.bmp new file mode 100644 index 0000000..8c67a48 Binary files /dev/null and b/judge/manager/images/piece12.bmp differ diff --git a/judge/manager/images/piece13.bmp b/judge/manager/images/piece13.bmp new file mode 100644 index 0000000..f822c24 Binary files /dev/null and b/judge/manager/images/piece13.bmp differ diff --git a/judge/manager/images/piece14.bmp b/judge/manager/images/piece14.bmp new file mode 100644 index 0000000..3145270 Binary files /dev/null and b/judge/manager/images/piece14.bmp differ diff --git a/judge/manager/images/piece2.bmp b/judge/manager/images/piece2.bmp new file mode 100644 index 0000000..0a11b11 Binary files /dev/null and b/judge/manager/images/piece2.bmp differ diff --git a/judge/manager/images/piece3.bmp b/judge/manager/images/piece3.bmp new file mode 100644 index 0000000..40ca3f8 Binary files /dev/null and b/judge/manager/images/piece3.bmp differ diff --git a/judge/manager/images/piece4.bmp b/judge/manager/images/piece4.bmp new file mode 100644 index 0000000..0a27a46 Binary files /dev/null and b/judge/manager/images/piece4.bmp differ diff --git a/judge/manager/images/piece5.bmp b/judge/manager/images/piece5.bmp new file mode 100644 index 0000000..051bd43 Binary files /dev/null and b/judge/manager/images/piece5.bmp differ diff --git a/judge/manager/images/piece6.bmp b/judge/manager/images/piece6.bmp new file mode 100644 index 0000000..5ca389b Binary files /dev/null and b/judge/manager/images/piece6.bmp differ diff --git a/judge/manager/images/piece7.bmp b/judge/manager/images/piece7.bmp new file mode 100644 index 0000000..aaf28a1 Binary files /dev/null and b/judge/manager/images/piece7.bmp differ diff --git a/judge/manager/images/piece8.bmp b/judge/manager/images/piece8.bmp new file mode 100644 index 0000000..b2ea5b7 Binary files /dev/null and b/judge/manager/images/piece8.bmp differ diff --git a/judge/manager/images/piece9.bmp b/judge/manager/images/piece9.bmp new file mode 100644 index 0000000..e37e251 Binary files /dev/null and b/judge/manager/images/piece9.bmp differ diff --git a/judge/manager/main.cpp b/judge/manager/main.cpp new file mode 100644 index 0000000..0fbd6ae --- /dev/null +++ b/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 << "BAD_SETUP "; + 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/judge/manager/manual.txt b/judge/manager/manual.txt new file mode 100644 index 0000000..239c6ac --- /dev/null +++ b/judge/manager/manual.txt @@ -0,0 +1,246 @@ +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. + +A WARNING ABOUT BUFFERING + The AI programs must unbuffer their stdin and stdout streams, otherwise it will be seen to be non-responsive. + If you C and you know a way to force the process started by exec() to have unbuffered stdin/stdout, please email the author. + + In C or C++, unbuffering is accomplished with the following lines, which should appear near the start of main() + setbuf(stdin, NULL); + setbuf(stdout, NULL); + In python, unbuffering is accomplished by passing the -u switch to the interpreter, ie: The first line of a script reads: + #!/usr/bin/python -u + + +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 are represented by the following characters: + + 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 + 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) attacks an enemy Bomb, that piece is destroyed. + F Flag NA 1 Immobile. If any piece attacks 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 #). + + Since 20/12 Bombs reflect the traditional rules; they are only destroyed by Miners. + In previous versions contact of an attacker other than a Miner with a Bomb destroyed the Bomb as well as the attacking piece. + + +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 + Occasionally the result is not printed at the end of the game. + So far this has only been observed to occur when RED wins the game by Flag capture. + + 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 + +THIS PAGE LAST UPDATED + 20/12/11 by Sam Moore + diff --git a/judge/manager/movementresult.h b/judge/manager/movementresult.h new file mode 100644 index 0000000..5a2aed6 --- /dev/null +++ b/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/judge/manager/program.cpp b/judge/manager/program.cpp new file mode 100644 index 0000000..588f714 --- /dev/null +++ b/judge/manager/program.cpp @@ -0,0 +1,189 @@ +#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 and is executable... + if (access(executablePath, X_OK) != 0) + { + 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 + + + if (access(executablePath, X_OK) == 0) //Check we STILL have permissions to start the file + 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/judge/manager/program.h b/judge/manager/program.h new file mode 100644 index 0000000..01e00bc --- /dev/null +++ b/judge/manager/program.h @@ -0,0 +1,41 @@ +#ifndef PROGRAM_H +#define PROGRAM_H + +#include "thread_util.h" + +#include +#include //Needed to check permissions + +/** + * 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/judge/manager/stratego.cpp b/judge/manager/stratego.cpp new file mode 100644 index 0000000..410b70a --- /dev/null +++ b/judge/manager/stratego.cpp @@ -0,0 +1,525 @@ + + +#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. If Piece::BOTH, all pieces will be revealed + * @param showRevealed - If true, then all pieces that have taken part in combat will be revealed, regardless of colour. + * If false, only pieces matching the colour reveal will be revealed + */ +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, updates state of the board + * @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 + */ +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 + { + //Use this to destroy only the attacking piece, and not the bomb + RemovePiece(target); + delete target; + board[x][y] = NULL; + return MovementResult(MovementResult::DIES, attackerType, defenderType); + + /* + //Use this to destroy both the bomb and the attacking piece + 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); + RemovePiece(target); + delete defender; + delete target; + board[x][y] = NULL; + board[x2][y2] = NULL; + return MovementResult(MovementResult::BOTH_DIE, 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/judge/manager/stratego.h b/judge/manager/stratego.h new file mode 100644 index 0000000..fe73aa1 --- /dev/null +++ b/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/judge/manager/thread_util.cpp b/judge/manager/thread_util.cpp new file mode 100644 index 0000000..2d12438 --- /dev/null +++ b/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/judge/manager/thread_util.h b/judge/manager/thread_util.h new file mode 100644 index 0000000..95a16e0 --- /dev/null +++ b/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/judge/simulator/Makefile b/judge/simulator/Makefile new file mode 100644 index 0000000..6296c3a --- /dev/null +++ b/judge/simulator/Makefile @@ -0,0 +1,16 @@ +#Makefile for simulations +# Not used for building simulate.py +# Used for building/removing results + +BASEDIR = /home/sam/Documents/progcomp2012/progcomp +RESULTSDIR = /home/sam/Documents/progcomp2012/progcomp/web/results +LOGDIR = /home/sam/Documents/progcomp2012/progcomp/web/log +AGENTSDIR = /home/sam/Documents/progcomp2012/progcomp/agents +MANAGER = /home/sam/Documents/progcomp2012/progcomp/judge/manager/stratego + + + +clean: + rm -r -f $(RESULTSDIR) + rm -r -f $(LOGDIR) + diff --git a/judge/simulator/simulate.py b/judge/simulator/simulate.py new file mode 100755 index 0000000..aead755 --- /dev/null +++ b/judge/simulator/simulate.py @@ -0,0 +1,436 @@ +#!/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. + + Now (sortof) generates .html files to display results in a prettiful manner. + + + 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 +from time import time + +#Global variables/arguments + +baseDirectory = "../.." #Base directory for results, logs, agents +nGames = 2 #Number of games played by each agent against each opponent. Half will be played as RED, half as BLUE. If nGames <= 1, then no games will be played (useful for dry run?) +nRounds = 1 + +if len(sys.argv) >= 2: + nRounds = int(sys.argv[1]) +if len(sys.argv) >= 3: + nGames = int(sys.argv[2]) + if nGames % 2 != 0: + print "Warning: nGames should be even. "+str(nGames)+" specified, but only " + str(int(nGames/2) * 2)+" will be played!" +if len(sys.argv) >= 4: + baseDirectory = sys.argv[3] +if len(sys.argv) >= 6: + print "Useage: " +sys.argv[0] + " [nRounds=1] [nGames=10] [baseDirectory=\""+baseDirectory+"\"] [managerPath=baseDirectory+\"/judge/manager/stratego\"]" + sys.exit(1) + +resultsDirectory = baseDirectory+"/web/results/" #Where results will go (results are in the form of text files of agent names and scores) +logDirectory = baseDirectory+"/web/log/" #Where log files go (direct output of manager program) +agentsDirectory = baseDirectory+"/agents/" #Where agents are found (each agent has its own subdirectory within this directory) +managerPath = baseDirectory+"/judge/manager/stratego" #Path to the executable that plays the games +if len(sys.argv) >= 5: + managerPath = sys.argv[5] + + +#Score dictionary - Tuple is of the form: (end score, other score, other result) where end is the player on whose turn the result occurs, other is the other player, other result indicates what to record the outcome as for the other player. +scores = {"VICTORY":(3,1, "DEFEAT"), "DEFEAT":(1,3, "VICTORY"), "SURRENDER":(1,3, "VICTORY"), "DRAW":(2,2, "DRAW"), "DRAW_DEFAULT":(1,1, "DRAW_DEFAULT"), "ILLEGAL":(-1,2, "DEFAULT"), "DEFAULT":(2,-1, "ILLEGAL"), "BOTH_ILLEGAL":(-1,-1, "BOTH_ILLEGAL"), "INTERNAL_ERROR":(0,0, "INTERNAL_ERROR"), "BAD_SETUP":(0,0,"BAD_SETUP")} + + +#Verbose - print lots of useless stuff about what you are doing (kind of like matches talking on irc...) +verbose = True + + + +#Check the manager program exists TODO: And is executable! +if os.path.exists(managerPath) == False: + print "Manager program at \""+managerPath+"\" doesn't exist!" + sys.exit(1) + +#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 from the "info" file in the results directory, if it doesn't exist then start at round 1. +if os.path.exists(resultsDirectory+"info") == False: + totalRounds = 1 +else: + info = open(resultsDirectory+"info", "r") + totalRounds = int(info.readline().strip()) + info.close() + os.remove(resultsDirectory+"info") + +info = open(resultsDirectory+"info", "w") +info.write(str(totalRounds + nRounds) + "\n") +info.close() + + + +if os.path.exists(logDirectory) == False: + os.mkdir(logDirectory) #Make the log directory if it didn't exist + + +startTime = time() #Record time at which simulation starts + +if verbose: + if nRounds > 1: + print "Simulating " + str(nRounds) + " rounds (" + str(totalRounds) + " to " + str(totalRounds + nRounds-1) + ")" + else: + print "Simulating one round." + print "" + print "Identifying possible agents in \""+agentsDirectory+"\"" + +#Get all agent names from agentsDirectory +agentNames = os.listdir(agentsDirectory) +agents = [] +for name in agentNames: + 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 + + infoFile = open(agentsDirectory+name+"/info", "r") + agentExecutable = agentsDirectory+name+"/"+(infoFile.readline().strip()) + author = infoFile.readline().strip() + language = infoFile.readline().strip() + description = "" + while True: + line = infoFile.readline() + if len(line) > 0: + description += line + else: + break + infoFile.close() + + if os.path.exists(agentExecutable) == False: + if verbose: + sys.stdout.write(" Invalid! (Path: \""+agentExecutable+"\" does not exist!)\n") + continue + + + if verbose: + sys.stdout.write(" Valid! (Path: \""+agentExecutable+"\")\n") + + #Convert array of valid names into array of dictionaries containing information about each agent + #I'm starting to like python... + agents.append({"name":name, "path":agentExecutable, "author":author, "language":language, "description":description, "score":[0], "VICTORY":[], "DEFEAT":[], "DRAW":[], "ILLEGAL":[], "DEFAULT":[], "INTERNAL_ERROR":[], "SURRENDER":[], "DRAW_DEFAULT":[], "BOTH_ILLEGAL":[], "BAD_SETUP":[], "ALL":[], "totalScore":0, "Wins":0, "Losses":0, "Draws":0, "Illegal":0, "Errors":0}) + +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 "" + +#Prepare the pretty .html files if they don't exist +if verbose: + print "Preparing .html results files..." + + +for agent in agents: + if os.path.exists(resultsDirectory+agent["name"] + ".html") == False: + agentFile = open(resultsDirectory+agent["name"] + ".html", "w") + agentFile.write("\n\n " + agent["name"] + " overview\n\n\n

Overview for " + agent["name"]+"

\n") + agentFile.write("\n") + agentFile.write("\n") + agentFile.write("\n") + agentFile.write("
Name Author Language
"+agent["name"]+" "+agent["author"]+" "+agent["language"]+"
\n"); + + agentFile.write("

Description

\n") + agentFile.write("

" + agent["description"] + "

\n") + agentFile.close() + + os.rename(resultsDirectory+agent["name"] + ".html", "tmpfile") + + oldFile = open("tmpfile", "r") + agentFile = open(resultsDirectory+agent["name"] + ".html", "w") + line = oldFile.readline() + while line != "": + #if verbose: + # print "Interpreting line \"" + line.strip() + "\"" + if line.strip() == "": + break + elif line == " Score Wins Losses Draws Illegal Errors \n": + agentFile.write(line) + line = oldFile.readline() + + values = line.split(' ') + agent["totalScore"] += int(values[2].strip()) + agent["Wins"] += int(values[5].strip()) + agent["Losses"] += int(values[8].strip()) + agent["Draws"] += int(values[11].strip()) + agent["Illegal"] += int(values[14].strip()) + agent["Errors"] += int(values[17].strip()) + agentFile.write(line) + line = oldFile.readline() + + if verbose: + print "Prepared results file \"" + resultsDirectory+agent["name"] + ".html\"." + oldFile.close() + agentFile.close() + os.remove("tmpfile") + +if verbose: + print "" + +#Do each round... +totalGames = nGames/2 * len(agents) * (len(agents)-1) +for roundNumber in range(totalRounds, totalRounds + nRounds): + + 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 + + for agent in agents: + agent.update({"name":agent["name"], "path":agent["path"], "score":[0], "VICTORY":[], "DEFEAT":[], "DRAW":[], "ILLEGAL":[], "DEFAULT":[], "INTERNAL_ERROR":[], "SURRENDER":[], "DRAW_DEFAULT":[], "BOTH_ILLEGAL":[], "BAD_SETUP":[], "ALL":[]}) + + + print "Commencing ROUND " + str(roundNumber) + " combat!" + print "Total: " + str(totalGames) + " games to be played. This could take a while... (Estimate 60s/game)" + + + + 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. + gameNumber = 0 + 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 + gameNumber += 1 + gameID = str(roundNumber) + "." + str(gameNumber) + 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["name"]+"\" and \""+blue["name"]+"\" playing game (ID: " + gameID + ") ... ") + logFile = logDirectory + "round"+str(roundNumber) + "/"+red["name"]+".vs."+blue["name"]+"."+str(gameID) + outline = os.popen(managerPath + " -o " + logFile + " " + red["path"] + " " + blue["path"], "r").read() + results = outline.split(' ') + + if len(results) != 6: + if verbose: + sys.stdout.write("Garbage output! \"" + outline + "\"\n") + red["INTERNAL_ERROR"].append((blue["name"], gameID, scores["INTERNAL_ERROR"][0])) + blue["INTERNAL_ERROR"].append((red["name"], gameID, scores["INTERNAL_ERROR"][0])) + red["ALL"].append((blue["name"], gameID, scores["INTERNAL_ERROR"][0], "INTERNAL_ERROR")) + blue["ALL"].append((red["name"], gameID, scores["INTERNAL_ERROR"][0], "INTERNAL_ERROR")) + managerErrors += 1 + else: + + if results[1] == "RED": + endColour = red + otherColour = blue + endStr = "RED" + otherStr = "BLUE" + elif results[1] == "BLUE": + endColour = blue + otherColour = red + endStr = "BLUE" + otherStr = "RED" + + + if results[1] == "BOTH": + red["INTERNAL_ERROR"].append((blue["name"], gameID, scores["INTERNAL_ERROR"][0])) + blue["INTERNAL_ERROR"].append((red["name"], gameID, scores["INTERNAL_ERROR"][0])) + red["ALL"].append((blue["name"], gameID, scores["INTERNAL_ERROR"][0], "INTERNAL_ERROR", "RED")) + blue["ALL"].append((red["name"], gameID, scores["INTERNAL_ERROR"][0], "INTERNAL_ERROR", "BLUE")) + managerErrors += 1 + else: + endColour["score"].insert(0,endColour["score"][0] + scores[results[2]][0]) + endColour[results[2]].append((otherColour["name"], gameID, scores[results[2]][0])) + endColour["ALL"].append((otherColour["name"], gameID, scores[results[2]][0], results[2], endStr)) + otherColour["score"].insert(0, otherColour["score"][0] + scores[results[2]][1]) + otherColour[scores[results[2]][2]].append((endColour["name"], gameID, scores[results[2]][1])) + otherColour["ALL"].append((endColour["name"], gameID, scores[results[2]][1], scores[results[2]][2], otherStr)) + + + if verbose: + sys.stdout.write(" Result \"") + for ii in range(1, len(results)): + sys.stdout.write(results[ii].strip()) + if ii < (len(results) - 1): + sys.stdout.write(" ") + sys.stdout.write("\"\n") + + if verbose: + print "Completed combat. Total of " + str(gameNumber) + " games played. " + if managerErrors != 0: + print "WARNING: Registered "+str(managerErrors)+" errors. Check the manager program." + + if verbose: + print "" + #We should now have complete score values. + + ''' + Obselete, non prettified results + if verbose: + sys.stdout.write("Creating raw results files for ROUND " + str(roundNumber) + "... ") + + agents.sort(key = lambda e : e["score"], 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["name"] + " " + str(agent["score"]) +"\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["name"] == data[0]: + agent["totalScore"] = int(data[1]) + agent["score"][0] #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["totalScore"], 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 "" + ''' + if verbose: + print "RESULTS FOR ROUND " + str(roundNumber) + + #totalFile = open(resultsDirectory+"total.scores", "w") #Recreate the file + for agent in agents: + #totalFile.write(agent["name"] + " " + str(agent["totalScore"]) +"\n") #Write the total scores in descending order + #if verbose: + print "Agent: " + str(agent) + + + if verbose: + print "Updating pretty .html files... " + + for agent in agents: + agentFile = open(resultsDirectory + agent["name"]+".html", "a") + agentFile.write("

Round " + str(roundNumber) + "

\n") + agentFile.write("

Round Overview

\n") + agentFile.write("\n") + agentFile.write("\n") + agentFile.write("\n") + + agentFile.write("
Score Wins Losses Draws Illegal Errors
"+str(agent["score"][0])+" "+str(len(agent["VICTORY"]) + len(agent["DEFAULT"]))+" "+str(len(agent["DEFEAT"]) + len(agent["SURRENDER"]))+" "+str(len(agent["DRAW"]) + len(agent["DRAW_DEFAULT"]))+" "+str(len(agent["ILLEGAL"]) + len(agent["BOTH_ILLEGAL"]) + len(agent["BAD_SETUP"]))+" " +str(len(agent["INTERNAL_ERROR"]))+"
\n") + agentFile.write("

Round "+str(roundNumber) + " Scoreboard

\n") + + agentFile.write("

Detailed

\n") + agentFile.write("\n") + agentFile.write("\n") + + + + for index in range(0, len(agent["ALL"])): + if agent["ALL"][index][4] == "RED": + logFile = logDirectory + "round"+str(roundNumber) + "/"+agent["name"]+".vs."+agent["ALL"][index][0]+"."+str(agent["ALL"][index][1]) + else: + logFile = logDirectory + "round"+str(roundNumber) + "/"+agent["ALL"][index][0]+".vs."+agent["name"]+"."+str(agent["ALL"][index][1]) + agentFile.write("\n") + agentFile.write("
Game ID Opponent Played as Outcome Score Accumulated Score
" + str(agent["ALL"][index][1]) + " "+agent["ALL"][index][0] + " " + agent["ALL"][index][4] + " " + agent["ALL"][index][3] + " " + str(agent["ALL"][index][2]) + " " + str(agent["score"][len(agent["score"])-index -2]) + "
\n") + + agent["totalScore"] += agent["score"][0] + agent["Wins"] += len(agent["VICTORY"]) + len(agent["DEFAULT"]) + agent["Losses"] += len(agent["DEFEAT"]) + len(agent["SURRENDER"]) + agent["Draws"] += len(agent["DRAW"]) + len(agent["DRAW_DEFAULT"]) + agent["Illegal"] += len(agent["ILLEGAL"]) + len(agent["BOTH_ILLEGAL"]) + len(agent["BAD_SETUP"]) + agent["Errors"] += len(agent["INTERNAL_ERROR"]) + + agentFile.write("

Accumulated Results

\n") + agentFile.write("\n") + agentFile.write("\n") + agentFile.write("\n") + + agentFile.write("
Score Wins Losses Draws Illegal Errors
"+str(agent["totalScore"])+" "+str(agent["Wins"])+" "+str(agent["Losses"])+" "+str(agent["Draws"])+" "+str(agent["Illegal"])+" " +str(agent["Errors"])+"
\n") + + + agentFile.close() + + #Update round file + roundFile = open(resultsDirectory + "round"+str(roundNumber)+".html", "w") + roundFile.write("\n\n Round " +str(roundNumber)+ " Overview \n\n\n") + roundFile.write("

Round " +str(roundNumber)+ " Overview

\n") + roundFile.write("\n") + roundFile.write("\n") + agents.sort(key = lambda e : e["score"][0], reverse=True) + for agent in agents: + roundFile.write("\n") + roundFile.write("
Name Score Total Score
"+agent["name"] + " " + str(agent["score"][0]) + " " + str(agent["totalScore"]) + "
\n") + roundFile.write("

Current Scoreboard

\n") + roundFile.write("\n\n\n\n") + roundFile.close() + + + + + +if verbose: + print "Finalising .html files... " +for agent in agents: + agentFile = open(resultsDirectory + agent["name"]+".html", "a") + + #Write the "total" statistics + + agentFile.write("\n\n\n\n") + agentFile.close() + + if os.path.exists(resultsDirectory + "total.html") == True: + os.remove(resultsDirectory + "total.html") #Delete the file + +totalFile = open(resultsDirectory + "total.html", "w") +totalFile.write("\n\n Total Overview \n\n\n") +totalFile.write("

Total Overview

\n") +totalFile.write("\n") +totalFile.write("\n") +agents.sort(key = lambda e : e["totalScore"], reverse=True) +for agent in agents: + totalFile.write("\n") +totalFile.write("
Name Total Score
"+agent["name"] + " " + str(agent["totalScore"]) + "
\n") + +totalFile.write("

Round Summaries

\n") +totalFile.write("\n") +for i in range(1, totalRounds+1): + totalFile.write("\n") +totalFile.write("
Round " + str(i) + "
\n") + +totalFile.write("\n\n\n\n") +totalFile.close() + + +if verbose: + print "Done!" + +endTime = time() +print "Completed simulating " + str(nRounds) + " rounds in " + str(endTime - startTime) + " seconds." +sys.exit(0) diff --git a/progcomp/agents/asmodeus/asmodeus.py b/progcomp/agents/asmodeus/asmodeus.py deleted file mode 100755 index 0371dcd..0000000 --- a/progcomp/agents/asmodeus/asmodeus.py +++ /dev/null @@ -1,85 +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] - -if __name__ == "__main__": - asmodeus = Asmodeus() - if asmodeus.Setup(): - while asmodeus.MoveCycle(): - pass - diff --git a/progcomp/agents/asmodeus/basic_python.py b/progcomp/agents/asmodeus/basic_python.py deleted file mode 120000 index 3d6b342..0000000 --- a/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/progcomp/agents/asmodeus/info b/progcomp/agents/asmodeus/info deleted file mode 100644 index 62e9c51..0000000 --- a/progcomp/agents/asmodeus/info +++ /dev/null @@ -1,4 +0,0 @@ -asmodeus.py -Sam Moore -python -Sample AI - Improves basic_python Scores moves based on paths towards enemy units and known combat outcomes, chooses highest scoring move. diff --git a/progcomp/agents/asmodeus/path.py b/progcomp/agents/asmodeus/path.py deleted file mode 100644 index 3f08979..0000000 --- a/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/progcomp/agents/basic_cpp/Makefile b/progcomp/agents/basic_cpp/Makefile deleted file mode 100644 index 0a249b3..0000000 --- a/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/progcomp/agents/basic_cpp/basic_cpp.cpp b/progcomp/agents/basic_cpp/basic_cpp.cpp deleted file mode 100644 index 4312489..0000000 --- a/progcomp/agents/basic_cpp/basic_cpp.cpp +++ /dev/null @@ -1,576 +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]); - - //We might want to actually check for the multiplier in the sample agents! 20/12/11 - unsigned int outIndex = 3; - int multiplier = atoi(tokens[outIndex].c_str()); - if (multiplier == 0) - multiplier = 1; - else - outIndex += 1; - - - - string & outcome = tokens[outIndex]; - - - int x2 = x; int y2 = y; Helper::MoveInDirection(x2,y2,dir, multiplier); - - 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; - } - if (tokens.size() < outIndex+2) - return false; - - - board->Set(x2,y2, attacker); - board->Set(x,y,NULL); - attacker->x = x2; attacker->y = y2; - attacker->rank = Piece::GetRank(tokens[outIndex+1][0]); - ForgetUnit(defender); - } - else if (outcome == "DIES") - { - if (defender == NULL) - { - //cerr << "No defender!\n"; - return false; - } - if (tokens.size() < outIndex+3) - return false; - - - board->Set(x,y,NULL); - defender->rank = Piece::GetRank(tokens[outIndex+2][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 deleted file mode 100644 index 733d9a7..0000000 --- a/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/progcomp/agents/basic_cpp/info b/progcomp/agents/basic_cpp/info deleted file mode 100644 index dd61e4a..0000000 --- a/progcomp/agents/basic_cpp/info +++ /dev/null @@ -1,4 +0,0 @@ -basic_cpp -Sam Moore -C++ -Sample AI - Provides classes that obey the manager program's protocol, and stores the state of the board and pieces, but only makes randomised moves. diff --git a/progcomp/agents/basic_python/basic_python.py b/progcomp/agents/basic_python/basic_python.py deleted file mode 100755 index b384838..0000000 --- a/progcomp/agents/basic_python/basic_python.py +++ /dev/null @@ -1,348 +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 is_integer(s): - """ Using exceptions for this feels... wrong...""" - try: - int(s) - return True - except ValueError: - return False - -def move(x, y, direction, multiplier): - """ Moves point (x,y) in direction, returns a pair """ - if direction == "UP": - return (x,y-multiplier) - elif direction == "DOWN": - return (x,y+multiplier) - elif direction == "LEFT": - return (x-multiplier, y) - elif direction == "RIGHT": - return (x+multiplier, 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 - self.beenRevealed = False - self.positions = [(x, y)] - - 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 - - -def valuedRank(rank): - if ranks.count(rank) > 0: - return len(ranks) - 2 - ranks.index(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 = [] - - self.totalAllies = {'B':6,'1':1,'2':1,'3':2,'4':3,'5':4,'6':4,'7':4,'8':5,'9':8,'s':1,'F':1} - self.totalEnemies = {'B':6,'1':1,'2':1,'3':2,'4':3,'5':4,'6':4,'7':4,'8':5,'9':8,'s':1,'F':1} - self.hiddenEnemies = {'B':6,'1':1,'2':1,'3':2,'4':3,'5':4,'6':4,'7':4,'8':5,'9':8,'s':1,'F':1} - self.hiddenAllies = {'B':6,'1':1,'2':1,'3':2,'4':3,'5':4,'6':4,'7':4,'8':5,'9':8,'s':1,'F':1} - self.lastMoved = None - - - - 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],1) - 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() - - multiplier = 1 - outcome = result[3].strip() - outIndex = 3 - if is_integer(outcome): - multiplier = int(outcome) - outcome = result[4].strip() - outIndex = 4 - - p = move(x,y,direction, multiplier) - - #Determine attacking piece - attacker = self.board[x][y] - self.board[x][y] = None - - if attacker == None: - return False - - lastMoved = attacker - - defender = self.board[p[0]][p[1]] - - #Update attacker's position (Don't overwrite the board yet though) - - attacker.x = p[0] - attacker.y = p[1] - attacker.positions.insert(0, (attacker.x, attacker.y)) - - - #Determine ranks of pieces if supplied - if len(result) >= outIndex + 3: - if defender == None: - return False - attacker.rank = result[outIndex+1].strip() - if attacker.beenRevealed == False: - if attacker.colour == self.colour: - self.hiddenAllies[attacker.rank] -= 1 - elif attacker.colour == oppositeColour(self.colour): - self.hiddenEnemies[attacker.rank] -= 1 - attacker.beenRevealed = True - defender.rank = result[outIndex+2].strip() - if defender.beenRevealed == False: - if defender.colour == self.colour: - self.hiddenAllies[defender.rank] -= 1 - elif defender.colour == oppositeColour(self.colour): - self.hiddenEnemies[defender.rank] -= 1 - - defender.beenRevealed = True - - - - if outcome == "OK": - self.board[p[0]][p[1]] = attacker - - elif outcome == "KILLS": - self.board[p[0]][p[1]] = attacker - - if defender.colour == self.colour: - self.totalAllies[defender.rank] -= 1 - self.units.remove(defender) - elif defender.colour == oppositeColour(self.colour): - self.totalEnemies[defender.rank] -= 1 - self.enemyUnits.remove(defender) - - elif outcome == "DIES": - if attacker.colour == self.colour: - self.totalAllies[attacker.rank] -= 1 - self.units.remove(attacker) - elif attacker.colour == oppositeColour(self.colour): - self.totalEnemies[attacker.rank] -= 1 - self.enemyUnits.remove(attacker) - - elif outcome == "BOTHDIE": - self.board[p[0]][p[1]] = None - - if defender.colour == self.colour: - self.totalAllies[defender.rank] -= 1 - self.units.remove(defender) - elif defender.colour == oppositeColour(self.colour): - self.totalEnemies[defender.rank] -= 1 - self.enemyUnits.remove(defender) - - if attacker.colour == self.colour: - self.totalAllies[attacker.rank] -= 1 - self.units.remove(attacker) - elif attacker.colour == oppositeColour(self.colour): - self.totalEnemies[attacker.rank] -= 1 - self.enemyUnits.remove(attacker) - - 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") - -if __name__ == "__main__": - basicAI = BasicAI() - if basicAI.Setup(): - while basicAI.MoveCycle(): - pass - diff --git a/progcomp/agents/basic_python/info b/progcomp/agents/basic_python/info deleted file mode 100644 index 93fbaa2..0000000 --- a/progcomp/agents/basic_python/info +++ /dev/null @@ -1,4 +0,0 @@ -basic_python.py -Sam Moore -python -Sample AI - Provides classes that obey the manager program's protocol, and stores the state of the board and pieces, but only makes randomised moves. diff --git a/progcomp/agents/vixen/asmodeus.py b/progcomp/agents/vixen/asmodeus.py deleted file mode 120000 index 1b1739c..0000000 --- a/progcomp/agents/vixen/asmodeus.py +++ /dev/null @@ -1 +0,0 @@ -../asmodeus/asmodeus.py \ No newline at end of file diff --git a/progcomp/agents/vixen/basic_python.py b/progcomp/agents/vixen/basic_python.py deleted file mode 120000 index 3d6b342..0000000 --- a/progcomp/agents/vixen/basic_python.py +++ /dev/null @@ -1 +0,0 @@ -../basic_python/basic_python.py \ No newline at end of file diff --git a/progcomp/agents/vixen/info b/progcomp/agents/vixen/info deleted file mode 100644 index 56945f6..0000000 --- a/progcomp/agents/vixen/info +++ /dev/null @@ -1,4 +0,0 @@ -vixen.py -Sam Moore -python -Sample AI - An improvement on asmodeus' score optimisation. Considers probabilities for unknown enemy units, and sums scores for paths with common first move. diff --git a/progcomp/agents/vixen/path.py b/progcomp/agents/vixen/path.py deleted file mode 120000 index 1d82284..0000000 --- a/progcomp/agents/vixen/path.py +++ /dev/null @@ -1 +0,0 @@ -../asmodeus/path.py \ No newline at end of file diff --git a/progcomp/agents/vixen/vixen.py b/progcomp/agents/vixen/vixen.py deleted file mode 100755 index 3aa47cb..0000000 --- a/progcomp/agents/vixen/vixen.py +++ /dev/null @@ -1,176 +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. - -''' - vixen.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 Vixen(BasicAI): - " Python based AI, improves upon Asmodeus by taking into account probabilities, and common paths " - def __init__(self): - #sys.stderr.write("Vixen initialised...\n") - BasicAI.__init__(self) - - - #self.bombScores = {'1' : -0.9 , '2' : -0.8 , '3' : -0.5 , '4' : 0.1, '5' : 0.1, '6' : 0.3, '7' : 0.7, '8' : 1 , '9' : 0.6, 's' : 0} - #self.bombScores = {'1' : -0.9 , '2' : -0.8 , '3' : -0.5 , '4' : -0.5, '5' : -0.4, '6' : -0.5, '7' : -0.2, '8' : 1.0 , '9' : -0.1, 's' : -0.2} - self.suicideScores = {'1' : -0.5 , '2' : -0.4 , '3' : -0.35, '4' : -0.25, '5' : -0.2, '6' : 0.0, '7' : 0.1, '8' : -1.0 , '9' : 0.0, 's' : -0.4} - self.killScores = {'1' : 1.0 , '2' : 0.9 , '3' : 0.9 , '4' : 0.8, '5' : 0.8, '6' : 0.8, '7' : 0.8, '8' : 0.9 , '9' : 0.7, 's' : 1.0} - self.riskScores = {'1' : 0.0, '2' : 0.1, '3' : 0.2, '4': 0.4, '5': 0.6, '6': 0.7, '7':0.8, '8': 0.0, '9' : 1.0, 's' : 0.1} - - - - - - def MakeMove(self): - #sys.stderr.write("Vixen MakingMove...\n") - " Over-rides the default BasicAI.MakeMove function " - - moveList = [] - for unit in self.units: - if unit.mobile() == False: - continue - - scores = {"LEFT":0, "RIGHT":0, "UP":0, "DOWN":0} - - - for target in self.enemyUnits: - if target == unit: - continue - path = PathFinder().pathFind((unit.x, unit.y), (target.x, target.y), self.board) - if path == False or len(path) == 0: - continue - #moveList.append({"unit":unit, "direction":path[0], "score":self.CalculateScore(unit, target, path)}) - scores[path[0]] += self.CalculateScore(unit, target, path) - - bestScore = sorted(scores.items(), key = lambda e : e[1], reverse=True)[0] - moveList.append({"unit":unit, "direction":bestScore[0], "score":bestScore[1]}) - - - if len(moveList) == 0: - print "NO_MOVE" - return True - - moveList.sort(key = lambda e : e["score"], reverse=True) - #sys.stderr.write("vixen - best move: " + str(moveList[0]["unit"].x) + " " + str(moveList[0]["unit"].y) + " " + moveList[0]["direction"] + " [ score = " + str(moveList[0]["score"]) + " ]\n") - if moveList[0]["score"] == 0: - print "NO_MOVE" - return True - - - print str(moveList[0]["unit"].x) + " " + str(moveList[0]["unit"].y) + " " + moveList[0]["direction"] - return True - - - def tailFactor(self, pathLength): - #if pathLength >= len(self.tailFactors) or pathLength <= 0: - # return 0.0 - #return self.tailFactors[pathLength] - #return 0.5 * (1.0 + pow(pathLength, 0.75)) - return 1.0 / pathLength - - - def CalculateScore(self, attacker, defender, path): - p = move(attacker.x, attacker.y, path[0], 1) - - - total = 0.0 - count = 0.0 - for rank in ranks: - prob = self.rankProbability(defender, rank) - if prob > 0.0: - #sys.stderr.write(" " + str(attacker.rank) + " vs. " + str(rank) + " [" + str(prob) + "] score " + str(self.combatScore(attacker.rank, rank, len(path))) + "\n") - total += prob * self.combatScore(attacker.rank, rank, len(path)) - count += 1 - - - #if count > 1: - # total = total / count + self.riskScore(attacker.rank) - - - total = total * self.tailFactor(len(path)) - #HACK - Prevent "oscillating" by decreasing the value of backtracks - if len(path) > 1 and len(attacker.positions) > 1 and attacker.positions[1][0] == p[0] and attacker.positions[1][1] == p[1]: - total = total / 100 - #sys.stderr.write("Total score for " + str(attacker) + " vs. " + str(defender) + " is " + str(total) + "\n") - return total - - def combatScore(self, attackerRank, defenderRank, pathLength): - if defenderRank == 'F': - return 1.0 - elif defenderRank == 'B': - return self.bombScore(attackerRank) - elif defenderRank == 's' and attackerRank == '1' and pathLength == 2: - return self.suicideScore(attackerRank) - elif defenderRank == '1' and attackerRank == 's' and pathLength != 2: - return self.killScore(attackerRank) - - if valuedRank(attackerRank) > valuedRank(defenderRank): - return self.killScore(defenderRank) - elif valuedRank(attackerRank) < valuedRank(defenderRank): - return self.suicideScore(attackerRank) - return self.killScore(defenderRank) + self.suicideScore(attackerRank) - - def killScore(self, defenderRank): - return self.killScores[defenderRank] - - def bombScore(self, attackerRank): - if attackerRank == '8': - return 1.0 - else: - return 0.0 - - def suicideScore(self, attackerRank): - return self.suicideScores[attackerRank] - - def riskScore(self, attackerRank): - return self.riskScores[attackerRank] - - def rankProbability(self, target, targetRank): - if targetRank == '+' or targetRank == '?': - return 0.0 - if target.rank == targetRank: - return 1.0 - elif target.rank != '?': - return 0.0 - - total = 0.0 - for rank in ranks: - if rank == '+' or rank == '?': - continue - elif rank == 'F' or rank == 'B': - if target.lastMoved < 0: - total += self.hiddenEnemies[rank] - else: - total += self.hiddenEnemies[rank] - - if total == 0.0: - return 0.0 - return float(float(self.hiddenEnemies[targetRank]) / float(total)) - - - - - - - -if __name__ == "__main__": - vixen = Vixen() - if vixen.Setup(): - while vixen.MoveCycle(): - pass - diff --git a/progcomp/judge/manager/Makefile b/progcomp/judge/manager/Makefile deleted file mode 100644 index fe319df..0000000 --- a/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/progcomp/judge/manager/ai_controller.cpp b/progcomp/judge/manager/ai_controller.cpp deleted file mode 100644 index 40ee3df..0000000 --- a/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/progcomp/judge/manager/ai_controller.h b/progcomp/judge/manager/ai_controller.h deleted file mode 100644 index 7d62591..0000000 --- a/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/progcomp/judge/manager/array.h b/progcomp/judge/manager/array.h deleted file mode 100644 index 2a1d29f..0000000 --- a/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/progcomp/judge/manager/controller.cpp b/progcomp/judge/manager/controller.cpp deleted file mode 100644 index 1b0a35d..0000000 --- a/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/progcomp/judge/manager/controller.h b/progcomp/judge/manager/controller.h deleted file mode 100644 index 55c233d..0000000 --- a/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/progcomp/judge/manager/game.cpp b/progcomp/judge/manager/game.cpp deleted file mode 100644 index 3d6dcc8..0000000 --- a/progcomp/judge/manager/game.cpp +++ /dev/null @@ -1,609 +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 (!red->HumanController()) - logMessage("Check that program \"%s\" exists and has executable permissions set.\n", redName); - } - if (!blue->Valid()) - { - logMessage("Controller for Player BLUE is invalid!\n"); - if (!blue->HumanController()) - logMessage("Check that program \"%s\" exists and has executable permissions set.\n", blueName); - } - 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()); - if (redSetup == MovementResult::OK) - { - 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"); - } - } - else - { - logMessage("INVALID!\n"); - } - - logMessage("%s BLUE SETUP\n", blue->name.c_str()); - if (blueSetup == MovementResult::OK) - { - for (int y=0; y < 4; ++y) - { - for (int x=0; x < theBoard.Width(); ++x) - { - if (theBoard.GetPiece(x, theBoard.Height()-4+y) != NULL) - logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, theBoard.Height()-4+y)->type)]); - else - logMessage("."); - } - logMessage("\n"); - } - } - else - { - logMessage("INVALID!\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() && blue->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() && red->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 deleted file mode 100644 index 1212b89..0000000 --- a/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/progcomp/judge/manager/graphics.cpp b/progcomp/judge/manager/graphics.cpp deleted file mode 100644 index 5b708df..0000000 --- a/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/progcomp/judge/manager/graphics.h b/progcomp/judge/manager/graphics.h deleted file mode 100644 index f81bdb3..0000000 --- a/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/progcomp/judge/manager/human_controller.cpp b/progcomp/judge/manager/human_controller.cpp deleted file mode 100644 index bf43e8d..0000000 --- a/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/progcomp/judge/manager/human_controller.h b/progcomp/judge/manager/human_controller.h deleted file mode 100644 index b2069fc..0000000 --- a/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/progcomp/judge/manager/images/piece0.bmp b/progcomp/judge/manager/images/piece0.bmp deleted file mode 100644 index bd67766..0000000 Binary files a/progcomp/judge/manager/images/piece0.bmp and /dev/null differ diff --git a/progcomp/judge/manager/images/piece1.bmp b/progcomp/judge/manager/images/piece1.bmp deleted file mode 100644 index 85fd5ba..0000000 Binary files a/progcomp/judge/manager/images/piece1.bmp and /dev/null differ diff --git a/progcomp/judge/manager/images/piece10.bmp b/progcomp/judge/manager/images/piece10.bmp deleted file mode 100644 index cf77ed4..0000000 Binary files a/progcomp/judge/manager/images/piece10.bmp and /dev/null differ diff --git a/progcomp/judge/manager/images/piece11.bmp b/progcomp/judge/manager/images/piece11.bmp deleted file mode 100644 index b006d3d..0000000 Binary files a/progcomp/judge/manager/images/piece11.bmp and /dev/null differ diff --git a/progcomp/judge/manager/images/piece12.bmp b/progcomp/judge/manager/images/piece12.bmp deleted file mode 100644 index 8c67a48..0000000 Binary files a/progcomp/judge/manager/images/piece12.bmp and /dev/null differ diff --git a/progcomp/judge/manager/images/piece13.bmp b/progcomp/judge/manager/images/piece13.bmp deleted file mode 100644 index f822c24..0000000 Binary files a/progcomp/judge/manager/images/piece13.bmp and /dev/null differ diff --git a/progcomp/judge/manager/images/piece14.bmp b/progcomp/judge/manager/images/piece14.bmp deleted file mode 100644 index 3145270..0000000 Binary files a/progcomp/judge/manager/images/piece14.bmp and /dev/null differ diff --git a/progcomp/judge/manager/images/piece2.bmp b/progcomp/judge/manager/images/piece2.bmp deleted file mode 100644 index 0a11b11..0000000 Binary files a/progcomp/judge/manager/images/piece2.bmp and /dev/null differ diff --git a/progcomp/judge/manager/images/piece3.bmp b/progcomp/judge/manager/images/piece3.bmp deleted file mode 100644 index 40ca3f8..0000000 Binary files a/progcomp/judge/manager/images/piece3.bmp and /dev/null differ diff --git a/progcomp/judge/manager/images/piece4.bmp b/progcomp/judge/manager/images/piece4.bmp deleted file mode 100644 index 0a27a46..0000000 Binary files a/progcomp/judge/manager/images/piece4.bmp and /dev/null differ diff --git a/progcomp/judge/manager/images/piece5.bmp b/progcomp/judge/manager/images/piece5.bmp deleted file mode 100644 index 051bd43..0000000 Binary files a/progcomp/judge/manager/images/piece5.bmp and /dev/null differ diff --git a/progcomp/judge/manager/images/piece6.bmp b/progcomp/judge/manager/images/piece6.bmp deleted file mode 100644 index 5ca389b..0000000 Binary files a/progcomp/judge/manager/images/piece6.bmp and /dev/null differ diff --git a/progcomp/judge/manager/images/piece7.bmp b/progcomp/judge/manager/images/piece7.bmp deleted file mode 100644 index aaf28a1..0000000 Binary files a/progcomp/judge/manager/images/piece7.bmp and /dev/null differ diff --git a/progcomp/judge/manager/images/piece8.bmp b/progcomp/judge/manager/images/piece8.bmp deleted file mode 100644 index b2ea5b7..0000000 Binary files a/progcomp/judge/manager/images/piece8.bmp and /dev/null differ diff --git a/progcomp/judge/manager/images/piece9.bmp b/progcomp/judge/manager/images/piece9.bmp deleted file mode 100644 index e37e251..0000000 Binary files a/progcomp/judge/manager/images/piece9.bmp and /dev/null differ diff --git a/progcomp/judge/manager/main.cpp b/progcomp/judge/manager/main.cpp deleted file mode 100644 index 0fbd6ae..0000000 --- a/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 << "BAD_SETUP "; - 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 deleted file mode 100644 index 239c6ac..0000000 --- a/progcomp/judge/manager/manual.txt +++ /dev/null @@ -1,246 +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. - -A WARNING ABOUT BUFFERING - The AI programs must unbuffer their stdin and stdout streams, otherwise it will be seen to be non-responsive. - If you C and you know a way to force the process started by exec() to have unbuffered stdin/stdout, please email the author. - - In C or C++, unbuffering is accomplished with the following lines, which should appear near the start of main() - setbuf(stdin, NULL); - setbuf(stdout, NULL); - In python, unbuffering is accomplished by passing the -u switch to the interpreter, ie: The first line of a script reads: - #!/usr/bin/python -u - - -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 are represented by the following characters: - - 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 - 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) attacks an enemy Bomb, that piece is destroyed. - F Flag NA 1 Immobile. If any piece attacks 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 #). - - Since 20/12 Bombs reflect the traditional rules; they are only destroyed by Miners. - In previous versions contact of an attacker other than a Miner with a Bomb destroyed the Bomb as well as the attacking piece. - - -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 - Occasionally the result is not printed at the end of the game. - So far this has only been observed to occur when RED wins the game by Flag capture. - - 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 - -THIS PAGE LAST UPDATED - 20/12/11 by Sam Moore - diff --git a/progcomp/judge/manager/movementresult.h b/progcomp/judge/manager/movementresult.h deleted file mode 100644 index 5a2aed6..0000000 --- a/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/progcomp/judge/manager/program.cpp b/progcomp/judge/manager/program.cpp deleted file mode 100644 index 588f714..0000000 --- a/progcomp/judge/manager/program.cpp +++ /dev/null @@ -1,189 +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 and is executable... - if (access(executablePath, X_OK) != 0) - { - 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 - - - if (access(executablePath, X_OK) == 0) //Check we STILL have permissions to start the file - 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 deleted file mode 100644 index 01e00bc..0000000 --- a/progcomp/judge/manager/program.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef PROGRAM_H -#define PROGRAM_H - -#include "thread_util.h" - -#include -#include //Needed to check permissions - -/** - * 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 deleted file mode 100644 index 410b70a..0000000 --- a/progcomp/judge/manager/stratego.cpp +++ /dev/null @@ -1,525 +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. If Piece::BOTH, all pieces will be revealed - * @param showRevealed - If true, then all pieces that have taken part in combat will be revealed, regardless of colour. - * If false, only pieces matching the colour reveal will be revealed - */ -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, updates state of the board - * @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 - */ -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 - { - //Use this to destroy only the attacking piece, and not the bomb - RemovePiece(target); - delete target; - board[x][y] = NULL; - return MovementResult(MovementResult::DIES, attackerType, defenderType); - - /* - //Use this to destroy both the bomb and the attacking piece - 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); - RemovePiece(target); - delete defender; - delete target; - board[x][y] = NULL; - board[x2][y2] = NULL; - return MovementResult(MovementResult::BOTH_DIE, 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 deleted file mode 100644 index fe73aa1..0000000 --- a/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/progcomp/judge/manager/thread_util.cpp b/progcomp/judge/manager/thread_util.cpp deleted file mode 100644 index 2d12438..0000000 --- a/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/progcomp/judge/manager/thread_util.h b/progcomp/judge/manager/thread_util.h deleted file mode 100644 index 95a16e0..0000000 --- a/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/progcomp/judge/simulator/Makefile b/progcomp/judge/simulator/Makefile deleted file mode 100644 index 6296c3a..0000000 --- a/progcomp/judge/simulator/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -#Makefile for simulations -# Not used for building simulate.py -# Used for building/removing results - -BASEDIR = /home/sam/Documents/progcomp2012/progcomp -RESULTSDIR = /home/sam/Documents/progcomp2012/progcomp/web/results -LOGDIR = /home/sam/Documents/progcomp2012/progcomp/web/log -AGENTSDIR = /home/sam/Documents/progcomp2012/progcomp/agents -MANAGER = /home/sam/Documents/progcomp2012/progcomp/judge/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 deleted file mode 100755 index aead755..0000000 --- a/progcomp/judge/simulator/simulate.py +++ /dev/null @@ -1,436 +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. - - Now (sortof) generates .html files to display results in a prettiful manner. - - - 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 -from time import time - -#Global variables/arguments - -baseDirectory = "../.." #Base directory for results, logs, agents -nGames = 2 #Number of games played by each agent against each opponent. Half will be played as RED, half as BLUE. If nGames <= 1, then no games will be played (useful for dry run?) -nRounds = 1 - -if len(sys.argv) >= 2: - nRounds = int(sys.argv[1]) -if len(sys.argv) >= 3: - nGames = int(sys.argv[2]) - if nGames % 2 != 0: - print "Warning: nGames should be even. "+str(nGames)+" specified, but only " + str(int(nGames/2) * 2)+" will be played!" -if len(sys.argv) >= 4: - baseDirectory = sys.argv[3] -if len(sys.argv) >= 6: - print "Useage: " +sys.argv[0] + " [nRounds=1] [nGames=10] [baseDirectory=\""+baseDirectory+"\"] [managerPath=baseDirectory+\"/judge/manager/stratego\"]" - sys.exit(1) - -resultsDirectory = baseDirectory+"/web/results/" #Where results will go (results are in the form of text files of agent names and scores) -logDirectory = baseDirectory+"/web/log/" #Where log files go (direct output of manager program) -agentsDirectory = baseDirectory+"/agents/" #Where agents are found (each agent has its own subdirectory within this directory) -managerPath = baseDirectory+"/judge/manager/stratego" #Path to the executable that plays the games -if len(sys.argv) >= 5: - managerPath = sys.argv[5] - - -#Score dictionary - Tuple is of the form: (end score, other score, other result) where end is the player on whose turn the result occurs, other is the other player, other result indicates what to record the outcome as for the other player. -scores = {"VICTORY":(3,1, "DEFEAT"), "DEFEAT":(1,3, "VICTORY"), "SURRENDER":(1,3, "VICTORY"), "DRAW":(2,2, "DRAW"), "DRAW_DEFAULT":(1,1, "DRAW_DEFAULT"), "ILLEGAL":(-1,2, "DEFAULT"), "DEFAULT":(2,-1, "ILLEGAL"), "BOTH_ILLEGAL":(-1,-1, "BOTH_ILLEGAL"), "INTERNAL_ERROR":(0,0, "INTERNAL_ERROR"), "BAD_SETUP":(0,0,"BAD_SETUP")} - - -#Verbose - print lots of useless stuff about what you are doing (kind of like matches talking on irc...) -verbose = True - - - -#Check the manager program exists TODO: And is executable! -if os.path.exists(managerPath) == False: - print "Manager program at \""+managerPath+"\" doesn't exist!" - sys.exit(1) - -#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 from the "info" file in the results directory, if it doesn't exist then start at round 1. -if os.path.exists(resultsDirectory+"info") == False: - totalRounds = 1 -else: - info = open(resultsDirectory+"info", "r") - totalRounds = int(info.readline().strip()) - info.close() - os.remove(resultsDirectory+"info") - -info = open(resultsDirectory+"info", "w") -info.write(str(totalRounds + nRounds) + "\n") -info.close() - - - -if os.path.exists(logDirectory) == False: - os.mkdir(logDirectory) #Make the log directory if it didn't exist - - -startTime = time() #Record time at which simulation starts - -if verbose: - if nRounds > 1: - print "Simulating " + str(nRounds) + " rounds (" + str(totalRounds) + " to " + str(totalRounds + nRounds-1) + ")" - else: - print "Simulating one round." - print "" - print "Identifying possible agents in \""+agentsDirectory+"\"" - -#Get all agent names from agentsDirectory -agentNames = os.listdir(agentsDirectory) -agents = [] -for name in agentNames: - 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 - - infoFile = open(agentsDirectory+name+"/info", "r") - agentExecutable = agentsDirectory+name+"/"+(infoFile.readline().strip()) - author = infoFile.readline().strip() - language = infoFile.readline().strip() - description = "" - while True: - line = infoFile.readline() - if len(line) > 0: - description += line - else: - break - infoFile.close() - - if os.path.exists(agentExecutable) == False: - if verbose: - sys.stdout.write(" Invalid! (Path: \""+agentExecutable+"\" does not exist!)\n") - continue - - - if verbose: - sys.stdout.write(" Valid! (Path: \""+agentExecutable+"\")\n") - - #Convert array of valid names into array of dictionaries containing information about each agent - #I'm starting to like python... - agents.append({"name":name, "path":agentExecutable, "author":author, "language":language, "description":description, "score":[0], "VICTORY":[], "DEFEAT":[], "DRAW":[], "ILLEGAL":[], "DEFAULT":[], "INTERNAL_ERROR":[], "SURRENDER":[], "DRAW_DEFAULT":[], "BOTH_ILLEGAL":[], "BAD_SETUP":[], "ALL":[], "totalScore":0, "Wins":0, "Losses":0, "Draws":0, "Illegal":0, "Errors":0}) - -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 "" - -#Prepare the pretty .html files if they don't exist -if verbose: - print "Preparing .html results files..." - - -for agent in agents: - if os.path.exists(resultsDirectory+agent["name"] + ".html") == False: - agentFile = open(resultsDirectory+agent["name"] + ".html", "w") - agentFile.write("\n\n " + agent["name"] + " overview\n\n\n

Overview for " + agent["name"]+"

\n") - agentFile.write("\n") - agentFile.write("\n") - agentFile.write("\n") - agentFile.write("
Name Author Language
"+agent["name"]+" "+agent["author"]+" "+agent["language"]+"
\n"); - - agentFile.write("

Description

\n") - agentFile.write("

" + agent["description"] + "

\n") - agentFile.close() - - os.rename(resultsDirectory+agent["name"] + ".html", "tmpfile") - - oldFile = open("tmpfile", "r") - agentFile = open(resultsDirectory+agent["name"] + ".html", "w") - line = oldFile.readline() - while line != "": - #if verbose: - # print "Interpreting line \"" + line.strip() + "\"" - if line.strip() == "": - break - elif line == " Score Wins Losses Draws Illegal Errors \n": - agentFile.write(line) - line = oldFile.readline() - - values = line.split(' ') - agent["totalScore"] += int(values[2].strip()) - agent["Wins"] += int(values[5].strip()) - agent["Losses"] += int(values[8].strip()) - agent["Draws"] += int(values[11].strip()) - agent["Illegal"] += int(values[14].strip()) - agent["Errors"] += int(values[17].strip()) - agentFile.write(line) - line = oldFile.readline() - - if verbose: - print "Prepared results file \"" + resultsDirectory+agent["name"] + ".html\"." - oldFile.close() - agentFile.close() - os.remove("tmpfile") - -if verbose: - print "" - -#Do each round... -totalGames = nGames/2 * len(agents) * (len(agents)-1) -for roundNumber in range(totalRounds, totalRounds + nRounds): - - 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 - - for agent in agents: - agent.update({"name":agent["name"], "path":agent["path"], "score":[0], "VICTORY":[], "DEFEAT":[], "DRAW":[], "ILLEGAL":[], "DEFAULT":[], "INTERNAL_ERROR":[], "SURRENDER":[], "DRAW_DEFAULT":[], "BOTH_ILLEGAL":[], "BAD_SETUP":[], "ALL":[]}) - - - print "Commencing ROUND " + str(roundNumber) + " combat!" - print "Total: " + str(totalGames) + " games to be played. This could take a while... (Estimate 60s/game)" - - - - 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. - gameNumber = 0 - 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 - gameNumber += 1 - gameID = str(roundNumber) + "." + str(gameNumber) - 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["name"]+"\" and \""+blue["name"]+"\" playing game (ID: " + gameID + ") ... ") - logFile = logDirectory + "round"+str(roundNumber) + "/"+red["name"]+".vs."+blue["name"]+"."+str(gameID) - outline = os.popen(managerPath + " -o " + logFile + " " + red["path"] + " " + blue["path"], "r").read() - results = outline.split(' ') - - if len(results) != 6: - if verbose: - sys.stdout.write("Garbage output! \"" + outline + "\"\n") - red["INTERNAL_ERROR"].append((blue["name"], gameID, scores["INTERNAL_ERROR"][0])) - blue["INTERNAL_ERROR"].append((red["name"], gameID, scores["INTERNAL_ERROR"][0])) - red["ALL"].append((blue["name"], gameID, scores["INTERNAL_ERROR"][0], "INTERNAL_ERROR")) - blue["ALL"].append((red["name"], gameID, scores["INTERNAL_ERROR"][0], "INTERNAL_ERROR")) - managerErrors += 1 - else: - - if results[1] == "RED": - endColour = red - otherColour = blue - endStr = "RED" - otherStr = "BLUE" - elif results[1] == "BLUE": - endColour = blue - otherColour = red - endStr = "BLUE" - otherStr = "RED" - - - if results[1] == "BOTH": - red["INTERNAL_ERROR"].append((blue["name"], gameID, scores["INTERNAL_ERROR"][0])) - blue["INTERNAL_ERROR"].append((red["name"], gameID, scores["INTERNAL_ERROR"][0])) - red["ALL"].append((blue["name"], gameID, scores["INTERNAL_ERROR"][0], "INTERNAL_ERROR", "RED")) - blue["ALL"].append((red["name"], gameID, scores["INTERNAL_ERROR"][0], "INTERNAL_ERROR", "BLUE")) - managerErrors += 1 - else: - endColour["score"].insert(0,endColour["score"][0] + scores[results[2]][0]) - endColour[results[2]].append((otherColour["name"], gameID, scores[results[2]][0])) - endColour["ALL"].append((otherColour["name"], gameID, scores[results[2]][0], results[2], endStr)) - otherColour["score"].insert(0, otherColour["score"][0] + scores[results[2]][1]) - otherColour[scores[results[2]][2]].append((endColour["name"], gameID, scores[results[2]][1])) - otherColour["ALL"].append((endColour["name"], gameID, scores[results[2]][1], scores[results[2]][2], otherStr)) - - - if verbose: - sys.stdout.write(" Result \"") - for ii in range(1, len(results)): - sys.stdout.write(results[ii].strip()) - if ii < (len(results) - 1): - sys.stdout.write(" ") - sys.stdout.write("\"\n") - - if verbose: - print "Completed combat. Total of " + str(gameNumber) + " games played. " - if managerErrors != 0: - print "WARNING: Registered "+str(managerErrors)+" errors. Check the manager program." - - if verbose: - print "" - #We should now have complete score values. - - ''' - Obselete, non prettified results - if verbose: - sys.stdout.write("Creating raw results files for ROUND " + str(roundNumber) + "... ") - - agents.sort(key = lambda e : e["score"], 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["name"] + " " + str(agent["score"]) +"\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["name"] == data[0]: - agent["totalScore"] = int(data[1]) + agent["score"][0] #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["totalScore"], 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 "" - ''' - if verbose: - print "RESULTS FOR ROUND " + str(roundNumber) - - #totalFile = open(resultsDirectory+"total.scores", "w") #Recreate the file - for agent in agents: - #totalFile.write(agent["name"] + " " + str(agent["totalScore"]) +"\n") #Write the total scores in descending order - #if verbose: - print "Agent: " + str(agent) - - - if verbose: - print "Updating pretty .html files... " - - for agent in agents: - agentFile = open(resultsDirectory + agent["name"]+".html", "a") - agentFile.write("

Round " + str(roundNumber) + "

\n") - agentFile.write("

Round Overview

\n") - agentFile.write("\n") - agentFile.write("\n") - agentFile.write("\n") - - agentFile.write("
Score Wins Losses Draws Illegal Errors
"+str(agent["score"][0])+" "+str(len(agent["VICTORY"]) + len(agent["DEFAULT"]))+" "+str(len(agent["DEFEAT"]) + len(agent["SURRENDER"]))+" "+str(len(agent["DRAW"]) + len(agent["DRAW_DEFAULT"]))+" "+str(len(agent["ILLEGAL"]) + len(agent["BOTH_ILLEGAL"]) + len(agent["BAD_SETUP"]))+" " +str(len(agent["INTERNAL_ERROR"]))+"
\n") - agentFile.write("

Round "+str(roundNumber) + " Scoreboard

\n") - - agentFile.write("

Detailed

\n") - agentFile.write("\n") - agentFile.write("\n") - - - - for index in range(0, len(agent["ALL"])): - if agent["ALL"][index][4] == "RED": - logFile = logDirectory + "round"+str(roundNumber) + "/"+agent["name"]+".vs."+agent["ALL"][index][0]+"."+str(agent["ALL"][index][1]) - else: - logFile = logDirectory + "round"+str(roundNumber) + "/"+agent["ALL"][index][0]+".vs."+agent["name"]+"."+str(agent["ALL"][index][1]) - agentFile.write("\n") - agentFile.write("
Game ID Opponent Played as Outcome Score Accumulated Score
" + str(agent["ALL"][index][1]) + " "+agent["ALL"][index][0] + " " + agent["ALL"][index][4] + " " + agent["ALL"][index][3] + " " + str(agent["ALL"][index][2]) + " " + str(agent["score"][len(agent["score"])-index -2]) + "
\n") - - agent["totalScore"] += agent["score"][0] - agent["Wins"] += len(agent["VICTORY"]) + len(agent["DEFAULT"]) - agent["Losses"] += len(agent["DEFEAT"]) + len(agent["SURRENDER"]) - agent["Draws"] += len(agent["DRAW"]) + len(agent["DRAW_DEFAULT"]) - agent["Illegal"] += len(agent["ILLEGAL"]) + len(agent["BOTH_ILLEGAL"]) + len(agent["BAD_SETUP"]) - agent["Errors"] += len(agent["INTERNAL_ERROR"]) - - agentFile.write("

Accumulated Results

\n") - agentFile.write("\n") - agentFile.write("\n") - agentFile.write("\n") - - agentFile.write("
Score Wins Losses Draws Illegal Errors
"+str(agent["totalScore"])+" "+str(agent["Wins"])+" "+str(agent["Losses"])+" "+str(agent["Draws"])+" "+str(agent["Illegal"])+" " +str(agent["Errors"])+"
\n") - - - agentFile.close() - - #Update round file - roundFile = open(resultsDirectory + "round"+str(roundNumber)+".html", "w") - roundFile.write("\n\n Round " +str(roundNumber)+ " Overview \n\n\n") - roundFile.write("

Round " +str(roundNumber)+ " Overview

\n") - roundFile.write("\n") - roundFile.write("\n") - agents.sort(key = lambda e : e["score"][0], reverse=True) - for agent in agents: - roundFile.write("\n") - roundFile.write("
Name Score Total Score
"+agent["name"] + " " + str(agent["score"][0]) + " " + str(agent["totalScore"]) + "
\n") - roundFile.write("

Current Scoreboard

\n") - roundFile.write("\n\n\n\n") - roundFile.close() - - - - - -if verbose: - print "Finalising .html files... " -for agent in agents: - agentFile = open(resultsDirectory + agent["name"]+".html", "a") - - #Write the "total" statistics - - agentFile.write("\n\n\n\n") - agentFile.close() - - if os.path.exists(resultsDirectory + "total.html") == True: - os.remove(resultsDirectory + "total.html") #Delete the file - -totalFile = open(resultsDirectory + "total.html", "w") -totalFile.write("\n\n Total Overview \n\n\n") -totalFile.write("

Total Overview

\n") -totalFile.write("\n") -totalFile.write("\n") -agents.sort(key = lambda e : e["totalScore"], reverse=True) -for agent in agents: - totalFile.write("\n") -totalFile.write("
Name Total Score
"+agent["name"] + " " + str(agent["totalScore"]) + "
\n") - -totalFile.write("

Round Summaries

\n") -totalFile.write("\n") -for i in range(1, totalRounds+1): - totalFile.write("\n") -totalFile.write("
Round " + str(i) + "
\n") - -totalFile.write("\n\n\n\n") -totalFile.close() - - -if verbose: - print "Done!" - -endTime = time() -print "Completed simulating " + str(nRounds) + " rounds in " + str(endTime - startTime) + " seconds." -sys.exit(0) diff --git a/progcomp/web/index.html b/progcomp/web/index.html deleted file mode 100644 index ff1fea5..0000000 --- a/progcomp/web/index.html +++ /dev/null @@ -1,89 +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

- -

Another Warning: AI programs must unbuffer stdin and stdout themselves. This is explained in the manual page, but I figured no one would read it. It is fairly simple to unbuffer stdin/stdout in C/C++ and python, I have not investigated other languages yet.

- -

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.

- -

Submissions

-

We (I?) are now accepting test submissions.

-

You must submit the full source code, and build instructions or makefile(s) for your program.

-

Also include the name of the executable or script, the name of the AI, your name, and optionally a description of your AI and its tactics.

-

Please email matches@ if you have a submission.

- -

Dates

-

The competition will run in 2012. The exact dates have not been determined. I will consider whether it is worth waiting until freshers have a chance to make submissions March/April, or running the competition earlier in January/February.

- -

Involvement

-

If you want to help set up the competition, email matches@

-

There isn't much left to do, but all help is appreciated. Maybe you can suggest something, and volunteer to implement it!

-

The most useful thing you can do is actually write an AI, and test the manager program. Please report any bugs (in the manager program, not in your AI!) to matches@ (you can feel free to fix them, but won't be able to push unless you email matches).

- -

Last webpage update: 20/12/11

- - - diff --git a/progcomp/web/screenshot.png b/progcomp/web/screenshot.png deleted file mode 100644 index 276d892..0000000 Binary files a/progcomp/web/screenshot.png and /dev/null differ diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..ff1fea5 --- /dev/null +++ b/web/index.html @@ -0,0 +1,89 @@ + + + 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

+ +

Another Warning: AI programs must unbuffer stdin and stdout themselves. This is explained in the manual page, but I figured no one would read it. It is fairly simple to unbuffer stdin/stdout in C/C++ and python, I have not investigated other languages yet.

+ +

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.

+ +

Submissions

+

We (I?) are now accepting test submissions.

+

You must submit the full source code, and build instructions or makefile(s) for your program.

+

Also include the name of the executable or script, the name of the AI, your name, and optionally a description of your AI and its tactics.

+

Please email matches@ if you have a submission.

+ +

Dates

+

The competition will run in 2012. The exact dates have not been determined. I will consider whether it is worth waiting until freshers have a chance to make submissions March/April, or running the competition earlier in January/February.

+ +

Involvement

+

If you want to help set up the competition, email matches@

+

There isn't much left to do, but all help is appreciated. Maybe you can suggest something, and volunteer to implement it!

+

The most useful thing you can do is actually write an AI, and test the manager program. Please report any bugs (in the manager program, not in your AI!) to matches@ (you can feel free to fix them, but won't be able to push unless you email matches).

+ +

Last webpage update: 20/12/11

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