[PATCH] Handle case where an AI sends an invalid message and then crashes
[progcomp2012.git] / judge / manager / game.cpp
index 486920d..3f5b46b 100644 (file)
@@ -1,5 +1,7 @@
 #include "game.h"
 #include <stdarg.h>
+#include <string>
+
 using namespace std;
 
 
@@ -7,7 +9,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, double newTimeoutTime) : 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)
+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)
@@ -22,24 +24,37 @@ Game::Game(const char * redPath, const char * bluePath, const bool enableGraphic
 
        #ifdef BUILD_GRAPHICS
        if (graphicsEnabled && (!Graphics::Initialised()))
-                       Graphics::Initialise("Stratego", theBoard.Width()*32, theBoard.Height()*32);
+       {
+                       string s = "Stratego: ";
+                       s += string(redPath);
+                       s += " ";
+                       s += string(bluePath);
+                       Graphics::Initialise(s.c_str(), theBoard.Width()*GRID_SIZE, theBoard.Height()*GRID_SIZE);
+       }
        #endif //BUILD_GRAPHICS
 
-       if (strcmp(redPath, "human") == 0)
-               red = new Human_Controller(Piece::RED, graphicsEnabled);
-       else
-               red = new AI_Controller(Piece::RED, redPath, timeoutTime);
-       
-       
-       if (strcmp(bluePath, "human") == 0)
-               blue = new Human_Controller(Piece::BLUE, graphicsEnabled);
-       else
-               blue = new AI_Controller(Piece::BLUE, bluePath, timeoutTime);
 
 
+       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, double newTimeoutTime) : 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)
+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)
@@ -53,7 +68,11 @@ Game::Game(const char * fromFile, const bool enableGraphics, double newStallTime
 
        #ifdef BUILD_GRAPHICS
        if (graphicsEnabled && (!Graphics::Initialised()))
-                       Graphics::Initialise("Stratego", theBoard.Width()*32, theBoard.Height()*32);
+       {
+                       string s = "Stratego: (file) ";
+                       s += string(fromFile);
+                       Graphics::Initialise(s.c_str(), theBoard.Width()*GRID_SIZE, theBoard.Height()*GRID_SIZE);
+       }
        #endif //BUILD_GRAPHICS
 
        input = fopen(fromFile, "r");
@@ -201,21 +220,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 +248,12 @@ void Game::Wait(double wait)
                                        break;
                        }
                }
-               #endif //BUILD_GRAPHICS
        }
        timer.Stop();
+
+       #else
+       usleep(wait*1000000); //Wait in seconds
+       #endif //BUILD_GRAPHICS
        
 }
 
@@ -240,62 +264,61 @@ void Game::HandleBrokenPipe(int sig)
                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: ");
-               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: ");
-               if (theGame->red->Valid()) //Should probably check this
-                       theGame->red->Message("DEFAULT");
-       }
-       else
-       {
-               theGame->logMessage("Game ends on ERROR's turn - REASON: ");
-                       
-       }
        
        theGame->logMessage("SIGPIPE - Broken pipe (AI program no longer running)\n");
 
-       if (Game::theGame->printBoard)
-               Game::theGame->theBoard.PrintPretty(stdout, Piece::BOTH);
+       MovementResult result = MovementResult::BAD_RESPONSE; 
 
-       
-       #ifdef BUILD_GRAPHICS
-       if (Game::theGame->graphicsEnabled && theGame->log == stdout)
+       if (theGame->turn == Piece::RED)
        {
-               theGame->logMessage("CLOSE WINDOW TO EXIT\n");
-               Game::theGame->theBoard.Draw(Piece::BOTH);
-               while (true)
+               if (theGame->red->Valid())
                {
-                       SDL_Event  event;
-                       while (SDL_PollEvent(&event))
+                       theGame->logMessage("   Strange; RED still appears valid.\n");
+                       if (theGame->blue->Valid())
                        {
-                               switch (event.type)
-                               {
-                                       case SDL_QUIT:
-                                               exit(EXIT_SUCCESS);
-                                               break;
-                               }
-                       }                       
+                               theGame->logMessage("   BLUE also appears valid. Exiting with ERROR.\n");
+                               result = MovementResult::ERROR;
+                       }
+                       else
+                       {
+                               theGame->logMessage("BLUE is invalid. Wait for BLUE's turn to exit.\n");
+                               return;
+                       }
                }
        }
-       else
-       #endif //BUILD_GRAPHICS
+       if (theGame->turn == Piece::BLUE)
        {
-               if (theGame->log == stdout || theGame->log == stderr)
+               if (theGame->blue->Valid())
                {
-                       theGame->logMessage( "PRESS ENTER TO EXIT\n");
-                       theGame->theBoard.Print(theGame->log);
-                       while (fgetc(stdin) != '\n');
+                       theGame->logMessage("   Strange; BLUE still appears valid.\n");
+                       if (theGame->red->Valid())
+                       {
+                               theGame->logMessage("   RED also appears valid. Exiting with ERROR.\n");
+                               result = MovementResult::ERROR;
+                       }
+                       else
+                       {
+                               theGame->logMessage("RED is invalid. Wait for RED's turn to exit.\n");
+                               return;
+                       }
                }
        }
-       
 
+
+       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);
 }
 
@@ -401,6 +424,7 @@ void Game::PrintEndMessage(const MovementResult & result)
                        }
                        break;
 
+
        }
 
        if (printBoard)
@@ -472,10 +496,10 @@ MovementResult Game::Play()
        
        
 
-
+//     logMessage("Messaging red with \"START\"\n");
        red->Message("START");
        
-
+       int moveCount = 0;
 
        while (!Board::HaltResult(result) && (turnCount < maxTurns || maxTurns < 0))
        {
@@ -494,11 +518,20 @@ 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();
@@ -508,8 +541,6 @@ MovementResult Game::Play()
 
                logMessage( "%d RED: ", turnCount);
                result = red->MakeMove(buffer);
-               red->Message(buffer);
-               blue->Message(buffer);
                logMessage( "%s\n", buffer.c_str());
 
                if (!Board::HaltResult(result))
@@ -519,6 +550,12 @@ MovementResult Game::Play()
                if (Board::HaltResult(result))
                        break;
 
+               red->Message(buffer);
+               blue->Message(buffer);
+
+
+
+
                if (stallTime >= 0)
                        Wait(stallTime);
                else
@@ -533,16 +570,26 @@ 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();
@@ -552,8 +599,6 @@ MovementResult Game::Play()
 
                logMessage( "%d BLU: ", turnCount);
                result = blue->MakeMove(buffer);
-               blue->Message(buffer);
-               red->Message(buffer);
                logMessage( "%s\n", buffer.c_str());
 
                if (!Board::HaltResult(result))
@@ -563,6 +608,10 @@ MovementResult Game::Play()
                if (Board::HaltResult(result))
                        break;
 
+               red->Message(buffer);
+               blue->Message(buffer);
+
+
                if (theBoard.MobilePieces(Piece::RED) == 0)
                        result = MovementResult::DRAW;
 
@@ -580,7 +629,7 @@ MovementResult Game::Play()
                else
                        ReadUserCommand();
        
-               
+               ++moveCount;
 
                ++turnCount;
        }
@@ -751,4 +800,170 @@ int Game::Tokenise(std::vector<string> & buffer, std::string & str, char split)
        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();
+}
+
+
+void Game::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_FLAG:
+                       case MovementResult::VICTORY_ATTRITION: //It does not matter how you win, it just matters that you won!
+                               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();
+       
+
+}
 

UCC git Repository :: git.ucc.asn.au