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) : 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)
+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)
Graphics::Initialise("Stratego", theBoard.Width()*32, theBoard.Height()*32);
#endif //BUILD_GRAPHICS
- if (strcmp(redPath, "human") == 0)
- red = new Human_Controller(Piece::RED, graphicsEnabled);
- else
- red = new AI_Controller(Piece::RED, redPath);
-
-
- if (strcmp(bluePath, "human") == 0)
- blue = new Human_Controller(Piece::BLUE, graphicsEnabled);
- else
- blue = new AI_Controller(Piece::BLUE, bluePath);
+ 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) : 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)
+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)
{
logMessage("Controller for Player RED is invalid!\n");
if (!red->HumanController())
- logMessage("Check that program \"%s\" exists and has executable permissions set.\n", redName);
+ logMessage("Check that executable \"%s\" exists and has executable permissions set.\n", redName);
}
if (!blue->Valid())
{
logMessage("Controller for Player BLUE is invalid!\n");
if (!blue->HumanController())
- logMessage("Check that program \"%s\" exists and has executable permissions set.\n", blueName);
+ logMessage("Check that executable \"%s\" exists and has executable permissions set.\n", blueName);
}
if (!red->Valid())
{
if (theGame->turn == Piece::RED)
{
theGame->logMessage("Game ends on RED's turn - REASON: ");
- theGame->blue->Message("DEFAULT");
+ 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: ");
- theGame->red->Message("DEFAULT");
+ if (theGame->red->Valid()) //Should probably check this
+ theGame->red->Message("DEFAULT");
}
else
{
}
- theGame->logMessage("SIGPIPE - Broken pipe (AI program may have segfaulted)\n");
+ theGame->logMessage("SIGPIPE - Broken pipe (AI program no longer running)\n");
if (Game::theGame->printBoard)
Game::theGame->theBoard.PrintPretty(stdout, Piece::BOTH);
else
#endif //BUILD_GRAPHICS
{
- if (theGame->log == stdout)
+ if (theGame->log == stdout || theGame->log == stderr)
{
theGame->logMessage( "PRESS ENTER TO EXIT\n");
theGame->theBoard.Print(theGame->log);
case MovementResult::POSITION_FULL:
logMessage("Attempted move into square occupied by neutral or allied piece\n");
break;
- case MovementResult::VICTORY:
+ case MovementResult::VICTORY_FLAG:
logMessage("Captured the flag\n");
break;
+ case MovementResult::VICTORY_ATTRITION:
+ logMessage("Destroyed all mobile enemy pieces\n");
+ break;
case MovementResult::BAD_RESPONSE:
logMessage("Unintelligable response\n");
break;
case MovementResult::NO_MOVE:
- logMessage("Did not make a move (may have exited)\n");
+ logMessage("Response timeout after %2f seconds.\n", timeoutTime);
break;
case MovementResult::COLOUR_ERROR:
logMessage("Internal controller error - COLOUR_ERROR\n");
{
logMessage("PRESS ENTER TO EXIT\n");
while (fgetc(stdin) != '\n');
+ exit(EXIT_SUCCESS); //Might want to actually exit, you foolish fool
}
}
}
+/** Checks for victory by attrition (destroying all mobile pieces)
+ *
+ * @returns OK for no victory,
+ * DRAW if both players have no pieces, or
+ * VICTORY_ATTRITION if the current player has won by attrition
+ */
+MovementResult Game::CheckVictoryAttrition()
+{
+ if (theBoard.MobilePieces(Piece::OppositeColour(turn)) == 0)
+ {
+ if (theBoard.MobilePieces(turn) == 0)
+ return MovementResult::DRAW;
+ else
+ return MovementResult::VICTORY_ATTRITION;
+ }
+ return MovementResult::OK;
-
-
+}
MovementResult Game::Play()
{
-
+// 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;
+ blue->Pause();
+ red->Continue();
+ if (!Board::HaltResult(result))
+ {
+ result = CheckVictoryAttrition();
+ }
+ if (Board::HaltResult(result))
+ break;
+
logMessage( "%d RED: ", turnCount);
result = red->MakeMove(buffer);
red->Message(buffer);
blue->Message(buffer);
logMessage( "%s\n", buffer.c_str());
+
+ if (!Board::HaltResult(result))
+ {
+ result = CheckVictoryAttrition();
+ }
if (Board::HaltResult(result))
break;
- if (stallTime > 0)
+ if (stallTime >= 0)
Wait(stallTime);
else
ReadUserCommand();
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
turn = Piece::BLUE;
+ red->Pause();
+ blue->Continue();
+ if (!Board::HaltResult(result))
+ {
+ result = CheckVictoryAttrition();
+ }
+ if (Board::HaltResult(result))
+ break;
+
logMessage( "%d BLU: ", turnCount);
result = blue->MakeMove(buffer);
blue->Message(buffer);
red->Message(buffer);
logMessage( "%s\n", buffer.c_str());
+ if (!Board::HaltResult(result))
+ {
+ result = CheckVictoryAttrition();
+ }
if (Board::HaltResult(result))
break;
-
+ if (theBoard.MobilePieces(Piece::RED) == 0)
+ result = MovementResult::DRAW;
-
+ if (theBoard.MobilePieces(Piece::RED) == 0)
+ {
+ if (theBoard.MobilePieces(Piece::BLUE) == 0)
+ result = MovementResult::DRAW;
+ else
+ result = MovementResult::VICTORY_ATTRITION;
+ break;
+ }
- if (stallTime > 0)
+ if (stallTime >= 0)
Wait(stallTime);
else
ReadUserCommand();
- if (theBoard.MobilePieces(Piece::BOTH) == 0)
- result = MovementResult::DRAW;
+ ++moveCount;
++turnCount;
}
*/
void Game::ReadUserCommand()
{
- fprintf(stdout, "Waiting for user to press enter...\n");
+ fprintf(stdout, "Waiting for user to press enter... (type QUIT to exit)\n");
string command("");
for (char c = fgetc(stdin); c != '\n' && (int)(c) != EOF; c = fgetc(stdin))
{
command += c;
}
+
+ if (command == "QUIT")
+ {
+ fprintf(stdout, "Ordered to quit... exiting...\n");
+ exit(EXIT_SUCCESS);
+ }
}
MovementResult FileController::QuerySetup(const char * opponentName, std::string setup[])
MovementResult FileController::QueryMove(std::string & buffer)
{
+ //This bit is kind of hacky and terrible, and yes I am mixing C with C++
+ //Yes I should have used fstream for the whole thing and it would be much easier.
+ //Oh well.
+
char buf[BUFSIZ];
fgets(buf, sizeof(buf), file);
char * s = (char*)(buf);
while (*s != ':' && *s != '\0')
++s;
-
- s += 2;
+ //Move forward to the start of the move information
+ for (int i=0; i < 2; ++i)
+ {
+ if (*s != '\0' && *s != '\n')
+ ++s;
+ }
+
+ //Unfortunately we can't just copy the whole line
buffer = string(s);
+ //We have to remove the movement result tokens
+
+
+ vector<string> tokens;
+ Game::Tokenise(tokens, buffer, ' ');
+ buffer.clear();
+
+ if (tokens.size() < 1)
+ return MovementResult::BAD_RESPONSE;
+ buffer += tokens[0];
+
+
+ if (tokens[0] == "NO_MOVE") //tokens[0] is either the x coordinate, or "NO_MOVE"
+ return MovementResult::OK;
+ if (tokens.size() < 2)
+ return MovementResult::BAD_RESPONSE;
+ buffer += " ";
+ buffer += tokens[1]; //The y coordinate
+ buffer += " ";
+ buffer += tokens[2]; //The direction
+
+ //Check for a possible multiplier. If tokens[3] is an integer it will be the multiplier, otherwise it won't be.
+ if (tokens.size() > 3 && atoi(tokens[3].c_str()) != 0)
+ {
+ buffer += " ";
+ buffer += tokens[3];
+ }
+ else
+ {
+ //(tokens[3] should include a new line)
+ //buffer += "\n";
+ }
+
+
+
+
+
+
return MovementResult::OK;
}
+/**
+ * Tokenise a string
+ */
+int Game::Tokenise(std::vector<string> & buffer, std::string & str, char split)
+{
+ string token = "";
+ for (unsigned int x = 0; x < str.size(); ++x)
+ {
+ if (str[x] == split && token.size() > 0)
+ {
+ buffer.push_back(token);
+ token = "";
+ }
+ if (str[x] != split)
+ token += str[x];
+ }
+ if (token.size() > 0)
+ buffer.push_back(token);
+ 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();
+}