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) : 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)
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
28 if (strcmp(redPath, "human") == 0)
29 red = new Human_Controller(Piece::RED, graphicsEnabled);
31 red = new AI_Controller(Piece::RED, redPath, timeoutTime);
34 if (strcmp(bluePath, "human") == 0)
35 blue = new Human_Controller(Piece::BLUE, graphicsEnabled);
37 blue = new AI_Controller(Piece::BLUE, bluePath, timeoutTime);
42 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)
47 fprintf(stderr, "Game::Game - Error - Tried to create more than one Game!\n");
52 signal(SIGPIPE, Game::HandleBrokenPipe);
55 if (graphicsEnabled && (!Graphics::Initialised()))
56 Graphics::Initialise("Stratego", theBoard.Width()*32, theBoard.Height()*32);
57 #endif //BUILD_GRAPHICS
59 input = fopen(fromFile, "r");
61 red = new FileController(Piece::RED, input);
62 blue = new FileController(Piece::BLUE, input);
73 if (log != NULL && log != stdout && log != stderr)
76 if (input != NULL && input != stdin)
81 * Attempts to setup the board and controllers
82 * @param redName the name of the red AI
83 * @param blueName the name of the blue AI
84 * @returns A colour, indicating if there were any errors
85 Piece::NONE indicates no errors
86 Piece::BOTH indicates errors with both AI
87 Piece::RED / Piece::BLUE indicates an error with only one of the two AI
89 Piece::Colour Game::Setup(const char * redName, const char * blueName)
94 logMessage("Controller for Player RED is invalid!\n");
95 if (!red->HumanController())
96 logMessage("Check that executable \"%s\" exists and has executable permissions set.\n", redName);
100 logMessage("Controller for Player BLUE is invalid!\n");
101 if (!blue->HumanController())
102 logMessage("Check that executable \"%s\" exists and has executable permissions set.\n", blueName);
110 else if (!blue->Valid())
115 for (int y = 4; y < 6; ++y)
117 for (int x = 2; x < 4; ++x)
119 theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE);
121 for (int x = 6; x < 8; ++x)
123 theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE);
128 MovementResult redSetup = red->Setup(blueName);
129 MovementResult blueSetup = blue->Setup(redName);
132 Piece::Colour result = Piece::NONE;
133 if (redSetup != MovementResult::OK)
135 if (blueSetup != MovementResult::OK)
137 logMessage("BOTH players give invalid setup!\n");
138 result = Piece::BOTH;
142 //logMessage("Player RED gave an invalid setup!\n");
147 else if (blueSetup != MovementResult::OK)
149 //logMessage("Player BLUE gave an invalid setup!\n");
150 result = Piece::BLUE;
154 logMessage("%s RED SETUP\n", red->name.c_str());
155 if (redSetup == MovementResult::OK)
157 for (int y=0; y < 4; ++y)
159 for (int x=0; x < theBoard.Width(); ++x)
161 if (theBoard.GetPiece(x, y) != NULL)
162 logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, y)->type)]);
171 logMessage("INVALID!\n");
174 logMessage("%s BLUE SETUP\n", blue->name.c_str());
175 if (blueSetup == MovementResult::OK)
177 for (int y=0; y < 4; ++y)
179 for (int x=0; x < theBoard.Width(); ++x)
181 if (theBoard.GetPiece(x, theBoard.Height()-4+y) != NULL)
182 logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, theBoard.Height()-4+y)->type)]);
191 logMessage("INVALID!\n");
199 void Game::Wait(double wait)
204 TimerThread timer(wait*1000000); //Wait in seconds
207 #ifdef BUILD_GRAPHICS
208 if (!graphicsEnabled)
210 while (!timer.Finished());
214 #endif //BUILD_GRAPHICS
216 while (!timer.Finished())
218 #ifdef BUILD_GRAPHICS
220 while (SDL_PollEvent(&event))
230 #endif //BUILD_GRAPHICS
236 void Game::HandleBrokenPipe(int sig)
240 fprintf(stderr, "ERROR - Recieved SIGPIPE during game exit!\n");
243 if (theGame->turn == Piece::RED)
245 theGame->logMessage("Game ends on RED's turn - REASON: ");
246 if (theGame->blue->Valid()) //Should probably check this
247 theGame->blue->Message("DEFAULT");
249 else if (theGame->turn == Piece::BLUE)
252 theGame->logMessage("Game ends on BLUE's turn - REASON: ");
253 if (theGame->red->Valid()) //Should probably check this
254 theGame->red->Message("DEFAULT");
258 theGame->logMessage("Game ends on ERROR's turn - REASON: ");
262 theGame->logMessage("SIGPIPE - Broken pipe (AI program no longer running)\n");
264 if (Game::theGame->printBoard)
265 Game::theGame->theBoard.PrintPretty(stdout, Piece::BOTH);
268 #ifdef BUILD_GRAPHICS
269 if (Game::theGame->graphicsEnabled && theGame->log == stdout)
271 theGame->logMessage("CLOSE WINDOW TO EXIT\n");
272 Game::theGame->theBoard.Draw(Piece::BOTH);
276 while (SDL_PollEvent(&event))
288 #endif //BUILD_GRAPHICS
290 if (theGame->log == stdout || theGame->log == stderr)
292 theGame->logMessage( "PRESS ENTER TO EXIT\n");
293 theGame->theBoard.Print(theGame->log);
294 while (fgetc(stdin) != '\n');
302 void Game::PrintEndMessage(const MovementResult & result)
306 logMessage("Game ends in the SETUP phase - REASON: ");
310 if (turn == Piece::RED)
312 logMessage("Game ends on RED's turn - REASON: ");
314 else if (turn == Piece::BLUE)
316 logMessage("Game ends on BLUE's turn - REASON: ");
320 logMessage("Game ends on ERROR's turn - REASON: ");
326 case MovementResult::OK:
327 logMessage("Status returned OK, unsure why game halted...\n");
329 case MovementResult::DIES:
330 logMessage("Status returned DIES, unsure why game halted...\n");
332 case MovementResult::KILLS:
333 logMessage("Status returned KILLS, unsure why game halted...\n");
335 case MovementResult::BOTH_DIE:
336 logMessage("Status returned BOTH_DIE, unsure why game halted...\n");
338 case MovementResult::NO_BOARD:
339 logMessage("Board does not exit?!\n");
341 case MovementResult::INVALID_POSITION:
342 logMessage("Coords outside board\n");
344 case MovementResult::NO_SELECTION:
345 logMessage("Move does not select a piece\n");
347 case MovementResult::NOT_YOUR_UNIT:
348 logMessage("Selected piece belongs to other player\n");
350 case MovementResult::IMMOBILE_UNIT:
351 logMessage("Selected piece is not mobile (FLAG or BOMB)\n");
353 case MovementResult::INVALID_DIRECTION:
354 logMessage("Selected unit cannot move that way\n");
356 case MovementResult::POSITION_FULL:
357 logMessage("Attempted move into square occupied by neutral or allied piece\n");
359 case MovementResult::VICTORY_FLAG:
360 logMessage("Captured the flag\n");
362 case MovementResult::VICTORY_ATTRITION:
363 logMessage("Destroyed all mobile enemy pieces\n");
365 case MovementResult::BAD_RESPONSE:
366 logMessage("Unintelligable response\n");
368 case MovementResult::NO_MOVE:
369 logMessage("Response timeout after %2f seconds.\n", timeoutTime);
371 case MovementResult::COLOUR_ERROR:
372 logMessage("Internal controller error - COLOUR_ERROR\n");
374 case MovementResult::ERROR:
375 logMessage("Internal controller error - Unspecified ERROR\n");
377 case MovementResult::DRAW_DEFAULT:
378 logMessage("Game declared a draw after %d turns\n", turnCount);
380 case MovementResult::DRAW:
381 logMessage("Game declared a draw because neither player has mobile pieces\n");
383 case MovementResult::SURRENDER:
384 logMessage("This player has surrendered!\n");
386 case MovementResult::BAD_SETUP:
390 logMessage("An illegal setup was made by RED\n");
393 logMessage("An illegal setup was made by BLUE\n");
396 logMessage("An illegal setup was made by BOTH players\n");
399 logMessage("Unknown internal error.\n");
409 fprintf(stdout, "%d Final State\n", turnCount);
410 theBoard.PrintPretty(stdout, Piece::BOTH);
411 fprintf(stdout, "\n");
414 #ifdef BUILD_GRAPHICS
415 if (graphicsEnabled && log == stdout)
417 logMessage("CLOSE WINDOW TO EXIT\n");
418 theBoard.Draw(Piece::BOTH);
422 while (SDL_PollEvent(&event))
434 #endif //BUILD_GRAPHICS
438 logMessage("PRESS ENTER TO EXIT\n");
439 while (fgetc(stdin) != '\n');
440 exit(EXIT_SUCCESS); //Might want to actually exit, you foolish fool
445 /** Checks for victory by attrition (destroying all mobile pieces)
447 * @returns OK for no victory,
448 * DRAW if both players have no pieces, or
449 * VICTORY_ATTRITION if the current player has won by attrition
451 MovementResult Game::CheckVictoryAttrition()
453 if (theBoard.MobilePieces(Piece::OppositeColour(turn)) == 0)
455 if (theBoard.MobilePieces(turn) == 0)
456 return MovementResult::DRAW;
458 return MovementResult::VICTORY_ATTRITION;
460 return MovementResult::OK;
463 MovementResult Game::Play()
466 MovementResult result = MovementResult::OK;
470 Piece::Colour toReveal = reveal;
476 red->Message("START");
480 while (!Board::HaltResult(result) && (turnCount < maxTurns || maxTurns < 0))
482 if (red->HumanController() && blue->HumanController())
483 toReveal = Piece::RED;
488 fprintf(stdout, "START:\n");
490 fprintf(stdout, "%d BLUE:\n", turnCount);
491 theBoard.PrintPretty(stdout, toReveal);
492 fprintf(stdout, "\n\n");
495 #ifdef BUILD_GRAPHICS
497 theBoard.Draw(toReveal);
498 #endif //BUILD_GRAPHICS
502 if (!Board::HaltResult(result))
504 result = CheckVictoryAttrition();
506 if (Board::HaltResult(result))
509 logMessage( "%d RED: ", turnCount);
510 result = red->MakeMove(buffer);
511 red->Message(buffer);
512 blue->Message(buffer);
513 logMessage( "%s\n", buffer.c_str());
515 if (!Board::HaltResult(result))
517 result = CheckVictoryAttrition();
519 if (Board::HaltResult(result))
527 if (blue->HumanController() && red->HumanController())
528 toReveal = Piece::BLUE;
532 fprintf(stdout, "%d RED:\n", turnCount);
533 theBoard.PrintPretty(stdout, toReveal);
534 fprintf(stdout, "\n\n");
537 #ifdef BUILD_GRAPHICS
539 theBoard.Draw(toReveal);
540 #endif //BUILD_GRAPHICS
546 if (!Board::HaltResult(result))
548 result = CheckVictoryAttrition();
550 if (Board::HaltResult(result))
553 logMessage( "%d BLU: ", turnCount);
554 result = blue->MakeMove(buffer);
555 blue->Message(buffer);
556 red->Message(buffer);
557 logMessage( "%s\n", buffer.c_str());
559 if (!Board::HaltResult(result))
561 result = CheckVictoryAttrition();
563 if (Board::HaltResult(result))
566 if (theBoard.MobilePieces(Piece::RED) == 0)
567 result = MovementResult::DRAW;
569 if (theBoard.MobilePieces(Piece::RED) == 0)
571 if (theBoard.MobilePieces(Piece::BLUE) == 0)
572 result = MovementResult::DRAW;
574 result = MovementResult::VICTORY_ATTRITION;
588 if ((maxTurns >= 0 && turnCount >= maxTurns) && result == MovementResult::OK)
590 result = MovementResult::DRAW_DEFAULT;
601 * Logs a message to the game's log file if it exists
602 * @param format the format string
603 * @param additional parameters - printed using va_args
604 * @returns the result of vfprintf or a negative number if the log file does not exist
606 int Game::logMessage(const char * format, ...)
611 va_start(ap, format);
613 int result = vfprintf(log, format, ap);
620 * Waits for a user command
621 * Currently ignores the command.
623 void Game::ReadUserCommand()
625 fprintf(stdout, "Waiting for user to press enter... (type QUIT to exit)\n");
627 for (char c = fgetc(stdin); c != '\n' && (int)(c) != EOF; c = fgetc(stdin))
632 if (command == "QUIT")
634 fprintf(stdout, "Ordered to quit... exiting...\n");
639 MovementResult FileController::QuerySetup(const char * opponentName, std::string setup[])
642 char c = fgetc(file);
650 while (fgetc(file) != '\n');
652 for (int y = 0; y < 4; ++y)
655 for (int x = 0; x < Game::theGame->theBoard.Width(); ++x)
657 setup[y] += fgetc(file);
660 if (fgetc(file) != '\n')
662 return MovementResult::BAD_RESPONSE;
665 return MovementResult::OK;
670 MovementResult FileController::QueryMove(std::string & buffer)
672 //This bit is kind of hacky and terrible, and yes I am mixing C with C++
673 //Yes I should have used fstream for the whole thing and it would be much easier.
678 fgets(buf, sizeof(buf), file);
679 char * s = (char*)(buf);
680 while (*s != ':' && *s != '\0')
683 //Move forward to the start of the move information
684 for (int i=0; i < 2; ++i)
686 if (*s != '\0' && *s != '\n')
690 //Unfortunately we can't just copy the whole line
692 //We have to remove the movement result tokens
695 vector<string> tokens;
696 Game::Tokenise(tokens, buffer, ' ');
699 if (tokens.size() < 1)
700 return MovementResult::BAD_RESPONSE;
704 if (tokens[0] == "NO_MOVE") //tokens[0] is either the x coordinate, or "NO_MOVE"
705 return MovementResult::OK;
706 if (tokens.size() < 2)
707 return MovementResult::BAD_RESPONSE;
709 buffer += tokens[1]; //The y coordinate
711 buffer += tokens[2]; //The direction
713 //Check for a possible multiplier. If tokens[3] is an integer it will be the multiplier, otherwise it won't be.
714 if (tokens.size() > 3 && atoi(tokens[3].c_str()) != 0)
721 //(tokens[3] should include a new line)
730 return MovementResult::OK;
736 int Game::Tokenise(std::vector<string> & buffer, std::string & str, char split)
739 for (unsigned int x = 0; x < str.size(); ++x)
741 if (str[x] == split && token.size() > 0)
743 buffer.push_back(token);
749 if (token.size() > 0)
750 buffer.push_back(token);
751 return buffer.size();