Commit b563784f authored by Sam Moore's avatar Sam Moore

More adding of pointless crap to manager

-f option to allow replaying of games output to files with -o
-m option to enforce max number of turns (default 5000) before a DRAW is called
-p to print a colourful representation of the board to stdout

Yes. I now have both graphics AND pretty coloured terminal escape codes.
Why did I do this??????????????
parent 4a3c0478
......@@ -11,7 +11,7 @@
class AI_Controller : public Controller, private Program
{
public:
AI_Controller(const Piece::Colour & newColour, const char * executablePath, const double newTimeout = 2.0) : Controller(newColour), Program(executablePath), timeout(newTimeout) {}
AI_Controller(const Piece::Colour & newColour, const char * executablePath, const double newTimeout = 2.0) : Controller(newColour, executablePath), Program(executablePath), timeout(newTimeout) {}
virtual ~AI_Controller() {}
......@@ -21,6 +21,8 @@ class AI_Controller : public Controller, private Program
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
......
......@@ -51,7 +51,7 @@ MovementResult Controller::Setup(const char * opponentName)
usedUnits[(int)(type)]++;
if (usedUnits[type] > Piece::maxUnits[(int)type])
{
fprintf(stderr, "Too many units of type %c\n", Piece::tokens[(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);
......@@ -107,7 +107,7 @@ MovementResult Controller::MakeMove(string & buffer)
}
else
{
fprintf(stderr, "BAD_RESPONSE \"%s\"\n", buffer.c_str());
//fprintf(stderr, "BAD_RESPONSE \"%s\"\n", buffer.c_str());
return MovementResult::BAD_RESPONSE; //Player gave bogus direction - it will lose by default.
}
......
......@@ -12,7 +12,7 @@
class Controller
{
public:
Controller(const Piece::Colour & newColour) : colour(newColour) {}
Controller(const Piece::Colour & newColour, const char * newName = "no-name") : colour(newColour), name(newName) {}
virtual ~Controller() {}
MovementResult Setup(const char * opponentName);
......@@ -26,9 +26,12 @@ class Controller
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;
};
......
......@@ -5,14 +5,14 @@ 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) : red(NULL), blue(NULL), turn(Piece::RED), theBoard(10,10), graphicsEnabled(enableGraphics), stallTime(newStallTime), allowIllegalMoves(allowIllegal), log(newLog), reveal(newReveal), turnCount(0)
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)
{
static bool gameCreated = false;
gameCreated = false;
if (gameCreated)
{
if (log != NULL)
fprintf(log, "ERROR - Game has already been created!\n");
fprintf(stderr, "Game::Game - Error - Tried to create more than one Game!\n");
exit(EXIT_FAILURE);
}
gameCreated = true;
......@@ -32,24 +32,62 @@ Game::Game(const char * redPath, const char * bluePath, const bool enableGraphic
if (strcmp(bluePath, "human") == 0)
blue = new Human_Controller(Piece::BLUE, graphicsEnabled);
else
blue = new AI_Controller(Piece::BLUE, redPath);
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()
{
fprintf(stderr, "Killing AI\n");
delete red;
delete blue;
if (log != NULL && log != stdout && log != stderr)
fclose(log);
if (input != NULL && input != stdin)
fclose(input);
}
bool Game::Setup(const char * redName, const char * blueName)
{
if (!red->Valid())
{
logMessage("Controller for Player RED is invalid!\n");
}
if (!blue->Valid())
{
logMessage("Controller for Player BLUE is invalid!\n");
}
if (!red->Valid() || !blue->Valid())
return false;
for (int y = 4; y < 6; ++y)
{
for (int x = 2; x < 4; ++x)
......@@ -70,15 +108,13 @@ bool Game::Setup(const char * redName, const char * blueName)
{
if (blueSetup != MovementResult::OK)
{
if (log != NULL)
fprintf(log, "BOTH players give invalid setup!\n");
logMessage("BOTH players give invalid setup!\n");
red->Message("ILLEGAL");
blue->Message("ILLEGAL");
}
else
{
if (log != NULL)
fprintf(log, "Player RED gave an invalid setup!\n");
logMessage("Player RED gave an invalid setup!\n");
red->Message("ILLEGAL");
blue->Message("DEFAULT");
}
......@@ -86,12 +122,29 @@ bool Game::Setup(const char * redName, const char * blueName)
}
else if (blueSetup != MovementResult::OK)
{
if (log != NULL)
fprintf(log, "Player BLUE gave an invalid setup!\n");
logMessage("Player BLUE gave an invalid setup!\n");
red->Message("DEFAULT");
blue->Message("ILLEGAL");
return false;
}
logMessage("%s RED SETUP\n", red->name.c_str());
for (int y=0; y < 4; ++y)
{
for (int x=0; x < theBoard.Width(); ++x)
logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, y)->type)]);
logMessage("\n");
}
logMessage("%s BLUE SETUP\n", blue->name.c_str());
for (int y=0; y < 4; ++y)
{
for (int x=0; x < theBoard.Width(); ++x)
logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, theBoard.Height()-4 + y)->type)]);
logMessage("\n");
}
return true;
}
......@@ -151,7 +204,8 @@ void Game::HandleBrokenPipe(int sig)
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)
{
......@@ -250,9 +304,19 @@ void Game::PrintEndMessage(const MovementResult & result)
case MovementResult::ERROR:
logMessage("Internal controller error - Unspecified ERROR\n");
break;
case MovementResult::DRAW:
logMessage("Game declared a draw after %d turns\n", turnCount);
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");
......@@ -294,8 +358,8 @@ MovementResult Game::Play()
red->Message("START");
logMessage("START");
while (Board::LegalResult(result))
//logMessage("START\n");
while (Board::LegalResult(result) && (turnCount < maxTurns || maxTurns < 0))
{
......@@ -309,6 +373,13 @@ MovementResult Game::Play()
break;
if (graphicsEnabled)
theBoard.Draw(reveal);
if (printBoard)
{
system("clear");
fprintf(stdout, "%d RED:\n", turnCount);
theBoard.PrintPretty(stdout, reveal);
fprintf(stdout, "\n\n");
}
Wait(stallTime);
turn = Piece::BLUE;
......@@ -325,11 +396,25 @@ MovementResult Game::Play()
if (graphicsEnabled)
theBoard.Draw(reveal);
if (printBoard)
{
system("clear");
fprintf(stdout, "%d BLUE:\n", turnCount);
theBoard.PrintPretty(stdout, reveal);
fprintf(stdout, "\n\n");
}
Wait(stallTime);
++turnCount;
}
if ((maxTurns >= 0 && turnCount >= maxTurns) && result == MovementResult::OK)
{
result = MovementResult::DRAW;
turn = Piece::BOTH;
}
return result;
......@@ -355,3 +440,49 @@ int Game::logMessage(const char * format, ...)
return result;
}
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;
}
......@@ -13,7 +13,8 @@
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);
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();
......@@ -32,10 +33,12 @@ class Game
int TurnCount() const {return turnCount;}
static Game * theGame;
private:
public:
int logMessage(const char * format, ...);
FILE * GetLogFile() const {return log;}
Controller * red;
Controller * blue;
private:
Piece::Colour turn;
public:
......@@ -48,10 +51,36 @@ class Game
private:
FILE * log;
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
......@@ -9,7 +9,7 @@
class Human_Controller : public Controller
{
public:
Human_Controller(const Piece::Colour & newColour, const bool enableGraphics) : Controller(newColour), graphicsEnabled(enableGraphics) {}
Human_Controller(const Piece::Colour & newColour, const bool enableGraphics) : Controller(newColour, "human"), graphicsEnabled(enableGraphics) {}
virtual ~Human_Controller() {}
virtual MovementResult QuerySetup(const char * opponentName, std::string setup[]);
......
......@@ -10,11 +10,41 @@
using namespace std;
void CreateGame(int argc, char ** argv);
void DestroyGame();
void PrintResults(const MovementResult & result);
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);
}
CreateGame(argc, argv);
if (Game::theGame == NULL)
{
fprintf(stderr, "ERROR: Couldn't create a game!\n");
exit(EXIT_FAILURE);
}
MovementResult result = Game::theGame->Play();
Game::theGame->PrintEndMessage(result);
PrintResults(result);
exit(EXIT_SUCCESS);
return 0;
}
void CreateGame(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;
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] == '-')
......@@ -31,10 +61,13 @@ int main(int argc, char ** argv)
++ii;
break;
case 'g':
graphics = true;
graphics = !graphics;
break;
case 'p':
printBoard = !printBoard;
break;
case 'i':
allowIllegal = true;
allowIllegal = !allowIllegal;
break;
case 'o':
......@@ -69,6 +102,34 @@ int main(int argc, char ** argv)
else
reveal = Piece::NONE;
break;
case 'm':
if (argc - ii <= 1)
{
fprintf(stderr, "Expected max_turns value after -m switch!\n");
exit(EXIT_FAILURE);
}
if (strcmp(argv[ii+1], "inf"))
maxTurns = -1;
else
maxTurns = atoi(argv[ii+1]);
++ii;
break;
case 'f':
if (argc - ii <= 1)
{
fprintf(stderr, "Expected filename after -f switch!\n");
exit(EXIT_FAILURE);
}
if (log != NULL)
{
fprintf(stderr, "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");
......@@ -102,27 +163,28 @@ int main(int argc, char ** argv)
}
}
}
if (argc == 1)
if (inputFile == NULL)
{
fprintf(stderr, "Usage: stratego [options] red blue\n");
fprintf(stderr, " stratego --help\n");
exit(EXIT_SUCCESS);
Game::theGame = new Game(red,blue, graphics, timeout, allowIllegal,log, reveal,maxTurns, printBoard);
}
Game game(red, blue, graphics, timeout, allowIllegal, log, reveal);
if (!game.Setup(red, blue))
else
{
Game::theGame = new Game(inputFile, graphics, timeout, allowIllegal,log, reveal,maxTurns, printBoard);
}
if (!Game::theGame->Setup(red, blue))
{
fprintf(stdout, "NONE %d\n",game.TurnCount());
fprintf(stdout, "NONE %d\n",Game::theGame->TurnCount());
exit(EXIT_SUCCESS);
}
atexit(DestroyGame);
MovementResult result = game.Play();
game.PrintEndMessage(result);
}
Piece::Colour winner = game.Turn();
void PrintResults(const MovementResult & result)
{
Piece::Colour winner = Game::theGame->Turn();
if (Board::LegalResult(result))
{
if (winner == Piece::BOTH)
......@@ -140,28 +202,27 @@ int main(int argc, char ** argv)
switch (winner)
{
case Piece::RED:
fprintf(stdout, "%s RED %d\n", red,game.TurnCount());
fprintf(stdout, "%s RED %d\n", Game::theGame->red->name.c_str(),Game::theGame->TurnCount());
Game::theGame->logMessage("%s RED %d\n", Game::theGame->red->name.c_str(),Game::theGame->TurnCount());
break;
case Piece::BLUE:
fprintf(stdout, "%s BLUE %d\n", blue,game.TurnCount());
fprintf(stdout, "%s BLUE %d\n", Game::theGame->blue->name.c_str(),Game::theGame->TurnCount());
Game::theGame->logMessage("%s BLUE %d\n", Game::theGame->blue->name.c_str(),Game::theGame->TurnCount());
break;
case Piece::BOTH:
fprintf(stdout, "DRAW %d\n",game.TurnCount());
fprintf(stdout, "DRAW %d\n",Game::theGame->TurnCount());
Game::theGame->logMessage("DRAW %d\n",Game::theGame->TurnCount());
break;
case Piece::NONE:
fprintf(stdout, "NONE %d\n",game.TurnCount());
fprintf(stdout, "NONE %d\n",Game::theGame->TurnCount());
Game::theGame->logMessage("NONE %d\n",Game::theGame->TurnCount());
break;
}
exit(EXIT_SUCCESS);
return 0;
}
void DestroyGame()
{
delete Game::theGame;
Game::theGame = NULL;
}
......@@ -5,14 +5,14 @@ WARNING
This program is still a work in progress. Consider it a Beta version.
SYNOPSIS
stratego {[-girb] [-o= output_file ] [-t= stall_time] red_player blue_player | {-h | --help}}
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 -h, or --help is given, both red_player and blue_player must be supplied.
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".
......@@ -27,6 +27,10 @@ DESCRIPTION
OPTIONS
-g
By default, graphics are disabled. If the -g switch is present, stratego will draw the game as it is played using 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.
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).
......@@ -42,8 +46,22 @@ OPTIONS
-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.
-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.
-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 present, this page will be printed and stratego will exit.
If the -h switch is used, this page will be printed and stratego will exit.
GAME RULES
Each player sets up 40 pieces on the Board. The pieces consist of the following:
......