7 Game* Game::theGame = NULL;
8 bool Game::gameCreated = false;
10 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)
15 fprintf(stderr, "Game::Game - Error - Tried to create more than one Game!\n");
20 signal(SIGPIPE, Game::HandleBrokenPipe);
24 if (graphicsEnabled && (!Graphics::Initialised()))
25 Graphics::Initialise("Stratego", theBoard.Width()*32, theBoard.Height()*32);
26 #endif //BUILD_GRAPHICS
30 MakeControllers(redPath, bluePath);
32 if (red == NULL || blue == NULL)
34 fprintf(stderr, "Game::Game - Error creating controller: ");
38 fprintf(stderr, " BOTH! (red: \"%s\", blue: \"%s\"\n", redPath, bluePath);
40 fprintf(stderr, " RED! (red: \"%s\")\n", redPath);
43 fprintf(stderr, "BLUE! (blue: \"%s\")\n", bluePath);
46 // logMessage("Game initialised.\n");
49 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)
54 fprintf(stderr, "Game::Game - Error - Tried to create more than one Game!\n");
59 signal(SIGPIPE, Game::HandleBrokenPipe);
62 if (graphicsEnabled && (!Graphics::Initialised()))
63 Graphics::Initialise("Stratego", theBoard.Width()*32, theBoard.Height()*32);
64 #endif //BUILD_GRAPHICS
66 input = fopen(fromFile, "r");
68 red = new FileController(Piece::RED, input);
69 blue = new FileController(Piece::BLUE, input);
80 if (log != NULL && log != stdout && log != stderr)
83 if (input != NULL && input != stdin)
88 * Attempts to setup the board and controllers
89 * @param redName the name of the red AI
90 * @param blueName the name of the blue AI
91 * @returns A colour, indicating if there were any errors
92 Piece::NONE indicates no errors
93 Piece::BOTH indicates errors with both AI
94 Piece::RED / Piece::BLUE indicates an error with only one of the two AI
96 Piece::Colour Game::Setup(const char * redName, const char * blueName)
101 logMessage("Controller for Player RED is invalid!\n");
102 if (!red->HumanController())
103 logMessage("Check that executable \"%s\" exists and has executable permissions set.\n", redName);
107 logMessage("Controller for Player BLUE is invalid!\n");
108 if (!blue->HumanController())
109 logMessage("Check that executable \"%s\" exists and has executable permissions set.\n", blueName);
117 else if (!blue->Valid())
122 for (int y = 4; y < 6; ++y)
124 for (int x = 2; x < 4; ++x)
126 theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE);
128 for (int x = 6; x < 8; ++x)
130 theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE);
135 MovementResult redSetup = red->Setup(blueName);
136 MovementResult blueSetup = blue->Setup(redName);
139 Piece::Colour result = Piece::NONE;
140 if (redSetup != MovementResult::OK)
142 if (blueSetup != MovementResult::OK)
144 logMessage("BOTH players give invalid setup!\n");
145 result = Piece::BOTH;
149 //logMessage("Player RED gave an invalid setup!\n");
154 else if (blueSetup != MovementResult::OK)
156 //logMessage("Player BLUE gave an invalid setup!\n");
157 result = Piece::BLUE;
161 logMessage("%s RED SETUP\n", red->name.c_str());
162 if (redSetup == MovementResult::OK)
164 for (int y=0; y < 4; ++y)
166 for (int x=0; x < theBoard.Width(); ++x)
168 if (theBoard.GetPiece(x, y) != NULL)
169 logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, y)->type)]);
178 logMessage("INVALID!\n");
181 logMessage("%s BLUE SETUP\n", blue->name.c_str());
182 if (blueSetup == MovementResult::OK)
184 for (int y=0; y < 4; ++y)
186 for (int x=0; x < theBoard.Width(); ++x)
188 if (theBoard.GetPiece(x, theBoard.Height()-4+y) != NULL)
189 logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, theBoard.Height()-4+y)->type)]);
198 logMessage("INVALID!\n");
206 void Game::Wait(double wait)
214 #ifdef BUILD_GRAPHICS
217 if (!graphicsEnabled)
219 usleep(1000000*wait); //Wait in seconds
223 TimerThread timer(wait*1000000); //Wait in seconds
225 while (!timer.Finished())
229 while (SDL_PollEvent(&event))
243 usleep(wait*1000000); //Wait in seconds
244 #endif //BUILD_GRAPHICS
248 void Game::HandleBrokenPipe(int sig)
252 fprintf(stderr, "ERROR - Recieved SIGPIPE during game exit!\n");
255 if (theGame->turn == Piece::RED)
257 theGame->logMessage("Game ends on RED's turn - REASON: ");
258 if (theGame->blue->Valid()) //Should probably check this
259 theGame->blue->Message("DEFAULT");
261 else if (theGame->turn == Piece::BLUE)
264 theGame->logMessage("Game ends on BLUE's turn - REASON: ");
265 if (theGame->red->Valid()) //Should probably check this
266 theGame->red->Message("DEFAULT");
270 theGame->logMessage("Game ends on ERROR's turn - REASON: ");
274 theGame->logMessage("SIGPIPE - Broken pipe (AI program no longer running)\n");
276 if (Game::theGame->printBoard)
277 Game::theGame->theBoard.PrintPretty(stdout, Piece::BOTH);
280 #ifdef BUILD_GRAPHICS
281 if (Game::theGame->graphicsEnabled && theGame->log == stdout)
283 theGame->logMessage("CLOSE WINDOW TO EXIT\n");
284 Game::theGame->theBoard.Draw(Piece::BOTH);
288 while (SDL_PollEvent(&event))
300 #endif //BUILD_GRAPHICS
302 if (theGame->log == stdout || theGame->log == stderr)
304 theGame->logMessage( "PRESS ENTER TO EXIT\n");
305 theGame->theBoard.Print(theGame->log);
306 while (fgetc(stdin) != '\n');
314 void Game::PrintEndMessage(const MovementResult & result)
318 logMessage("Game ends in the SETUP phase - REASON: ");
322 if (turn == Piece::RED)
324 logMessage("Game ends on RED's turn - REASON: ");
326 else if (turn == Piece::BLUE)
328 logMessage("Game ends on BLUE's turn - REASON: ");
332 logMessage("Game ends on ERROR's turn - REASON: ");
338 case MovementResult::OK:
339 logMessage("Status returned OK, unsure why game halted...\n");
341 case MovementResult::DIES:
342 logMessage("Status returned DIES, unsure why game halted...\n");
344 case MovementResult::KILLS:
345 logMessage("Status returned KILLS, unsure why game halted...\n");
347 case MovementResult::BOTH_DIE:
348 logMessage("Status returned BOTH_DIE, unsure why game halted...\n");
350 case MovementResult::NO_BOARD:
351 logMessage("Board does not exit?!\n");
353 case MovementResult::INVALID_POSITION:
354 logMessage("Coords outside board\n");
356 case MovementResult::NO_SELECTION:
357 logMessage("Move does not select a piece\n");
359 case MovementResult::NOT_YOUR_UNIT:
360 logMessage("Selected piece belongs to other player\n");
362 case MovementResult::IMMOBILE_UNIT:
363 logMessage("Selected piece is not mobile (FLAG or BOMB)\n");
365 case MovementResult::INVALID_DIRECTION:
366 logMessage("Selected unit cannot move that way\n");
368 case MovementResult::POSITION_FULL:
369 logMessage("Attempted move into square occupied by neutral or allied piece\n");
371 case MovementResult::VICTORY_FLAG:
372 logMessage("Captured the flag\n");
374 case MovementResult::VICTORY_ATTRITION:
375 logMessage("Destroyed all mobile enemy pieces\n");
377 case MovementResult::BAD_RESPONSE:
378 logMessage("Unintelligable response\n");
380 case MovementResult::NO_MOVE:
381 logMessage("Response timeout after %2f seconds.\n", timeoutTime);
383 case MovementResult::COLOUR_ERROR:
384 logMessage("Internal controller error - COLOUR_ERROR\n");
386 case MovementResult::ERROR:
387 logMessage("Internal controller error - Unspecified ERROR\n");
389 case MovementResult::DRAW_DEFAULT:
390 logMessage("Game declared a draw after %d turns\n", turnCount);
392 case MovementResult::DRAW:
393 logMessage("Game declared a draw because neither player has mobile pieces\n");
395 case MovementResult::SURRENDER:
396 logMessage("This player has surrendered!\n");
398 case MovementResult::BAD_SETUP:
402 logMessage("An illegal setup was made by RED\n");
405 logMessage("An illegal setup was made by BLUE\n");
408 logMessage("An illegal setup was made by BOTH players\n");
411 logMessage("Unknown internal error.\n");
421 fprintf(stdout, "%d Final State\n", turnCount);
422 theBoard.PrintPretty(stdout, Piece::BOTH);
423 fprintf(stdout, "\n");
426 #ifdef BUILD_GRAPHICS
427 if (graphicsEnabled && log == stdout)
429 logMessage("CLOSE WINDOW TO EXIT\n");
430 theBoard.Draw(Piece::BOTH);
434 while (SDL_PollEvent(&event))
446 #endif //BUILD_GRAPHICS
450 logMessage("PRESS ENTER TO EXIT\n");
451 while (fgetc(stdin) != '\n');
452 exit(EXIT_SUCCESS); //Might want to actually exit, you foolish fool
457 /** Checks for victory by attrition (destroying all mobile pieces)
459 * @returns OK for no victory,
460 * DRAW if both players have no pieces, or
461 * VICTORY_ATTRITION if the current player has won by attrition
463 MovementResult Game::CheckVictoryAttrition()
465 if (theBoard.MobilePieces(Piece::OppositeColour(turn)) == 0)
467 if (theBoard.MobilePieces(turn) == 0)
468 return MovementResult::DRAW;
470 return MovementResult::VICTORY_ATTRITION;
472 return MovementResult::OK;
475 MovementResult Game::Play()
478 MovementResult result = MovementResult::OK;
482 Piece::Colour toReveal = reveal;
487 // logMessage("Messaging red with \"START\"\n");
488 red->Message("START");
492 while (!Board::HaltResult(result) && (turnCount < maxTurns || maxTurns < 0))
494 if (red->HumanController() && blue->HumanController())
495 toReveal = Piece::RED;
500 fprintf(stdout, "START:\n");
502 fprintf(stdout, "%d BLUE:\n", turnCount);
503 theBoard.PrintPretty(stdout, toReveal);
504 fprintf(stdout, "\n\n");
507 #ifdef BUILD_GRAPHICS
510 theBoard.Draw(toReveal);
511 if (imageOutput != "")
513 string imageFile = "" + imageOutput + "/"+ itostr(moveCount) + ".bmp";
514 Graphics::ScreenShot(imageFile.c_str());
518 #endif //BUILD_GRAPHICS
523 if (!Board::HaltResult(result))
525 result = CheckVictoryAttrition();
527 if (Board::HaltResult(result))
530 logMessage( "%d RED: ", turnCount);
531 result = red->MakeMove(buffer);
532 red->Message(buffer);
533 blue->Message(buffer);
534 logMessage( "%s\n", buffer.c_str());
536 if (!Board::HaltResult(result))
538 result = CheckVictoryAttrition();
540 if (Board::HaltResult(result))
548 if (blue->HumanController() && red->HumanController())
549 toReveal = Piece::BLUE;
553 fprintf(stdout, "%d RED:\n", turnCount);
554 theBoard.PrintPretty(stdout, toReveal);
555 fprintf(stdout, "\n\n");
560 #ifdef BUILD_GRAPHICS
563 theBoard.Draw(toReveal);
564 if (imageOutput != "")
566 string imageFile = "" + imageOutput + "/" + itostr(moveCount) + ".bmp";
567 Graphics::ScreenShot(imageFile.c_str());
570 #endif //BUILD_GRAPHICS
577 if (!Board::HaltResult(result))
579 result = CheckVictoryAttrition();
581 if (Board::HaltResult(result))
584 logMessage( "%d BLU: ", turnCount);
585 result = blue->MakeMove(buffer);
586 blue->Message(buffer);
587 red->Message(buffer);
588 logMessage( "%s\n", buffer.c_str());
590 if (!Board::HaltResult(result))
592 result = CheckVictoryAttrition();
594 if (Board::HaltResult(result))
597 if (theBoard.MobilePieces(Piece::RED) == 0)
598 result = MovementResult::DRAW;
600 if (theBoard.MobilePieces(Piece::RED) == 0)
602 if (theBoard.MobilePieces(Piece::BLUE) == 0)
603 result = MovementResult::DRAW;
605 result = MovementResult::VICTORY_ATTRITION;
619 if ((maxTurns >= 0 && turnCount >= maxTurns) && result == MovementResult::OK)
621 result = MovementResult::DRAW_DEFAULT;
632 * Logs a message to the game's log file if it exists
633 * @param format the format string
634 * @param additional parameters - printed using va_args
635 * @returns the result of vfprintf or a negative number if the log file does not exist
637 int Game::logMessage(const char * format, ...)
642 va_start(ap, format);
644 int result = vfprintf(log, format, ap);
651 * Waits for a user command
652 * Currently ignores the command.
654 void Game::ReadUserCommand()
656 fprintf(stdout, "Waiting for user to press enter... (type QUIT to exit)\n");
658 for (char c = fgetc(stdin); c != '\n' && (int)(c) != EOF; c = fgetc(stdin))
663 if (command == "QUIT")
665 fprintf(stdout, "Ordered to quit... exiting...\n");
670 MovementResult FileController::QuerySetup(const char * opponentName, std::string setup[])
673 char c = fgetc(file);
681 while (fgetc(file) != '\n');
683 for (int y = 0; y < 4; ++y)
686 for (int x = 0; x < Game::theGame->theBoard.Width(); ++x)
688 setup[y] += fgetc(file);
691 if (fgetc(file) != '\n')
693 return MovementResult::BAD_RESPONSE;
696 return MovementResult::OK;
701 MovementResult FileController::QueryMove(std::string & buffer)
703 //This bit is kind of hacky and terrible, and yes I am mixing C with C++
704 //Yes I should have used fstream for the whole thing and it would be much easier.
709 fgets(buf, sizeof(buf), file);
710 char * s = (char*)(buf);
711 while (*s != ':' && *s != '\0')
714 //Move forward to the start of the move information
715 for (int i=0; i < 2; ++i)
717 if (*s != '\0' && *s != '\n')
721 //Unfortunately we can't just copy the whole line
723 //We have to remove the movement result tokens
726 vector<string> tokens;
727 Game::Tokenise(tokens, buffer, ' ');
730 if (tokens.size() < 1)
731 return MovementResult::BAD_RESPONSE;
735 if (tokens[0] == "NO_MOVE") //tokens[0] is either the x coordinate, or "NO_MOVE"
736 return MovementResult::OK;
737 if (tokens.size() < 2)
738 return MovementResult::BAD_RESPONSE;
740 buffer += tokens[1]; //The y coordinate
742 buffer += tokens[2]; //The direction
744 //Check for a possible multiplier. If tokens[3] is an integer it will be the multiplier, otherwise it won't be.
745 if (tokens.size() > 3 && atoi(tokens[3].c_str()) != 0)
752 //(tokens[3] should include a new line)
761 return MovementResult::OK;
767 int Game::Tokenise(std::vector<string> & buffer, std::string & str, char split)
770 for (unsigned int x = 0; x < str.size(); ++x)
772 if (str[x] == split && token.size() > 0)
774 buffer.push_back(token);
780 if (token.size() > 0)
781 buffer.push_back(token);
782 return buffer.size();
786 * Creates Controller baseds off strings. Takes into account controllers other than AI_Controller.
787 * @param redPath - Either the path to an AI_Controller compatable executable, or one of %human or %network or %network:[IP_ADDRESS]
788 * @param bluePath - Ditto
789 * Sets this->red to a controller using redPath, and this->blue to a controller using bluePath
790 * TODO: Make nicer (this function should be ~half the length)
792 void Game::MakeControllers(const char * redPath, const char * bluePath)
794 Network * redNetwork = NULL;
795 Network * blueNetwork = NULL;
796 //To allow for running two network controllers (I don't know why you would, but beside the point...) use two ports
797 static const int port1 = 4560;
798 static const int port2 = 4561;
800 if (redPath[0] == '@')
802 if (strcmp(redPath, "@human") == 0)
803 red = new Human_Controller(Piece::RED, graphicsEnabled);
806 const char * network = strstr(redPath, "@network");
812 network = strstr(network, ":");
816 logMessage("Creating server for red AI... ");
817 redNetwork = new Server(port1);
818 logMessage("Successful!\n");
823 network = (const char*)(network+1);
824 logMessage("Creating client for red AI... ");
825 redNetwork = new Client(network, port2);
826 logMessage("Connected to address %s\n", network);
829 logMessage(" (Red's responses will be received over the connection)\n");
830 red = new NetworkReceiver(Piece::RED, redNetwork);
834 red = new AI_Controller(Piece::RED, redPath, timeoutTime);
836 if (bluePath[0] == '@')
838 if (strcmp(bluePath, "@human") == 0)
839 blue = new Human_Controller(Piece::BLUE, graphicsEnabled);
842 const char * network = strstr(bluePath, "@network");
848 network = strstr(network, ":");
852 logMessage("Creating server for blue AI... ");
853 blueNetwork = new Server(port2);
854 logMessage("Successful!\n");
859 network = (const char*)(network+1);
860 logMessage("Creating client for blue AI... ");
861 blueNetwork = new Client(network, port1);
862 logMessage("Connected to address %s\n", network);
864 logMessage(" (Blue's responses will be received over the connection)\n");
865 blue = new NetworkReceiver(Piece::BLUE, blueNetwork);
869 blue = new AI_Controller(Piece::BLUE, bluePath, timeoutTime);
871 if (redNetwork != NULL)
874 blue = new NetworkSender(Piece::BLUE,blue, redNetwork);
875 logMessage(" (Blue's responses will be copied over the connection)\n");
877 if (blueNetwork != NULL)
880 red = new NetworkSender(Piece::RED, red, blueNetwork);
881 logMessage(" (Red's responses will be copied over the connection)\n");
884 red->FixName(); blue->FixName();