#include "game.h"
#include <stdarg.h>
+#include <string>
+
using namespace std;
Game* Game::theGame = NULL;
bool Game::gameCreated = false;
-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)
+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)
{
gameCreated = false;
if (gameCreated)
#ifdef BUILD_GRAPHICS
if (graphicsEnabled && (!Graphics::Initialised()))
- Graphics::Initialise("Stratego", theBoard.Width()*32, theBoard.Height()*32);
+ {
+ string s = "Stratego: ";
+ s += string(redPath);
+ s += " ";
+ s += string(bluePath);
+ Graphics::Initialise(s.c_str(), theBoard.Width()*GRID_SIZE, theBoard.Height()*GRID_SIZE);
+ }
#endif //BUILD_GRAPHICS
- if (strcmp(redPath, "human") == 0)
- red = new Human_Controller(Piece::RED, graphicsEnabled);
- else
- red = new AI_Controller(Piece::RED, redPath, timeoutTime);
-
-
- if (strcmp(bluePath, "human") == 0)
- blue = new Human_Controller(Piece::BLUE, graphicsEnabled);
- else
- blue = new AI_Controller(Piece::BLUE, bluePath, timeoutTime);
+ MakeControllers(redPath, bluePath);
+
+ if (red == NULL || blue == NULL)
+ {
+ fprintf(stderr, "Game::Game - Error creating controller: ");
+ if (red == NULL)
+ {
+ if (blue == NULL)
+ fprintf(stderr, " BOTH! (red: \"%s\", blue: \"%s\"\n", redPath, bluePath);
+ else
+ fprintf(stderr, " RED! (red: \"%s\")\n", redPath);
+ }
+ else
+ fprintf(stderr, "BLUE! (blue: \"%s\")\n", bluePath);
+ exit(EXIT_FAILURE);
+ }
+// logMessage("Game initialised.\n");
}
-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)
+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)
{
gameCreated = false;
if (gameCreated)
#ifdef BUILD_GRAPHICS
if (graphicsEnabled && (!Graphics::Initialised()))
- Graphics::Initialise("Stratego", theBoard.Width()*32, theBoard.Height()*32);
+ {
+ string s = "Stratego: (file) ";
+ s += string(fromFile);
+ Graphics::Initialise(s.c_str(), theBoard.Width()*GRID_SIZE, theBoard.Height()*GRID_SIZE);
+ }
#endif //BUILD_GRAPHICS
input = fopen(fromFile, "r");
if (wait <= 0)
return;
- TimerThread timer(wait*1000000); //Wait in seconds
- timer.Start();
+
+
#ifdef BUILD_GRAPHICS
+
+
if (!graphicsEnabled)
{
- while (!timer.Finished());
- timer.Stop();
+ usleep(1000000*wait); //Wait in seconds
return;
}
- #endif //BUILD_GRAPHICS
+ TimerThread timer(wait*1000000); //Wait in seconds
+ timer.Start();
while (!timer.Finished())
{
- #ifdef BUILD_GRAPHICS
+
SDL_Event event;
while (SDL_PollEvent(&event))
{
break;
}
}
- #endif //BUILD_GRAPHICS
}
timer.Stop();
+
+ #else
+ usleep(wait*1000000); //Wait in seconds
+ #endif //BUILD_GRAPHICS
}
fprintf(stderr, "ERROR - Recieved SIGPIPE during game exit!\n");
exit(EXIT_FAILURE);
}
- if (theGame->turn == Piece::RED)
- {
- theGame->logMessage("Game ends on RED's turn - REASON: ");
- if (theGame->blue->Valid()) //Should probably check this
- theGame->blue->Message("DEFAULT");
- }
- else if (theGame->turn == Piece::BLUE)
- {
-
- theGame->logMessage("Game ends on BLUE's turn - REASON: ");
- if (theGame->red->Valid()) //Should probably check this
- theGame->red->Message("DEFAULT");
- }
- else
- {
- theGame->logMessage("Game ends on ERROR's turn - REASON: ");
-
- }
theGame->logMessage("SIGPIPE - Broken pipe (AI program no longer running)\n");
- if (Game::theGame->printBoard)
- Game::theGame->theBoard.PrintPretty(stdout, Piece::BOTH);
+ MovementResult result = MovementResult::BAD_RESPONSE;
-
- #ifdef BUILD_GRAPHICS
- if (Game::theGame->graphicsEnabled && theGame->log == stdout)
+ if (theGame->turn == Piece::RED)
{
- theGame->logMessage("CLOSE WINDOW TO EXIT\n");
- Game::theGame->theBoard.Draw(Piece::BOTH);
- while (true)
+ if (theGame->red->Valid())
{
- SDL_Event event;
- while (SDL_PollEvent(&event))
+ theGame->logMessage(" Strange; RED still appears valid.\n");
+ if (theGame->blue->Valid())
{
- switch (event.type)
- {
- case SDL_QUIT:
- exit(EXIT_SUCCESS);
- break;
- }
- }
+ theGame->logMessage(" BLUE also appears valid. Exiting with ERROR.\n");
+ result = MovementResult::ERROR;
+ }
+ else
+ {
+ theGame->logMessage("BLUE is invalid. Wait for BLUE's turn to exit.\n");
+ return;
+ }
}
}
- else
- #endif //BUILD_GRAPHICS
+ if (theGame->turn == Piece::BLUE)
{
- if (theGame->log == stdout || theGame->log == stderr)
+ if (theGame->blue->Valid())
{
- theGame->logMessage( "PRESS ENTER TO EXIT\n");
- theGame->theBoard.Print(theGame->log);
- while (fgetc(stdin) != '\n');
+ theGame->logMessage(" Strange; BLUE still appears valid.\n");
+ if (theGame->red->Valid())
+ {
+ theGame->logMessage(" RED also appears valid. Exiting with ERROR.\n");
+ result = MovementResult::ERROR;
+ }
+ else
+ {
+ theGame->logMessage("RED is invalid. Wait for RED's turn to exit.\n");
+ return;
+ }
}
}
-
+
+ Game::theGame->PrintEndMessage(result);
+
+ string buffer = "";
+ PrintResults(result, buffer);
+
+ //Message the AI's the quit message
+ Game::theGame->red->Message("QUIT " + buffer);
+ Game::theGame->blue->Message("QUIT " + buffer);
+
+ //Log the message
+ if (Game::theGame->GetLogFile() != stdout)
+ Game::theGame->logMessage("%s\n", buffer.c_str());
+
+ fprintf(stdout, "%s\n", buffer.c_str());
exit(EXIT_SUCCESS);
}
}
break;
+
}
if (printBoard)
-
+// logMessage("Messaging red with \"START\"\n");
red->Message("START");
-
+ int moveCount = 0;
while (!Board::HaltResult(result) && (turnCount < maxTurns || maxTurns < 0))
{
#ifdef BUILD_GRAPHICS
if (graphicsEnabled)
+ {
theBoard.Draw(toReveal);
+ if (imageOutput != "")
+ {
+ string imageFile = "" + imageOutput + "/"+ itostr(moveCount) + ".bmp";
+ Graphics::ScreenShot(imageFile.c_str());
+ }
+
+ }
#endif //BUILD_GRAPHICS
turn = Piece::RED;
logMessage( "%d RED: ", turnCount);
result = red->MakeMove(buffer);
- red->Message(buffer);
- blue->Message(buffer);
logMessage( "%s\n", buffer.c_str());
if (!Board::HaltResult(result))
if (Board::HaltResult(result))
break;
+ red->Message(buffer);
+ blue->Message(buffer);
+
+
+
+
if (stallTime >= 0)
Wait(stallTime);
else
theBoard.PrintPretty(stdout, toReveal);
fprintf(stdout, "\n\n");
}
+
+ ++moveCount;
#ifdef BUILD_GRAPHICS
if (graphicsEnabled)
+ {
theBoard.Draw(toReveal);
+ if (imageOutput != "")
+ {
+ string imageFile = "" + imageOutput + "/" + itostr(moveCount) + ".bmp";
+ Graphics::ScreenShot(imageFile.c_str());
+ }
+ }
#endif //BUILD_GRAPHICS
logMessage( "%d BLU: ", turnCount);
result = blue->MakeMove(buffer);
- blue->Message(buffer);
- red->Message(buffer);
logMessage( "%s\n", buffer.c_str());
if (!Board::HaltResult(result))
if (Board::HaltResult(result))
break;
+ red->Message(buffer);
+ blue->Message(buffer);
+
+
if (theBoard.MobilePieces(Piece::RED) == 0)
result = MovementResult::DRAW;
else
ReadUserCommand();
-
+ ++moveCount;
++turnCount;
}
return buffer.size();
}
+/**
+ * Creates Controller baseds off strings. Takes into account controllers other than AI_Controller.
+ * @param redPath - Either the path to an AI_Controller compatable executable, or one of %human or %network or %network:[IP_ADDRESS]
+ * @param bluePath - Ditto
+ * Sets this->red to a controller using redPath, and this->blue to a controller using bluePath
+ * TODO: Make nicer (this function should be ~half the length)
+ */
+void Game::MakeControllers(const char * redPath, const char * bluePath)
+{
+ Network * redNetwork = NULL;
+ Network * blueNetwork = NULL;
+ //To allow for running two network controllers (I don't know why you would, but beside the point...) use two ports
+ static const int port1 = 4560;
+ static const int port2 = 4561;
+
+ if (redPath[0] == '@')
+ {
+ if (strcmp(redPath, "@human") == 0)
+ red = new Human_Controller(Piece::RED, graphicsEnabled);
+ else
+ {
+ const char * network = strstr(redPath, "@network");
+ if (network == NULL)
+ {
+ red = NULL;
+ return;
+ }
+ network = strstr(network, ":");
+
+ if (network == NULL)
+ {
+ logMessage("Creating server for red AI... ");
+ redNetwork = new Server(port1);
+ logMessage("Successful!\n");
+
+ }
+ else
+ {
+ network = (const char*)(network+1);
+ logMessage("Creating client for red AI... ");
+ redNetwork = new Client(network, port2);
+ logMessage("Connected to address %s\n", network);
+ }
+
+ logMessage(" (Red's responses will be received over the connection)\n");
+ red = new NetworkReceiver(Piece::RED, redNetwork);
+ }
+ }
+ else
+ red = new AI_Controller(Piece::RED, redPath, timeoutTime);
+
+ if (bluePath[0] == '@')
+ {
+ if (strcmp(bluePath, "@human") == 0)
+ blue = new Human_Controller(Piece::BLUE, graphicsEnabled);
+ else
+ {
+ const char * network = strstr(bluePath, "@network");
+ if (network == NULL)
+ {
+ blue = NULL;
+ return;
+ }
+ network = strstr(network, ":");
+
+ if (network == NULL)
+ {
+ logMessage("Creating server for blue AI... ");
+ blueNetwork = new Server(port2);
+ logMessage("Successful!\n");
+
+ }
+ else
+ {
+ network = (const char*)(network+1);
+ logMessage("Creating client for blue AI... ");
+ blueNetwork = new Client(network, port1);
+ logMessage("Connected to address %s\n", network);
+ }
+ logMessage(" (Blue's responses will be received over the connection)\n");
+ blue = new NetworkReceiver(Piece::BLUE, blueNetwork);
+ }
+ }
+ else
+ blue = new AI_Controller(Piece::BLUE, bluePath, timeoutTime);
+
+ if (redNetwork != NULL)
+ {
+
+ blue = new NetworkSender(Piece::BLUE,blue, redNetwork);
+ logMessage(" (Blue's responses will be copied over the connection)\n");
+ }
+ if (blueNetwork != NULL)
+ {
+
+ red = new NetworkSender(Piece::RED, red, blueNetwork);
+ logMessage(" (Red's responses will be copied over the connection)\n");
+ }
+
+ red->FixName(); blue->FixName();
+
+}
+
+string itostr(int i)
+{
+ stringstream s;
+ s << i;
+ return s.str();
+}
+
+
+void Game::PrintResults(const MovementResult & result, string & buffer)
+{
+ stringstream s("");
+ switch (Game::theGame->Turn())
+ {
+ case Piece::RED:
+ s << Game::theGame->red->name << " RED ";
+ break;
+ case Piece::BLUE:
+ s << Game::theGame->blue->name << " BLUE ";
+ break;
+ case Piece::BOTH:
+ s << "neither BOTH ";
+ break;
+ case Piece::NONE:
+ s << "neither NONE ";
+ break;
+ }
+
+ if (!Board::LegalResult(result) && result != MovementResult::BAD_SETUP)
+ s << "ILLEGAL ";
+ else if (!Board::HaltResult(result))
+ s << "INTERNAL_ERROR ";
+ else
+ {
+ switch (result.type)
+ {
+ case MovementResult::VICTORY_FLAG:
+ case MovementResult::VICTORY_ATTRITION: //It does not matter how you win, it just matters that you won!
+ s << "VICTORY ";
+ break;
+ case MovementResult::SURRENDER:
+ s << "SURRENDER ";
+ break;
+ case MovementResult::DRAW:
+ s << "DRAW ";
+ break;
+ case MovementResult::DRAW_DEFAULT:
+ s << "DRAW_DEFAULT ";
+ break;
+ case MovementResult::BAD_SETUP:
+ s << "BAD_SETUP ";
+ break;
+ default:
+ s << "INTERNAL_ERROR ";
+ break;
+ }
+ }
+
+ s << Game::theGame->TurnCount() << " " << Game::theGame->theBoard.TotalPieceValue(Piece::RED) << " " << Game::theGame->theBoard.TotalPieceValue(Piece::BLUE);
+
+ buffer = s.str();
+
+
+}