X-Git-Url: https://git.ucc.asn.au/?p=progcomp2012.git;a=blobdiff_plain;f=judge%2Fmanager%2Fgame.cpp;h=c1238f0660104b9b09326f970382badd105130cf;hp=df7a061ab1352241cbac23a92bec0318f87dc8fc;hb=f37d392713a0b6fec7a2c543edcd8402156f3744;hpb=78293905481ab7a67e773d05350da29940a58ea6 diff --git a/judge/manager/game.cpp b/judge/manager/game.cpp index df7a061..c1238f0 100644 --- a/judge/manager/game.cpp +++ b/judge/manager/game.cpp @@ -7,7 +7,7 @@ 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) +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, double newTimeoutTime, const char * newImageOutput) : 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), timeoutTime(newTimeoutTime), imageOutput(newImageOutput) { gameCreated = false; if (gameCreated) @@ -25,21 +25,28 @@ Game::Game(const char * redPath, const char * bluePath, const bool enableGraphic Graphics::Initialise("Stratego", theBoard.Width()*32, theBoard.Height()*32); #endif //BUILD_GRAPHICS - 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); + MakeControllers(redPath, bluePath); + + if (red == NULL || blue == NULL) + { + fprintf(stderr, "Game::Game - Error creating controller: "); + if (red == NULL) + { + if (blue == NULL) + fprintf(stderr, " BOTH! (red: \"%s\", blue: \"%s\"\n", redPath, bluePath); + else + fprintf(stderr, " RED! (red: \"%s\")\n", redPath); + } + else + fprintf(stderr, "BLUE! (blue: \"%s\")\n", bluePath); + exit(EXIT_FAILURE); + } +// logMessage("Game initialised.\n"); } -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) +Game::Game(const char * fromFile, const bool enableGraphics, double newStallTime, const bool allowIllegal, FILE * newLog, const Piece::Colour & newReveal, int newMaxTurns, bool newPrintBoard, double newTimeoutTime,const char * newImageOutput) : 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), timeoutTime(newTimeoutTime), imageOutput(newImageOutput) { gameCreated = false; if (gameCreated) @@ -93,13 +100,13 @@ Piece::Colour Game::Setup(const char * redName, const char * blueName) { logMessage("Controller for Player RED is invalid!\n"); if (!red->HumanController()) - logMessage("Check that program \"%s\" exists and has executable permissions set.\n", redName); + logMessage("Check that executable \"%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); + logMessage("Check that executable \"%s\" exists and has executable permissions set.\n", blueName); } if (!red->Valid()) { @@ -201,21 +208,23 @@ void Game::Wait(double wait) if (wait <= 0) return; - TimerThread timer(wait*1000000); //Wait in seconds - timer.Start(); + + #ifdef BUILD_GRAPHICS + + if (!graphicsEnabled) { - while (!timer.Finished()); - timer.Stop(); + usleep(1000000*wait); //Wait in seconds return; } - #endif //BUILD_GRAPHICS + TimerThread timer(wait*1000000); //Wait in seconds + timer.Start(); while (!timer.Finished()) { - #ifdef BUILD_GRAPHICS + SDL_Event event; while (SDL_PollEvent(&event)) { @@ -227,9 +236,12 @@ void Game::Wait(double wait) break; } } - #endif //BUILD_GRAPHICS } timer.Stop(); + + #else + usleep(wait*1000000); //Wait in seconds + #endif //BUILD_GRAPHICS } @@ -243,13 +255,15 @@ void Game::HandleBrokenPipe(int sig) if (theGame->turn == Piece::RED) { theGame->logMessage("Game ends on RED's turn - REASON: "); - theGame->blue->Message("DEFAULT"); + if (theGame->blue->Valid()) //Should probably check this + theGame->blue->Message("DEFAULT"); } else if (theGame->turn == Piece::BLUE) { theGame->logMessage("Game ends on BLUE's turn - REASON: "); - theGame->red->Message("DEFAULT"); + if (theGame->red->Valid()) //Should probably check this + theGame->red->Message("DEFAULT"); } else { @@ -257,7 +271,7 @@ void Game::HandleBrokenPipe(int sig) } - theGame->logMessage("SIGPIPE - Broken pipe (AI program may have segfaulted)\n"); + theGame->logMessage("SIGPIPE - Broken pipe (AI program no longer running)\n"); if (Game::theGame->printBoard) Game::theGame->theBoard.PrintPretty(stdout, Piece::BOTH); @@ -285,7 +299,7 @@ void Game::HandleBrokenPipe(int sig) else #endif //BUILD_GRAPHICS { - if (theGame->log == stdout) + if (theGame->log == stdout || theGame->log == stderr) { theGame->logMessage( "PRESS ENTER TO EXIT\n"); theGame->theBoard.Print(theGame->log); @@ -354,14 +368,17 @@ void Game::PrintEndMessage(const MovementResult & result) case MovementResult::POSITION_FULL: logMessage("Attempted move into square occupied by neutral or allied piece\n"); break; - case MovementResult::VICTORY: + case MovementResult::VICTORY_FLAG: logMessage("Captured the flag\n"); break; + case MovementResult::VICTORY_ATTRITION: + logMessage("Destroyed all mobile enemy pieces\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"); + logMessage("Response timeout after %2f seconds.\n", timeoutTime); break; case MovementResult::COLOUR_ERROR: logMessage("Internal controller error - COLOUR_ERROR\n"); @@ -432,13 +449,29 @@ void Game::PrintEndMessage(const MovementResult & result) { logMessage("PRESS ENTER TO EXIT\n"); while (fgetc(stdin) != '\n'); + exit(EXIT_SUCCESS); //Might want to actually exit, you foolish fool } } } +/** Checks for victory by attrition (destroying all mobile pieces) + * + * @returns OK for no victory, + * DRAW if both players have no pieces, or + * VICTORY_ATTRITION if the current player has won by attrition + */ +MovementResult Game::CheckVictoryAttrition() +{ + if (theBoard.MobilePieces(Piece::OppositeColour(turn)) == 0) + { + if (theBoard.MobilePieces(turn) == 0) + return MovementResult::DRAW; + else + return MovementResult::VICTORY_ATTRITION; + } + return MovementResult::OK; - - +} MovementResult Game::Play() { @@ -451,10 +484,10 @@ MovementResult Game::Play() - +// logMessage("Messaging red with \"START\"\n"); red->Message("START"); - + int moveCount = 0; while (!Board::HaltResult(result) && (turnCount < maxTurns || maxTurns < 0)) { @@ -473,19 +506,41 @@ MovementResult Game::Play() #ifdef BUILD_GRAPHICS if (graphicsEnabled) + { theBoard.Draw(toReveal); + if (imageOutput != "") + { + string imageFile = "" + imageOutput + "/"+ itostr(moveCount) + ".bmp"; + Graphics::ScreenShot(imageFile.c_str()); + } + + } #endif //BUILD_GRAPHICS turn = Piece::RED; + blue->Pause(); + red->Continue(); + if (!Board::HaltResult(result)) + { + result = CheckVictoryAttrition(); + } + if (Board::HaltResult(result)) + break; + logMessage( "%d RED: ", turnCount); result = red->MakeMove(buffer); red->Message(buffer); blue->Message(buffer); logMessage( "%s\n", buffer.c_str()); + + if (!Board::HaltResult(result)) + { + result = CheckVictoryAttrition(); + } if (Board::HaltResult(result)) break; - if (stallTime > 0) + if (stallTime >= 0) Wait(stallTime); else ReadUserCommand(); @@ -499,35 +554,64 @@ MovementResult Game::Play() theBoard.PrintPretty(stdout, toReveal); fprintf(stdout, "\n\n"); } + + ++moveCount; #ifdef BUILD_GRAPHICS if (graphicsEnabled) + { theBoard.Draw(toReveal); + if (imageOutput != "") + { + string imageFile = "" + imageOutput + "/" + itostr(moveCount) + ".bmp"; + Graphics::ScreenShot(imageFile.c_str()); + } + } #endif //BUILD_GRAPHICS turn = Piece::BLUE; + red->Pause(); + blue->Continue(); + if (!Board::HaltResult(result)) + { + result = CheckVictoryAttrition(); + } + if (Board::HaltResult(result)) + break; + logMessage( "%d BLU: ", turnCount); result = blue->MakeMove(buffer); blue->Message(buffer); red->Message(buffer); logMessage( "%s\n", buffer.c_str()); + if (!Board::HaltResult(result)) + { + result = CheckVictoryAttrition(); + } if (Board::HaltResult(result)) break; - + if (theBoard.MobilePieces(Piece::RED) == 0) + result = MovementResult::DRAW; - + if (theBoard.MobilePieces(Piece::RED) == 0) + { + if (theBoard.MobilePieces(Piece::BLUE) == 0) + result = MovementResult::DRAW; + else + result = MovementResult::VICTORY_ATTRITION; + break; + } - if (stallTime > 0) + if (stallTime >= 0) Wait(stallTime); else ReadUserCommand(); - if (theBoard.MobilePieces(Piece::BOTH) == 0) - result = MovementResult::DRAW; + ++moveCount; ++turnCount; } @@ -569,12 +653,18 @@ int Game::logMessage(const char * format, ...) */ void Game::ReadUserCommand() { - fprintf(stdout, "Waiting for user to press enter...\n"); + fprintf(stdout, "Waiting for user to press enter... (type QUIT to exit)\n"); string command(""); for (char c = fgetc(stdin); c != '\n' && (int)(c) != EOF; c = fgetc(stdin)) { command += c; } + + if (command == "QUIT") + { + fprintf(stdout, "Ordered to quit... exiting...\n"); + exit(EXIT_SUCCESS); + } } MovementResult FileController::QuerySetup(const char * opponentName, std::string setup[]) @@ -610,17 +700,195 @@ MovementResult FileController::QuerySetup(const char * opponentName, std::string MovementResult FileController::QueryMove(std::string & buffer) { + //This bit is kind of hacky and terrible, and yes I am mixing C with C++ + //Yes I should have used fstream for the whole thing and it would be much easier. + //Oh well. + char buf[BUFSIZ]; fgets(buf, sizeof(buf), file); char * s = (char*)(buf); while (*s != ':' && *s != '\0') ++s; - - s += 2; + //Move forward to the start of the move information + for (int i=0; i < 2; ++i) + { + if (*s != '\0' && *s != '\n') + ++s; + } + + //Unfortunately we can't just copy the whole line buffer = string(s); + //We have to remove the movement result tokens + + + vector tokens; + Game::Tokenise(tokens, buffer, ' '); + buffer.clear(); + + if (tokens.size() < 1) + return MovementResult::BAD_RESPONSE; + buffer += tokens[0]; + + + if (tokens[0] == "NO_MOVE") //tokens[0] is either the x coordinate, or "NO_MOVE" + return MovementResult::OK; + if (tokens.size() < 2) + return MovementResult::BAD_RESPONSE; + buffer += " "; + buffer += tokens[1]; //The y coordinate + buffer += " "; + buffer += tokens[2]; //The direction + + //Check for a possible multiplier. If tokens[3] is an integer it will be the multiplier, otherwise it won't be. + if (tokens.size() > 3 && atoi(tokens[3].c_str()) != 0) + { + buffer += " "; + buffer += tokens[3]; + } + else + { + //(tokens[3] should include a new line) + //buffer += "\n"; + } + + + + + + return MovementResult::OK; } +/** + * Tokenise a string + */ +int Game::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(); +} + +/** + * Creates Controller baseds off strings. Takes into account controllers other than AI_Controller. + * @param redPath - Either the path to an AI_Controller compatable executable, or one of %human or %network or %network:[IP_ADDRESS] + * @param bluePath - Ditto + * Sets this->red to a controller using redPath, and this->blue to a controller using bluePath + * TODO: Make nicer (this function should be ~half the length) + */ +void Game::MakeControllers(const char * redPath, const char * bluePath) +{ + Network * redNetwork = NULL; + Network * blueNetwork = NULL; + //To allow for running two network controllers (I don't know why you would, but beside the point...) use two ports + static const int port1 = 4560; + static const int port2 = 4561; + + if (redPath[0] == '@') + { + if (strcmp(redPath, "@human") == 0) + red = new Human_Controller(Piece::RED, graphicsEnabled); + else + { + const char * network = strstr(redPath, "@network"); + if (network == NULL) + { + red = NULL; + return; + } + network = strstr(network, ":"); + + if (network == NULL) + { + logMessage("Creating server for red AI... "); + redNetwork = new Server(port1); + logMessage("Successful!\n"); + + } + else + { + network = (const char*)(network+1); + logMessage("Creating client for red AI... "); + redNetwork = new Client(network, port2); + logMessage("Connected to address %s\n", network); + } + + logMessage(" (Red's responses will be received over the connection)\n"); + red = new NetworkReceiver(Piece::RED, redNetwork); + } + } + else + red = new AI_Controller(Piece::RED, redPath, timeoutTime); + + if (bluePath[0] == '@') + { + if (strcmp(bluePath, "@human") == 0) + blue = new Human_Controller(Piece::BLUE, graphicsEnabled); + else + { + const char * network = strstr(bluePath, "@network"); + if (network == NULL) + { + blue = NULL; + return; + } + network = strstr(network, ":"); + + if (network == NULL) + { + logMessage("Creating server for blue AI... "); + blueNetwork = new Server(port2); + logMessage("Successful!\n"); + + } + else + { + network = (const char*)(network+1); + logMessage("Creating client for blue AI... "); + blueNetwork = new Client(network, port1); + logMessage("Connected to address %s\n", network); + } + logMessage(" (Blue's responses will be received over the connection)\n"); + blue = new NetworkReceiver(Piece::BLUE, blueNetwork); + } + } + else + blue = new AI_Controller(Piece::BLUE, bluePath, timeoutTime); + + if (redNetwork != NULL) + { + + blue = new NetworkSender(Piece::BLUE,blue, redNetwork); + logMessage(" (Blue's responses will be copied over the connection)\n"); + } + if (blueNetwork != NULL) + { + + red = new NetworkSender(Piece::RED, red, blueNetwork); + logMessage(" (Red's responses will be copied over the connection)\n"); + } + + red->FixName(); blue->FixName(); + +} + +string itostr(int i) +{ + stringstream s; + s << i; + return s.str(); +}