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, bool server, bool client) : 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
31 red = new Client(Piece::RED, "server", redPath); //TODO: Retrieve server address
35 assert(redPath != NULL);
36 if (strcmp(redPath, "human") == 0)
37 red = new Human_Controller(Piece::RED, graphicsEnabled);
39 red = new AI_Controller(Piece::RED, redPath, timeoutTime);
45 blue = new Server(Piece::BLUE, "client");
49 assert(bluePath != NULL);
50 if (strcmp(bluePath, "human") == 0)
51 blue = new Human_Controller(Piece::BLUE, graphicsEnabled);
53 blue = new AI_Controller(Piece::BLUE, bluePath, timeoutTime);
59 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)
64 fprintf(stderr, "Game::Game - Error - Tried to create more than one Game!\n");
69 signal(SIGPIPE, Game::HandleBrokenPipe);
72 if (graphicsEnabled && (!Graphics::Initialised()))
73 Graphics::Initialise("Stratego", theBoard.Width()*32, theBoard.Height()*32);
74 #endif //BUILD_GRAPHICS
76 input = fopen(fromFile, "r");
78 red = new FileController(Piece::RED, input);
79 blue = new FileController(Piece::BLUE, input);
90 if (log != NULL && log != stdout && log != stderr)
93 if (input != NULL && input != stdin)
98 * Attempts to setup the board and controllers
99 * @param redName the name of the red AI
100 * @param blueName the name of the blue AI
101 * @returns A colour, indicating if there were any errors
102 Piece::NONE indicates no errors
103 Piece::BOTH indicates errors with both AI
104 Piece::RED / Piece::BLUE indicates an error with only one of the two AI
106 Piece::Colour Game::Setup(const char * redName, const char * blueName)
111 logMessage("Controller for Player RED is invalid!\n");
112 if (!red->HumanController())
113 logMessage("Check that executable \"%s\" exists and has executable permissions set.\n", redName);
117 logMessage("Controller for Player BLUE is invalid!\n");
118 if (!blue->HumanController())
119 logMessage("Check that executable \"%s\" exists and has executable permissions set.\n", blueName);
127 else if (!blue->Valid())
132 for (int y = 4; y < 6; ++y)
134 for (int x = 2; x < 4; ++x)
136 theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE);
138 for (int x = 6; x < 8; ++x)
140 theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE);
145 MovementResult redSetup = red->Setup(blueName);
146 MovementResult blueSetup = blue->Setup(redName);
149 Piece::Colour result = Piece::NONE;
150 if (redSetup != MovementResult::OK)
152 if (blueSetup != MovementResult::OK)
154 logMessage("BOTH players give invalid setup!\n");
155 result = Piece::BOTH;
159 //logMessage("Player RED gave an invalid setup!\n");
164 else if (blueSetup != MovementResult::OK)
166 //logMessage("Player BLUE gave an invalid setup!\n");
167 result = Piece::BLUE;
171 logMessage("%s RED SETUP\n", red->name.c_str());
172 if (redSetup == MovementResult::OK)
174 for (int y=0; y < 4; ++y)
176 for (int x=0; x < theBoard.Width(); ++x)
178 if (theBoard.GetPiece(x, y) != NULL)
179 logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, y)->type)]);
188 logMessage("INVALID!\n");
191 logMessage("%s BLUE SETUP\n", blue->name.c_str());
192 if (blueSetup == MovementResult::OK)
194 for (int y=0; y < 4; ++y)
196 for (int x=0; x < theBoard.Width(); ++x)
198 if (theBoard.GetPiece(x, theBoard.Height()-4+y) != NULL)
199 logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, theBoard.Height()-4+y)->type)]);
208 logMessage("INVALID!\n");
216 void Game::Wait(double wait)
221 TimerThread timer(wait*1000000); //Wait in seconds
224 #ifdef BUILD_GRAPHICS
225 if (!graphicsEnabled)
227 while (!timer.Finished());
231 #endif //BUILD_GRAPHICS
233 while (!timer.Finished())
235 #ifdef BUILD_GRAPHICS
237 while (SDL_PollEvent(&event))
247 #endif //BUILD_GRAPHICS
253 void Game::HandleBrokenPipe(int sig)
257 fprintf(stderr, "ERROR - Recieved SIGPIPE during game exit!\n");
260 if (theGame->turn == Piece::RED)
262 theGame->logMessage("Game ends on RED's turn - REASON: ");
263 if (theGame->blue->Valid()) //Should probably check this
264 theGame->blue->Message("DEFAULT");
266 else if (theGame->turn == Piece::BLUE)
269 theGame->logMessage("Game ends on BLUE's turn - REASON: ");
270 if (theGame->red->Valid()) //Should probably check this
271 theGame->red->Message("DEFAULT");
275 theGame->logMessage("Game ends on ERROR's turn - REASON: ");
279 theGame->logMessage("SIGPIPE - Broken pipe (AI program no longer running)\n");
281 if (Game::theGame->printBoard)
282 Game::theGame->theBoard.PrintPretty(stdout, Piece::BOTH);
285 #ifdef BUILD_GRAPHICS
286 if (Game::theGame->graphicsEnabled && theGame->log == stdout)
288 theGame->logMessage("CLOSE WINDOW TO EXIT\n");
289 Game::theGame->theBoard.Draw(Piece::BOTH);
293 while (SDL_PollEvent(&event))
305 #endif //BUILD_GRAPHICS
307 if (theGame->log == stdout || theGame->log == stderr)
309 theGame->logMessage( "PRESS ENTER TO EXIT\n");
310 theGame->theBoard.Print(theGame->log);
311 while (fgetc(stdin) != '\n');
319 void Game::PrintEndMessage(const MovementResult & result)
323 logMessage("Game ends in the SETUP phase - REASON: ");
327 if (turn == Piece::RED)
329 logMessage("Game ends on RED's turn - REASON: ");
331 else if (turn == Piece::BLUE)
333 logMessage("Game ends on BLUE's turn - REASON: ");
337 logMessage("Game ends on ERROR's turn - REASON: ");
343 case MovementResult::OK:
344 logMessage("Status returned OK, unsure why game halted...\n");
346 case MovementResult::DIES:
347 logMessage("Status returned DIES, unsure why game halted...\n");
349 case MovementResult::KILLS:
350 logMessage("Status returned KILLS, unsure why game halted...\n");
352 case MovementResult::BOTH_DIE:
353 logMessage("Status returned BOTH_DIE, unsure why game halted...\n");
355 case MovementResult::NO_BOARD:
356 logMessage("Board does not exit?!\n");
358 case MovementResult::INVALID_POSITION:
359 logMessage("Coords outside board\n");
361 case MovementResult::NO_SELECTION:
362 logMessage("Move does not select a piece\n");
364 case MovementResult::NOT_YOUR_UNIT:
365 logMessage("Selected piece belongs to other player\n");
367 case MovementResult::IMMOBILE_UNIT:
368 logMessage("Selected piece is not mobile (FLAG or BOMB)\n");
370 case MovementResult::INVALID_DIRECTION:
371 logMessage("Selected unit cannot move that way\n");
373 case MovementResult::POSITION_FULL:
374 logMessage("Attempted move into square occupied by neutral or allied piece\n");
376 case MovementResult::VICTORY_FLAG:
377 logMessage("Captured the flag\n");
379 case MovementResult::VICTORY_ATTRITION:
380 logMessage("Destroyed all mobile enemy pieces\n");
382 case MovementResult::BAD_RESPONSE:
383 logMessage("Unintelligable response\n");
385 case MovementResult::NO_MOVE:
386 logMessage("Response timeout after %2f seconds.\n", timeoutTime);
388 case MovementResult::COLOUR_ERROR:
389 logMessage("Internal controller error - COLOUR_ERROR\n");
391 case MovementResult::ERROR:
392 logMessage("Internal controller error - Unspecified ERROR\n");
394 case MovementResult::DRAW_DEFAULT:
395 logMessage("Game declared a draw after %d turns\n", turnCount);
397 case MovementResult::DRAW:
398 logMessage("Game declared a draw because neither player has mobile pieces\n");
400 case MovementResult::SURRENDER:
401 logMessage("This player has surrendered!\n");
403 case MovementResult::BAD_SETUP:
407 logMessage("An illegal setup was made by RED\n");
410 logMessage("An illegal setup was made by BLUE\n");
413 logMessage("An illegal setup was made by BOTH players\n");
416 logMessage("Unknown internal error.\n");
426 fprintf(stdout, "%d Final State\n", turnCount);
427 theBoard.PrintPretty(stdout, Piece::BOTH);
428 fprintf(stdout, "\n");
431 #ifdef BUILD_GRAPHICS
432 if (graphicsEnabled && log == stdout)
434 logMessage("CLOSE WINDOW TO EXIT\n");
435 theBoard.Draw(Piece::BOTH);
439 while (SDL_PollEvent(&event))
451 #endif //BUILD_GRAPHICS
455 logMessage("PRESS ENTER TO EXIT\n");
456 while (fgetc(stdin) != '\n');
457 exit(EXIT_SUCCESS); //Might want to actually exit, you foolish fool
462 /** Checks for victory by attrition (destroying all mobile pieces)
464 * @returns OK for no victory,
465 * DRAW if both players have no pieces, or
466 * VICTORY_ATTRITION if the current player has won by attrition
468 MovementResult Game::CheckVictoryAttrition()
470 if (theBoard.MobilePieces(Piece::OppositeColour(turn)) == 0)
472 if (theBoard.MobilePieces(turn) == 0)
473 return MovementResult::DRAW;
475 return MovementResult::VICTORY_ATTRITION;
477 return MovementResult::OK;
480 MovementResult Game::Play()
483 MovementResult result = MovementResult::OK;
487 Piece::Colour toReveal = reveal;
493 red->Message("START");
497 while (!Board::HaltResult(result) && (turnCount < maxTurns || maxTurns < 0))
499 if (red->HumanController() && blue->HumanController())
500 toReveal = Piece::RED;
505 fprintf(stdout, "START:\n");
507 fprintf(stdout, "%d BLUE:\n", turnCount);
508 theBoard.PrintPretty(stdout, toReveal);
509 fprintf(stdout, "\n\n");
512 #ifdef BUILD_GRAPHICS
514 theBoard.Draw(toReveal);
515 #endif //BUILD_GRAPHICS
520 if (!Board::HaltResult(result))
522 result = CheckVictoryAttrition();
524 if (Board::HaltResult(result))
527 logMessage( "%d RED: ", turnCount);
528 result = red->MakeMove(buffer);
529 red->Message(buffer);
530 blue->Message(buffer);
531 logMessage( "%s\n", buffer.c_str());
533 if (!Board::HaltResult(result))
535 result = CheckVictoryAttrition();
537 if (Board::HaltResult(result))
545 if (blue->HumanController() && red->HumanController())
546 toReveal = Piece::BLUE;
550 fprintf(stdout, "%d RED:\n", turnCount);
551 theBoard.PrintPretty(stdout, toReveal);
552 fprintf(stdout, "\n\n");
555 #ifdef BUILD_GRAPHICS
557 theBoard.Draw(toReveal);
558 #endif //BUILD_GRAPHICS
565 if (!Board::HaltResult(result))
567 result = CheckVictoryAttrition();
569 if (Board::HaltResult(result))
572 logMessage( "%d BLU: ", turnCount);
573 result = blue->MakeMove(buffer);
574 blue->Message(buffer);
575 red->Message(buffer);
576 logMessage( "%s\n", buffer.c_str());
578 if (!Board::HaltResult(result))
580 result = CheckVictoryAttrition();
582 if (Board::HaltResult(result))
585 if (theBoard.MobilePieces(Piece::RED) == 0)
586 result = MovementResult::DRAW;
588 if (theBoard.MobilePieces(Piece::RED) == 0)
590 if (theBoard.MobilePieces(Piece::BLUE) == 0)
591 result = MovementResult::DRAW;
593 result = MovementResult::VICTORY_ATTRITION;
607 if ((maxTurns >= 0 && turnCount >= maxTurns) && result == MovementResult::OK)
609 result = MovementResult::DRAW_DEFAULT;
620 * Logs a message to the game's log file if it exists
621 * @param format the format string
622 * @param additional parameters - printed using va_args
623 * @returns the result of vfprintf or a negative number if the log file does not exist
625 int Game::logMessage(const char * format, ...)
630 va_start(ap, format);
632 int result = vfprintf(log, format, ap);
639 * Waits for a user command
640 * Currently ignores the command.
642 void Game::ReadUserCommand()
644 fprintf(stdout, "Waiting for user to press enter... (type QUIT to exit)\n");
646 for (char c = fgetc(stdin); c != '\n' && (int)(c) != EOF; c = fgetc(stdin))
651 if (command == "QUIT")
653 fprintf(stdout, "Ordered to quit... exiting...\n");
658 MovementResult FileController::QuerySetup(const char * opponentName, std::string setup[])
661 char c = fgetc(file);
669 while (fgetc(file) != '\n');
671 for (int y = 0; y < 4; ++y)
674 for (int x = 0; x < Game::theGame->theBoard.Width(); ++x)
676 setup[y] += fgetc(file);
679 if (fgetc(file) != '\n')
681 return MovementResult::BAD_RESPONSE;
684 return MovementResult::OK;
689 MovementResult FileController::QueryMove(std::string & buffer)
691 //This bit is kind of hacky and terrible, and yes I am mixing C with C++
692 //Yes I should have used fstream for the whole thing and it would be much easier.
697 fgets(buf, sizeof(buf), file);
698 char * s = (char*)(buf);
699 while (*s != ':' && *s != '\0')
702 //Move forward to the start of the move information
703 for (int i=0; i < 2; ++i)
705 if (*s != '\0' && *s != '\n')
709 //Unfortunately we can't just copy the whole line
711 //We have to remove the movement result tokens
714 vector<string> tokens;
715 Game::Tokenise(tokens, buffer, ' ');
718 if (tokens.size() < 1)
719 return MovementResult::BAD_RESPONSE;
723 if (tokens[0] == "NO_MOVE") //tokens[0] is either the x coordinate, or "NO_MOVE"
724 return MovementResult::OK;
725 if (tokens.size() < 2)
726 return MovementResult::BAD_RESPONSE;
728 buffer += tokens[1]; //The y coordinate
730 buffer += tokens[2]; //The direction
732 //Check for a possible multiplier. If tokens[3] is an integer it will be the multiplier, otherwise it won't be.
733 if (tokens.size() > 3 && atoi(tokens[3].c_str()) != 0)
740 //(tokens[3] should include a new line)
749 return MovementResult::OK;
755 int Game::Tokenise(std::vector<string> & buffer, std::string & str, char split)
758 for (unsigned int x = 0; x < str.size(); ++x)
760 if (str[x] == split && token.size() > 0)
762 buffer.push_back(token);
768 if (token.size() > 0)
769 buffer.push_back(token);
770 return buffer.size();