9 Game* Game::theGame = NULL;
10 bool Game::gameCreated = false;
12 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)
17 fprintf(stderr, "Game::Game - Error - Tried to create more than one Game!\n");
22 signal(SIGPIPE, Game::HandleBrokenPipe);
26 if (graphicsEnabled && (!Graphics::Initialised()))
28 string s = "Stratego: ";
31 s += string(bluePath);
32 Graphics::Initialise(s.c_str(), theBoard.Width()*GRID_SIZE, theBoard.Height()*GRID_SIZE);
34 #endif //BUILD_GRAPHICS
38 MakeControllers(redPath, bluePath);
40 if (red == NULL || blue == NULL)
42 fprintf(stderr, "Game::Game - Error creating controller: ");
46 fprintf(stderr, " BOTH! (red: \"%s\", blue: \"%s\"\n", redPath, bluePath);
48 fprintf(stderr, " RED! (red: \"%s\")\n", redPath);
51 fprintf(stderr, "BLUE! (blue: \"%s\")\n", bluePath);
54 // logMessage("Game initialised.\n");
57 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)
62 fprintf(stderr, "Game::Game - Error - Tried to create more than one Game!\n");
67 signal(SIGPIPE, Game::HandleBrokenPipe);
70 if (graphicsEnabled && (!Graphics::Initialised()))
72 string s = "Stratego: (file) ";
73 s += string(fromFile);
74 Graphics::Initialise(s.c_str(), theBoard.Width()*GRID_SIZE, theBoard.Height()*GRID_SIZE);
76 #endif //BUILD_GRAPHICS
78 input = fopen(fromFile, "r");
80 red = new FileController(Piece::RED, input);
81 blue = new FileController(Piece::BLUE, input);
92 if (log != NULL && log != stdout && log != stderr)
95 if (input != NULL && input != stdin)
100 * Attempts to setup the board and controllers
101 * @param redName the name of the red AI
102 * @param blueName the name of the blue AI
103 * @returns A colour, indicating if there were any errors
104 Piece::NONE indicates no errors
105 Piece::BOTH indicates errors with both AI
106 Piece::RED / Piece::BLUE indicates an error with only one of the two AI
108 Piece::Colour Game::Setup(const char * redName, const char * blueName)
113 logMessage("Controller for Player RED is invalid!\n");
114 if (!red->HumanController())
115 logMessage("Check that executable \"%s\" exists and has executable permissions set.\n", redName);
119 logMessage("Controller for Player BLUE is invalid!\n");
120 if (!blue->HumanController())
121 logMessage("Check that executable \"%s\" exists and has executable permissions set.\n", blueName);
129 else if (!blue->Valid())
134 for (int y = 4; y < 6; ++y)
136 for (int x = 2; x < 4; ++x)
138 theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE);
140 for (int x = 6; x < 8; ++x)
142 theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE);
147 MovementResult redSetup = red->Setup(blueName);
148 MovementResult blueSetup = blue->Setup(redName);
151 Piece::Colour result = Piece::NONE;
152 if (redSetup != MovementResult::OK)
154 if (blueSetup != MovementResult::OK)
156 logMessage("BOTH players give invalid setup!\n");
157 result = Piece::BOTH;
161 //logMessage("Player RED gave an invalid setup!\n");
166 else if (blueSetup != MovementResult::OK)
168 //logMessage("Player BLUE gave an invalid setup!\n");
169 result = Piece::BLUE;
173 logMessage("%s RED SETUP\n", red->name.c_str());
174 if (redSetup == MovementResult::OK)
176 for (int y=0; y < 4; ++y)
178 for (int x=0; x < theBoard.Width(); ++x)
180 if (theBoard.GetPiece(x, y) != NULL)
181 logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, y)->type)]);
190 logMessage("INVALID!\n");
193 logMessage("%s BLUE SETUP\n", blue->name.c_str());
194 if (blueSetup == MovementResult::OK)
196 for (int y=0; y < 4; ++y)
198 for (int x=0; x < theBoard.Width(); ++x)
200 if (theBoard.GetPiece(x, theBoard.Height()-4+y) != NULL)
201 logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, theBoard.Height()-4+y)->type)]);
210 logMessage("INVALID!\n");
218 void Game::Wait(double wait)
226 #ifdef BUILD_GRAPHICS
229 if (!graphicsEnabled)
231 usleep(1000000*wait); //Wait in seconds
235 TimerThread timer(wait*1000000); //Wait in seconds
237 while (!timer.Finished())
241 while (SDL_PollEvent(&event))
255 usleep(wait*1000000); //Wait in seconds
256 #endif //BUILD_GRAPHICS
260 void Game::HandleBrokenPipe(int sig)
264 fprintf(stderr, "ERROR - Recieved SIGPIPE during game exit!\n");
267 if (theGame->turn == Piece::RED)
269 theGame->logMessage("Game ends on RED's turn - REASON: ");
270 if (theGame->blue->Valid()) //Should probably check this
271 theGame->blue->Message("DEFAULT");
273 else if (theGame->turn == Piece::BLUE)
276 theGame->logMessage("Game ends on BLUE's turn - REASON: ");
277 if (theGame->red->Valid()) //Should probably check this
278 theGame->red->Message("DEFAULT");
282 theGame->logMessage("Game ends on ERROR's turn - REASON: ");
286 theGame->logMessage("SIGPIPE - Broken pipe (AI program no longer running)\n");
288 if (Game::theGame->printBoard)
289 Game::theGame->theBoard.PrintPretty(stdout, Piece::BOTH);
292 #ifdef BUILD_GRAPHICS
293 if (Game::theGame->graphicsEnabled && theGame->log == stdout)
295 theGame->logMessage("CLOSE WINDOW TO EXIT\n");
296 Game::theGame->theBoard.Draw(Piece::BOTH);
300 while (SDL_PollEvent(&event))
312 #endif //BUILD_GRAPHICS
314 if (theGame->log == stdout || theGame->log == stderr)
316 theGame->logMessage( "PRESS ENTER TO EXIT\n");
317 theGame->theBoard.Print(theGame->log);
318 while (fgetc(stdin) != '\n');
326 void Game::PrintEndMessage(const MovementResult & result)
330 logMessage("Game ends in the SETUP phase - REASON: ");
334 if (turn == Piece::RED)
336 logMessage("Game ends on RED's turn - REASON: ");
338 else if (turn == Piece::BLUE)
340 logMessage("Game ends on BLUE's turn - REASON: ");
344 logMessage("Game ends on ERROR's turn - REASON: ");
350 case MovementResult::OK:
351 logMessage("Status returned OK, unsure why game halted...\n");
353 case MovementResult::DIES:
354 logMessage("Status returned DIES, unsure why game halted...\n");
356 case MovementResult::KILLS:
357 logMessage("Status returned KILLS, unsure why game halted...\n");
359 case MovementResult::BOTH_DIE:
360 logMessage("Status returned BOTH_DIE, unsure why game halted...\n");
362 case MovementResult::NO_BOARD:
363 logMessage("Board does not exit?!\n");
365 case MovementResult::INVALID_POSITION:
366 logMessage("Coords outside board\n");
368 case MovementResult::NO_SELECTION:
369 logMessage("Move does not select a piece\n");
371 case MovementResult::NOT_YOUR_UNIT:
372 logMessage("Selected piece belongs to other player\n");
374 case MovementResult::IMMOBILE_UNIT:
375 logMessage("Selected piece is not mobile (FLAG or BOMB)\n");
377 case MovementResult::INVALID_DIRECTION:
378 logMessage("Selected unit cannot move that way\n");
380 case MovementResult::POSITION_FULL:
381 logMessage("Attempted move into square occupied by neutral or allied piece\n");
383 case MovementResult::VICTORY_FLAG:
384 logMessage("Captured the flag\n");
386 case MovementResult::VICTORY_ATTRITION:
387 logMessage("Destroyed all mobile enemy pieces\n");
389 case MovementResult::BAD_RESPONSE:
390 logMessage("Unintelligable response\n");
392 case MovementResult::NO_MOVE:
393 logMessage("Response timeout after %2f seconds.\n", timeoutTime);
395 case MovementResult::COLOUR_ERROR:
396 logMessage("Internal controller error - COLOUR_ERROR\n");
398 case MovementResult::ERROR:
399 logMessage("Internal controller error - Unspecified ERROR\n");
401 case MovementResult::DRAW_DEFAULT:
402 logMessage("Game declared a draw after %d turns\n", turnCount);
404 case MovementResult::DRAW:
405 logMessage("Game declared a draw because neither player has mobile pieces\n");
407 case MovementResult::SURRENDER:
408 logMessage("This player has surrendered!\n");
410 case MovementResult::BAD_SETUP:
414 logMessage("An illegal setup was made by RED\n");
417 logMessage("An illegal setup was made by BLUE\n");
420 logMessage("An illegal setup was made by BOTH players\n");
423 logMessage("Unknown internal error.\n");
433 fprintf(stdout, "%d Final State\n", turnCount);
434 theBoard.PrintPretty(stdout, Piece::BOTH);
435 fprintf(stdout, "\n");
438 #ifdef BUILD_GRAPHICS
439 if (graphicsEnabled && log == stdout)
441 logMessage("CLOSE WINDOW TO EXIT\n");
442 theBoard.Draw(Piece::BOTH);
446 while (SDL_PollEvent(&event))
458 #endif //BUILD_GRAPHICS
462 logMessage("PRESS ENTER TO EXIT\n");
463 while (fgetc(stdin) != '\n');
464 exit(EXIT_SUCCESS); //Might want to actually exit, you foolish fool
469 /** Checks for victory by attrition (destroying all mobile pieces)
471 * @returns OK for no victory,
472 * DRAW if both players have no pieces, or
473 * VICTORY_ATTRITION if the current player has won by attrition
475 MovementResult Game::CheckVictoryAttrition()
477 if (theBoard.MobilePieces(Piece::OppositeColour(turn)) == 0)
479 if (theBoard.MobilePieces(turn) == 0)
480 return MovementResult::DRAW;
482 return MovementResult::VICTORY_ATTRITION;
484 return MovementResult::OK;
487 MovementResult Game::Play()
490 MovementResult result = MovementResult::OK;
494 Piece::Colour toReveal = reveal;
499 // logMessage("Messaging red with \"START\"\n");
500 red->Message("START");
504 while (!Board::HaltResult(result) && (turnCount < maxTurns || maxTurns < 0))
506 if (red->HumanController() && blue->HumanController())
507 toReveal = Piece::RED;
512 fprintf(stdout, "START:\n");
514 fprintf(stdout, "%d BLUE:\n", turnCount);
515 theBoard.PrintPretty(stdout, toReveal);
516 fprintf(stdout, "\n\n");
519 #ifdef BUILD_GRAPHICS
522 theBoard.Draw(toReveal);
523 if (imageOutput != "")
525 string imageFile = "" + imageOutput + "/"+ itostr(moveCount) + ".bmp";
526 Graphics::ScreenShot(imageFile.c_str());
530 #endif //BUILD_GRAPHICS
535 if (!Board::HaltResult(result))
537 result = CheckVictoryAttrition();
539 if (Board::HaltResult(result))
542 logMessage( "%d RED: ", turnCount);
543 result = red->MakeMove(buffer);
544 red->Message(buffer);
545 blue->Message(buffer);
546 logMessage( "%s\n", buffer.c_str());
548 if (!Board::HaltResult(result))
550 result = CheckVictoryAttrition();
552 if (Board::HaltResult(result))
560 if (blue->HumanController() && red->HumanController())
561 toReveal = Piece::BLUE;
565 fprintf(stdout, "%d RED:\n", turnCount);
566 theBoard.PrintPretty(stdout, toReveal);
567 fprintf(stdout, "\n\n");
572 #ifdef BUILD_GRAPHICS
575 theBoard.Draw(toReveal);
576 if (imageOutput != "")
578 string imageFile = "" + imageOutput + "/" + itostr(moveCount) + ".bmp";
579 Graphics::ScreenShot(imageFile.c_str());
582 #endif //BUILD_GRAPHICS
589 if (!Board::HaltResult(result))
591 result = CheckVictoryAttrition();
593 if (Board::HaltResult(result))
596 logMessage( "%d BLU: ", turnCount);
597 result = blue->MakeMove(buffer);
598 blue->Message(buffer);
599 red->Message(buffer);
600 logMessage( "%s\n", buffer.c_str());
602 if (!Board::HaltResult(result))
604 result = CheckVictoryAttrition();
606 if (Board::HaltResult(result))
609 if (theBoard.MobilePieces(Piece::RED) == 0)
610 result = MovementResult::DRAW;
612 if (theBoard.MobilePieces(Piece::RED) == 0)
614 if (theBoard.MobilePieces(Piece::BLUE) == 0)
615 result = MovementResult::DRAW;
617 result = MovementResult::VICTORY_ATTRITION;
631 if ((maxTurns >= 0 && turnCount >= maxTurns) && result == MovementResult::OK)
633 result = MovementResult::DRAW_DEFAULT;
644 * Logs a message to the game's log file if it exists
645 * @param format the format string
646 * @param additional parameters - printed using va_args
647 * @returns the result of vfprintf or a negative number if the log file does not exist
649 int Game::logMessage(const char * format, ...)
654 va_start(ap, format);
656 int result = vfprintf(log, format, ap);
663 * Waits for a user command
664 * Currently ignores the command.
666 void Game::ReadUserCommand()
668 fprintf(stdout, "Waiting for user to press enter... (type QUIT to exit)\n");
670 for (char c = fgetc(stdin); c != '\n' && (int)(c) != EOF; c = fgetc(stdin))
675 if (command == "QUIT")
677 fprintf(stdout, "Ordered to quit... exiting...\n");
682 MovementResult FileController::QuerySetup(const char * opponentName, std::string setup[])
685 char c = fgetc(file);
693 while (fgetc(file) != '\n');
695 for (int y = 0; y < 4; ++y)
698 for (int x = 0; x < Game::theGame->theBoard.Width(); ++x)
700 setup[y] += fgetc(file);
703 if (fgetc(file) != '\n')
705 return MovementResult::BAD_RESPONSE;
708 return MovementResult::OK;
713 MovementResult FileController::QueryMove(std::string & buffer)
715 //This bit is kind of hacky and terrible, and yes I am mixing C with C++
716 //Yes I should have used fstream for the whole thing and it would be much easier.
721 fgets(buf, sizeof(buf), file);
722 char * s = (char*)(buf);
723 while (*s != ':' && *s != '\0')
726 //Move forward to the start of the move information
727 for (int i=0; i < 2; ++i)
729 if (*s != '\0' && *s != '\n')
733 //Unfortunately we can't just copy the whole line
735 //We have to remove the movement result tokens
738 vector<string> tokens;
739 Game::Tokenise(tokens, buffer, ' ');
742 if (tokens.size() < 1)
743 return MovementResult::BAD_RESPONSE;
747 if (tokens[0] == "NO_MOVE") //tokens[0] is either the x coordinate, or "NO_MOVE"
748 return MovementResult::OK;
749 if (tokens.size() < 2)
750 return MovementResult::BAD_RESPONSE;
752 buffer += tokens[1]; //The y coordinate
754 buffer += tokens[2]; //The direction
756 //Check for a possible multiplier. If tokens[3] is an integer it will be the multiplier, otherwise it won't be.
757 if (tokens.size() > 3 && atoi(tokens[3].c_str()) != 0)
764 //(tokens[3] should include a new line)
773 return MovementResult::OK;
779 int Game::Tokenise(std::vector<string> & buffer, std::string & str, char split)
782 for (unsigned int x = 0; x < str.size(); ++x)
784 if (str[x] == split && token.size() > 0)
786 buffer.push_back(token);
792 if (token.size() > 0)
793 buffer.push_back(token);
794 return buffer.size();
798 * Creates Controller baseds off strings. Takes into account controllers other than AI_Controller.
799 * @param redPath - Either the path to an AI_Controller compatable executable, or one of %human or %network or %network:[IP_ADDRESS]
800 * @param bluePath - Ditto
801 * Sets this->red to a controller using redPath, and this->blue to a controller using bluePath
802 * TODO: Make nicer (this function should be ~half the length)
804 void Game::MakeControllers(const char * redPath, const char * bluePath)
806 Network * redNetwork = NULL;
807 Network * blueNetwork = NULL;
808 //To allow for running two network controllers (I don't know why you would, but beside the point...) use two ports
809 static const int port1 = 4560;
810 static const int port2 = 4561;
812 if (redPath[0] == '@')
814 if (strcmp(redPath, "@human") == 0)
815 red = new Human_Controller(Piece::RED, graphicsEnabled);
818 const char * network = strstr(redPath, "@network");
824 network = strstr(network, ":");
828 logMessage("Creating server for red AI... ");
829 redNetwork = new Server(port1);
830 logMessage("Successful!\n");
835 network = (const char*)(network+1);
836 logMessage("Creating client for red AI... ");
837 redNetwork = new Client(network, port2);
838 logMessage("Connected to address %s\n", network);
841 logMessage(" (Red's responses will be received over the connection)\n");
842 red = new NetworkReceiver(Piece::RED, redNetwork);
846 red = new AI_Controller(Piece::RED, redPath, timeoutTime);
848 if (bluePath[0] == '@')
850 if (strcmp(bluePath, "@human") == 0)
851 blue = new Human_Controller(Piece::BLUE, graphicsEnabled);
854 const char * network = strstr(bluePath, "@network");
860 network = strstr(network, ":");
864 logMessage("Creating server for blue AI... ");
865 blueNetwork = new Server(port2);
866 logMessage("Successful!\n");
871 network = (const char*)(network+1);
872 logMessage("Creating client for blue AI... ");
873 blueNetwork = new Client(network, port1);
874 logMessage("Connected to address %s\n", network);
876 logMessage(" (Blue's responses will be received over the connection)\n");
877 blue = new NetworkReceiver(Piece::BLUE, blueNetwork);
881 blue = new AI_Controller(Piece::BLUE, bluePath, timeoutTime);
883 if (redNetwork != NULL)
886 blue = new NetworkSender(Piece::BLUE,blue, redNetwork);
887 logMessage(" (Blue's responses will be copied over the connection)\n");
889 if (blueNetwork != NULL)
892 red = new NetworkSender(Piece::RED, red, blueNetwork);
893 logMessage(" (Red's responses will be copied over the connection)\n");
896 red->FixName(); blue->FixName();