X-Git-Url: https://git.ucc.asn.au/?p=progcomp2012.git;a=blobdiff_plain;f=progcomp%2Fjudge%2Fmanager%2Fgame.cpp;fp=progcomp%2Fjudge%2Fmanager%2Fgame.cpp;h=26369d39e7f415eed0409e341a0a8f2b41a728cd;hp=0000000000000000000000000000000000000000;hb=88fc5a96c424e9a451b98f3b680bc1980345320d;hpb=e3b15cd5dea739f7523920d83bda592db95a7b93 diff --git a/progcomp/judge/manager/game.cpp b/progcomp/judge/manager/game.cpp new file mode 100644 index 0000000..26369d3 --- /dev/null +++ b/progcomp/judge/manager/game.cpp @@ -0,0 +1,585 @@ +#include "game.h" + +using namespace std; + + + +Game* Game::theGame = NULL; +bool Game::gameCreated = false; + +Game::Game(const char * redPath, const char * bluePath, const bool enableGraphics, double newStallTime, const bool allowIllegal, FILE * newLog, const Piece::Colour & newReveal, int newMaxTurns, bool newPrintBoard) : red(NULL), blue(NULL), turn(Piece::RED), theBoard(10,10), graphicsEnabled(enableGraphics), stallTime(newStallTime), allowIllegalMoves(allowIllegal), log(newLog), reveal(newReveal), turnCount(0), input(NULL), maxTurns(newMaxTurns), printBoard(newPrintBoard) +{ + gameCreated = false; + if (gameCreated) + { + fprintf(stderr, "Game::Game - Error - Tried to create more than one Game!\n"); + exit(EXIT_FAILURE); + } + gameCreated = true; + Game::theGame = this; + signal(SIGPIPE, Game::HandleBrokenPipe); + + + if (graphicsEnabled && (!Graphics::Initialised())) + Graphics::Initialise("Stratego", theBoard.Width()*32, theBoard.Height()*32); + + if (strcmp(redPath, "human") == 0) + red = new Human_Controller(Piece::RED, graphicsEnabled); + else + red = new AI_Controller(Piece::RED, redPath); + + + if (strcmp(bluePath, "human") == 0) + blue = new Human_Controller(Piece::BLUE, graphicsEnabled); + else + blue = new AI_Controller(Piece::BLUE, bluePath); + + +} + +Game::Game(const char * fromFile, const bool enableGraphics, double newStallTime, const bool allowIllegal, FILE * newLog, const Piece::Colour & newReveal, int newMaxTurns, bool newPrintBoard) : red(NULL), blue(NULL), turn(Piece::RED), theBoard(10,10), graphicsEnabled(enableGraphics), stallTime(newStallTime), allowIllegalMoves(allowIllegal), log(newLog), reveal(newReveal), turnCount(0), input(NULL), maxTurns(newMaxTurns), printBoard(newPrintBoard) +{ + gameCreated = false; + if (gameCreated) + { + fprintf(stderr, "Game::Game - Error - Tried to create more than one Game!\n"); + exit(EXIT_FAILURE); + } + gameCreated = true; + Game::theGame = this; + signal(SIGPIPE, Game::HandleBrokenPipe); + + + if (graphicsEnabled && (!Graphics::Initialised())) + Graphics::Initialise("Stratego", theBoard.Width()*32, theBoard.Height()*32); + + input = fopen(fromFile, "r"); + + red = new FileController(Piece::RED, input); + blue = new FileController(Piece::BLUE, input); + + +} + +Game::~Game() +{ + + delete red; + delete blue; + + if (log != NULL && log != stdout && log != stderr) + fclose(log); + + if (input != NULL && input != stdin) + fclose(input); +} + +/** + * Attempts to setup the board and controllers + * @param redName the name of the red AI + * @param blueName the name of the blue AI + * @returns A colour, indicating if there were any errors + Piece::NONE indicates no errors + Piece::BOTH indicates errors with both AI + Piece::RED / Piece::BLUE indicates an error with only one of the two AI + */ +Piece::Colour Game::Setup(const char * redName, const char * blueName) +{ + + if (!red->Valid()) + { + logMessage("Controller for Player RED is invalid!\n"); + } + if (!blue->Valid()) + { + logMessage("Controller for Player BLUE is invalid!\n"); + } + if (!red->Valid()) + { + if (!blue->Valid()) + return Piece::BOTH; + return Piece::RED; + } + else if (!blue->Valid()) + { + return Piece::BLUE; + } + + for (int y = 4; y < 6; ++y) + { + for (int x = 2; x < 4; ++x) + { + theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE); + } + for (int x = 6; x < 8; ++x) + { + theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE); + } + } + + + MovementResult redSetup = red->Setup(blueName); + MovementResult blueSetup = blue->Setup(redName); + + + Piece::Colour result = Piece::NONE; + if (redSetup != MovementResult::OK) + { + if (blueSetup != MovementResult::OK) + { + logMessage("BOTH players give invalid setup!\n"); + result = Piece::BOTH; + } + else + { + logMessage("Player RED gave an invalid setup!\n"); + result = Piece::RED; + } + + } + else if (blueSetup != MovementResult::OK) + { + logMessage("Player BLUE gave an invalid setup!\n"); + result = Piece::BLUE; + } + + logMessage("%s RED SETUP\n", red->name.c_str()); + for (int y=0; y < 4; ++y) + { + for (int x=0; x < theBoard.Width(); ++x) + { + if (theBoard.GetPiece(x, y) != NULL) + logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, y)->type)]); + else + logMessage("."); + } + logMessage("\n"); + } + + logMessage("%s BLUE SETUP\n", blue->name.c_str()); + for (int y=0; y < 4; ++y) + { + for (int x=0; x < theBoard.Width(); ++x) + logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, theBoard.Height()-4 + y)->type)]); + logMessage("\n"); + } + + + return result; + +} + +void Game::Wait(double wait) +{ + if (wait <= 0) + return; + + TimerThread timer(wait*1000000); //Wait in seconds + timer.Start(); + + if (!graphicsEnabled) + { + while (!timer.Finished()); + timer.Stop(); + return; + } + + + while (!timer.Finished()) + { + SDL_Event event; + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_QUIT: + timer.Stop(); + exit(EXIT_SUCCESS); + break; + } + } + } + timer.Stop(); + +} + +void Game::HandleBrokenPipe(int sig) +{ + if (theGame == NULL) + { + fprintf(stderr, "ERROR - Recieved SIGPIPE during game exit!\n"); + exit(EXIT_FAILURE); + } + if (theGame->turn == Piece::RED) + { + theGame->logMessage("Game ends on RED's turn - REASON: "); + theGame->blue->Message("DEFAULT"); + } + else if (theGame->turn == Piece::BLUE) + { + + theGame->logMessage("Game ends on BLUE's turn - REASON: "); + theGame->red->Message("DEFAULT"); + } + else + { + theGame->logMessage("Game ends on ERROR's turn - REASON: "); + + } + + theGame->logMessage("SIGPIPE - Broken pipe (AI program may have segfaulted)\n"); + + if (Game::theGame->printBoard) + Game::theGame->theBoard.PrintPretty(stdout, Piece::BOTH); + + if (Game::theGame->graphicsEnabled && theGame->log == stdout) + { + theGame->logMessage("CLOSE WINDOW TO EXIT\n"); + Game::theGame->theBoard.Draw(Piece::BOTH); + while (true) + { + SDL_Event event; + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_QUIT: + exit(EXIT_SUCCESS); + break; + } + } + } + } + else + { + if (theGame->log == stdout) + { + theGame->logMessage( "PRESS ENTER TO EXIT\n"); + theGame->theBoard.Print(theGame->log); + while (fgetc(stdin) != '\n'); + } + } + + + exit(EXIT_SUCCESS); +} + +void Game::PrintEndMessage(const MovementResult & result) +{ + if (turnCount == 0) + { + logMessage("Game ends in the SETUP phase - REASON: "); + } + else + { + if (turn == Piece::RED) + { + logMessage("Game ends on RED's turn - REASON: "); + } + else if (turn == Piece::BLUE) + { + logMessage("Game ends on BLUE's turn - REASON: "); + } + else + { + logMessage("Game ends on ERROR's turn - REASON: "); + + } + } + switch (result.type) + { + case MovementResult::OK: + logMessage("Status returned OK, unsure why game halted...\n"); + break; + case MovementResult::DIES: + logMessage("Status returned DIES, unsure why game halted...\n"); + break; + case MovementResult::KILLS: + logMessage("Status returned KILLS, unsure why game halted...\n"); + break; + case MovementResult::BOTH_DIE: + logMessage("Status returned BOTH_DIE, unsure why game halted...\n"); + break; + case MovementResult::NO_BOARD: + logMessage("Board does not exit?!\n"); + break; + case MovementResult::INVALID_POSITION: + logMessage("Coords outside board\n"); + break; + case MovementResult::NO_SELECTION: + logMessage("Move does not select a piece\n"); + break; + case MovementResult::NOT_YOUR_UNIT: + logMessage("Selected piece belongs to other player\n"); + break; + case MovementResult::IMMOBILE_UNIT: + logMessage("Selected piece is not mobile (FLAG or BOMB)\n"); + break; + case MovementResult::INVALID_DIRECTION: + logMessage("Selected unit cannot move that way\n"); + break; + case MovementResult::POSITION_FULL: + logMessage("Attempted move into square occupied by neutral or allied piece\n"); + break; + case MovementResult::VICTORY: + logMessage("Captured the flag\n"); + break; + case MovementResult::BAD_RESPONSE: + logMessage("Unintelligable response\n"); + break; + case MovementResult::NO_MOVE: + logMessage("Did not make a move (may have exited)\n"); + break; + case MovementResult::COLOUR_ERROR: + logMessage("Internal controller error - COLOUR_ERROR\n"); + break; + case MovementResult::ERROR: + logMessage("Internal controller error - Unspecified ERROR\n"); + break; + case MovementResult::DRAW_DEFAULT: + logMessage("Game declared a draw after %d turns\n", turnCount); + break; + case MovementResult::DRAW: + logMessage("Game declared a draw because neither player has mobile pieces\n"); + break; + case MovementResult::SURRENDER: + logMessage("This player has surrendered!\n"); + break; + case MovementResult::BAD_SETUP: + switch (turn) + { + case Piece::RED: + logMessage("An illegal setup was made by RED\n"); + break; + case Piece::BLUE: + logMessage("An illegal setup was made by BLUE\n"); + break; + case Piece::BOTH: + logMessage("An illegal setup was made by BOTH players\n"); + break; + case Piece::NONE: + logMessage("Unknown internal error.\n"); + break; + } + break; + + } + + if (printBoard) + { + system("clear"); + fprintf(stdout, "%d Final State\n", turnCount); + theBoard.PrintPretty(stdout, Piece::BOTH); + fprintf(stdout, "\n"); + } + if (graphicsEnabled && log == stdout) + { + logMessage("CLOSE WINDOW TO EXIT\n"); + theBoard.Draw(Piece::BOTH); + while (true) + { + SDL_Event event; + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_QUIT: + exit(EXIT_SUCCESS); + break; + } + } + } + } + else + { + if (log == stdout) + { + logMessage("PRESS ENTER TO EXIT\n"); + while (fgetc(stdin) != '\n'); + } + } + +} + + + +MovementResult Game::Play() +{ + + MovementResult result = MovementResult::OK; + turnCount = 1; + string buffer; + + Piece::Colour toReveal = reveal; + + + + + + red->Message("START"); + + + + while (!Board::HaltResult(result) && (turnCount < maxTurns || maxTurns < 0)) + { + if (red->HumanController()) + toReveal = Piece::RED; + if (printBoard) + { + system("clear"); + if (turnCount == 0) + fprintf(stdout, "START:\n"); + else + fprintf(stdout, "%d BLUE:\n", turnCount); + theBoard.PrintPretty(stdout, toReveal); + fprintf(stdout, "\n\n"); + } + + if (graphicsEnabled) + theBoard.Draw(toReveal); + + turn = Piece::RED; + logMessage( "%d RED: ", turnCount); + result = red->MakeMove(buffer); + red->Message(buffer); + blue->Message(buffer); + logMessage( "%s\n", buffer.c_str()); + if (Board::HaltResult(result)) + break; + + if (stallTime > 0) + Wait(stallTime); + else + ReadUserCommand(); + + if (blue->HumanController()) + toReveal = Piece::BLUE; + if (printBoard) + { + system("clear"); + fprintf(stdout, "%d RED:\n", turnCount); + theBoard.PrintPretty(stdout, toReveal); + fprintf(stdout, "\n\n"); + } + if (graphicsEnabled) + theBoard.Draw(toReveal); + + + + turn = Piece::BLUE; + logMessage( "%d BLU: ", turnCount); + result = blue->MakeMove(buffer); + blue->Message(buffer); + red->Message(buffer); + logMessage( "%s\n", buffer.c_str()); + + if (Board::HaltResult(result)) + break; + + + + + + if (stallTime > 0) + Wait(stallTime); + else + ReadUserCommand(); + + if (theBoard.MobilePieces(Piece::BOTH) == 0) + result = MovementResult::DRAW; + + ++turnCount; + } + + if ((maxTurns >= 0 && turnCount >= maxTurns) && result == MovementResult::OK) + { + result = MovementResult::DRAW_DEFAULT; + } + + + return result; + + + +} + +/** + * Logs a message to the game's log file if it exists + * @param format the format string + * @param additional parameters - printed using va_args + * @returns the result of vfprintf or a negative number if the log file does not exist + */ +int Game::logMessage(const char * format, ...) +{ + if (log == NULL) + return -666; + va_list ap; + va_start(ap, format); + + int result = vfprintf(log, format, ap); + va_end(ap); + + return result; +} + +/** + * Waits for a user command + * Currently ignores the command. + */ +void Game::ReadUserCommand() +{ + fprintf(stdout, "Waiting for user to press enter...\n"); + string command(""); + for (char c = fgetc(stdin); c != '\n' && (int)(c) != EOF; c = fgetc(stdin)) + { + command += c; + } +} + +MovementResult FileController::QuerySetup(const char * opponentName, std::string setup[]) +{ + + char c = fgetc(file); + name = ""; + while (c != ' ') + { + name += c; + c = fgetc(file); + } + + while (fgetc(file) != '\n'); + + for (int y = 0; y < 4; ++y) + { + setup[y] = ""; + for (int x = 0; x < Game::theGame->theBoard.Width(); ++x) + { + setup[y] += fgetc(file); + } + + if (fgetc(file) != '\n') + { + return MovementResult::BAD_RESPONSE; + } + } + return MovementResult::OK; + + +} + +MovementResult FileController::QueryMove(std::string & buffer) +{ + char buf[BUFSIZ]; + + fgets(buf, sizeof(buf), file); + char * s = (char*)(buf); + while (*s != ':' && *s != '\0') + ++s; + + s += 2; + + buffer = string(s); + return MovementResult::OK; +} + +