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");
268 theGame->logMessage("SIGPIPE - Broken pipe (AI program no longer running)\n");
270 MovementResult result = MovementResult::BAD_RESPONSE;
272 if (theGame->turn == Piece::RED)
274 if (theGame->red->Valid())
276 theGame->logMessage(" Strange; RED still appears valid.\n");
277 if (theGame->blue->Valid())
279 theGame->logMessage(" BLUE also appears valid. Exiting with ERROR.\n");
280 result = MovementResult::ERROR;
284 theGame->logMessage("BLUE is invalid. Wait for BLUE's turn to exit.\n");
289 if (theGame->turn == Piece::BLUE)
291 if (theGame->blue->Valid())
293 theGame->logMessage(" Strange; BLUE still appears valid.\n");
294 if (theGame->red->Valid())
296 theGame->logMessage(" RED also appears valid. Exiting with ERROR.\n");
297 result = MovementResult::ERROR;
301 theGame->logMessage("RED is invalid. Wait for RED's turn to exit.\n");
308 Game::theGame->PrintEndMessage(result);
311 PrintResults(result, buffer);
313 //Message the AI's the quit message
314 Game::theGame->red->Message("QUIT " + buffer);
315 Game::theGame->blue->Message("QUIT " + buffer);
318 if (Game::theGame->GetLogFile() != stdout)
319 Game::theGame->logMessage("%s\n", buffer.c_str());
321 fprintf(stdout, "%s\n", buffer.c_str());
325 void Game::PrintEndMessage(const MovementResult & result)
329 logMessage("Game ends in the SETUP phase - REASON: ");
333 if (turn == Piece::RED)
335 logMessage("Game ends on RED's turn - REASON: ");
337 else if (turn == Piece::BLUE)
339 logMessage("Game ends on BLUE's turn - REASON: ");
343 logMessage("Game ends on ERROR's turn - REASON: ");
349 case MovementResult::OK:
350 logMessage("Status returned OK, unsure why game halted...\n");
352 case MovementResult::DIES:
353 logMessage("Status returned DIES, unsure why game halted...\n");
355 case MovementResult::KILLS:
356 logMessage("Status returned KILLS, unsure why game halted...\n");
358 case MovementResult::BOTH_DIE:
359 logMessage("Status returned BOTH_DIE, unsure why game halted...\n");
361 case MovementResult::NO_BOARD:
362 logMessage("Board does not exit?!\n");
364 case MovementResult::INVALID_POSITION:
365 logMessage("Coords outside board\n");
367 case MovementResult::NO_SELECTION:
368 logMessage("Move does not select a piece\n");
370 case MovementResult::NOT_YOUR_UNIT:
371 logMessage("Selected piece belongs to other player\n");
373 case MovementResult::IMMOBILE_UNIT:
374 logMessage("Selected piece is not mobile (FLAG or BOMB)\n");
376 case MovementResult::INVALID_DIRECTION:
377 logMessage("Selected unit cannot move that way\n");
379 case MovementResult::POSITION_FULL:
380 logMessage("Attempted move into square occupied by neutral or allied piece\n");
382 case MovementResult::VICTORY_FLAG:
383 logMessage("Captured the flag\n");
385 case MovementResult::VICTORY_ATTRITION:
386 logMessage("Destroyed all mobile enemy pieces\n");
388 case MovementResult::BAD_RESPONSE:
389 logMessage("Unintelligable response\n");
391 case MovementResult::NO_MOVE:
392 logMessage("Response timeout after %2f seconds.\n", timeoutTime);
394 case MovementResult::COLOUR_ERROR:
395 logMessage("Internal controller error - COLOUR_ERROR\n");
397 case MovementResult::ERROR:
398 logMessage("Internal controller error - Unspecified ERROR\n");
400 case MovementResult::DRAW_DEFAULT:
401 logMessage("Game declared a draw after %d turns\n", turnCount);
403 case MovementResult::DRAW:
404 logMessage("Game declared a draw because neither player has mobile pieces\n");
406 case MovementResult::SURRENDER:
407 logMessage("This player has surrendered!\n");
409 case MovementResult::BAD_SETUP:
413 logMessage("An illegal setup was made by RED\n");
416 logMessage("An illegal setup was made by BLUE\n");
419 logMessage("An illegal setup was made by BOTH players\n");
422 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 logMessage( "%s\n", buffer.c_str());
546 if (!Board::HaltResult(result))
548 result = CheckVictoryAttrition();
550 if (Board::HaltResult(result))
553 red->Message(buffer);
554 blue->Message(buffer);
564 if (blue->HumanController() && red->HumanController())
565 toReveal = Piece::BLUE;
569 fprintf(stdout, "%d RED:\n", turnCount);
570 theBoard.PrintPretty(stdout, toReveal);
571 fprintf(stdout, "\n\n");
576 #ifdef BUILD_GRAPHICS
579 theBoard.Draw(toReveal);
580 if (imageOutput != "")
582 string imageFile = "" + imageOutput + "/" + itostr(moveCount) + ".bmp";
583 Graphics::ScreenShot(imageFile.c_str());
586 #endif //BUILD_GRAPHICS
593 if (!Board::HaltResult(result))
595 result = CheckVictoryAttrition();
597 if (Board::HaltResult(result))
600 logMessage( "%d BLU: ", turnCount);
601 result = blue->MakeMove(buffer);
602 logMessage( "%s\n", buffer.c_str());
604 if (!Board::HaltResult(result))
606 result = CheckVictoryAttrition();
608 if (Board::HaltResult(result))
611 red->Message(buffer);
612 blue->Message(buffer);
615 if (theBoard.MobilePieces(Piece::RED) == 0)
616 result = MovementResult::DRAW;
618 if (theBoard.MobilePieces(Piece::RED) == 0)
620 if (theBoard.MobilePieces(Piece::BLUE) == 0)
621 result = MovementResult::DRAW;
623 result = MovementResult::VICTORY_ATTRITION;
637 if ((maxTurns >= 0 && turnCount >= maxTurns) && result == MovementResult::OK)
639 result = MovementResult::DRAW_DEFAULT;
650 * Logs a message to the game's log file if it exists
651 * @param format the format string
652 * @param additional parameters - printed using va_args
653 * @returns the result of vfprintf or a negative number if the log file does not exist
655 int Game::logMessage(const char * format, ...)
660 va_start(ap, format);
662 int result = vfprintf(log, format, ap);
669 * Waits for a user command
670 * Currently ignores the command.
672 void Game::ReadUserCommand()
674 fprintf(stdout, "Waiting for user to press enter... (type QUIT to exit)\n");
676 for (char c = fgetc(stdin); c != '\n' && (int)(c) != EOF; c = fgetc(stdin))
681 if (command == "QUIT")
683 fprintf(stdout, "Ordered to quit... exiting...\n");
688 MovementResult FileController::QuerySetup(const char * opponentName, std::string setup[])
691 char c = fgetc(file);
699 while (fgetc(file) != '\n');
701 for (int y = 0; y < 4; ++y)
704 for (int x = 0; x < Game::theGame->theBoard.Width(); ++x)
706 setup[y] += fgetc(file);
709 if (fgetc(file) != '\n')
711 return MovementResult::BAD_RESPONSE;
714 return MovementResult::OK;
719 MovementResult FileController::QueryMove(std::string & buffer)
721 //This bit is kind of hacky and terrible, and yes I am mixing C with C++
722 //Yes I should have used fstream for the whole thing and it would be much easier.
727 fgets(buf, sizeof(buf), file);
728 char * s = (char*)(buf);
729 while (*s != ':' && *s != '\0')
732 //Move forward to the start of the move information
733 for (int i=0; i < 2; ++i)
735 if (*s != '\0' && *s != '\n')
739 //Unfortunately we can't just copy the whole line
741 //We have to remove the movement result tokens
744 vector<string> tokens;
745 Game::Tokenise(tokens, buffer, ' ');
748 if (tokens.size() < 1)
749 return MovementResult::BAD_RESPONSE;
753 if (tokens[0] == "NO_MOVE") //tokens[0] is either the x coordinate, or "NO_MOVE"
754 return MovementResult::OK;
755 if (tokens.size() < 2)
756 return MovementResult::BAD_RESPONSE;
758 buffer += tokens[1]; //The y coordinate
760 buffer += tokens[2]; //The direction
762 //Check for a possible multiplier. If tokens[3] is an integer it will be the multiplier, otherwise it won't be.
763 if (tokens.size() > 3 && atoi(tokens[3].c_str()) != 0)
770 //(tokens[3] should include a new line)
779 return MovementResult::OK;
785 int Game::Tokenise(std::vector<string> & buffer, std::string & str, char split)
788 for (unsigned int x = 0; x < str.size(); ++x)
790 if (str[x] == split && token.size() > 0)
792 buffer.push_back(token);
798 if (token.size() > 0)
799 buffer.push_back(token);
800 return buffer.size();
804 * Creates Controller baseds off strings. Takes into account controllers other than AI_Controller.
805 * @param redPath - Either the path to an AI_Controller compatable executable, or one of %human or %network or %network:[IP_ADDRESS]
806 * @param bluePath - Ditto
807 * Sets this->red to a controller using redPath, and this->blue to a controller using bluePath
808 * TODO: Make nicer (this function should be ~half the length)
810 void Game::MakeControllers(const char * redPath, const char * bluePath)
812 Network * redNetwork = NULL;
813 Network * blueNetwork = NULL;
814 //To allow for running two network controllers (I don't know why you would, but beside the point...) use two ports
815 static const int port1 = 4560;
816 static const int port2 = 4561;
818 if (redPath[0] == '@')
820 if (strcmp(redPath, "@human") == 0)
821 red = new Human_Controller(Piece::RED, graphicsEnabled);
824 const char * network = strstr(redPath, "@network");
830 network = strstr(network, ":");
834 logMessage("Creating server for red AI... ");
835 redNetwork = new Server(port1);
836 logMessage("Successful!\n");
841 network = (const char*)(network+1);
842 logMessage("Creating client for red AI... ");
843 redNetwork = new Client(network, port2);
844 logMessage("Connected to address %s\n", network);
847 logMessage(" (Red's responses will be received over the connection)\n");
848 red = new NetworkReceiver(Piece::RED, redNetwork);
852 red = new AI_Controller(Piece::RED, redPath, timeoutTime);
854 if (bluePath[0] == '@')
856 if (strcmp(bluePath, "@human") == 0)
857 blue = new Human_Controller(Piece::BLUE, graphicsEnabled);
860 const char * network = strstr(bluePath, "@network");
866 network = strstr(network, ":");
870 logMessage("Creating server for blue AI... ");
871 blueNetwork = new Server(port2);
872 logMessage("Successful!\n");
877 network = (const char*)(network+1);
878 logMessage("Creating client for blue AI... ");
879 blueNetwork = new Client(network, port1);
880 logMessage("Connected to address %s\n", network);
882 logMessage(" (Blue's responses will be received over the connection)\n");
883 blue = new NetworkReceiver(Piece::BLUE, blueNetwork);
887 blue = new AI_Controller(Piece::BLUE, bluePath, timeoutTime);
889 if (redNetwork != NULL)
892 blue = new NetworkSender(Piece::BLUE,blue, redNetwork);
893 logMessage(" (Blue's responses will be copied over the connection)\n");
895 if (blueNetwork != NULL)
898 red = new NetworkSender(Piece::RED, red, blueNetwork);
899 logMessage(" (Red's responses will be copied over the connection)\n");
902 red->FixName(); blue->FixName();
914 void Game::PrintResults(const MovementResult & result, string & buffer)
917 switch (Game::theGame->Turn())
920 s << Game::theGame->red->name << " RED ";
923 s << Game::theGame->blue->name << " BLUE ";
926 s << "neither BOTH ";
929 s << "neither NONE ";
933 if (!Board::LegalResult(result) && result != MovementResult::BAD_SETUP)
935 else if (!Board::HaltResult(result))
936 s << "INTERNAL_ERROR ";
941 case MovementResult::VICTORY_FLAG:
942 case MovementResult::VICTORY_ATTRITION: //It does not matter how you win, it just matters that you won!
945 case MovementResult::SURRENDER:
948 case MovementResult::DRAW:
951 case MovementResult::DRAW_DEFAULT:
952 s << "DRAW_DEFAULT ";
954 case MovementResult::BAD_SETUP:
958 s << "INTERNAL_ERROR ";
963 s << Game::theGame->TurnCount() << " " << Game::theGame->theBoard.TotalPieceValue(Piece::RED) << " " << Game::theGame->theBoard.TotalPieceValue(Piece::BLUE);