From: Sam Moore Date: Sat, 24 Dec 2011 10:59:38 +0000 (+0800) Subject: [RULE CHANGE] *Victory by "attrition"* + Bug fixes X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=e1153eebe8cfd0c881cef2ff8fca63f130e736b3;p=progcomp2012.git [RULE CHANGE] *Victory by "attrition"* + Bug fixes Minor bugs in the manager program fixed. Changed some messages for clarity, can't remember what, look at diff. Added VICTORY_ATTRITION; victory by destroying all of the opponents _mobile_ pieces (ie: Everything except Bombs/Flag) This means we don't get results of DRAW or DRAW_DEFAULT when one AI destroys all the other's mobile pieces. Since those games would last up to 5000 turns, this saves a lot of wasted time. AI should still respond with "NO_MOVE" when they have no mobile pieces. Made timeout value adjustable by an argument switch, '-T' Altered simulate.py to use a timeout variable and supply the switch to the manager program. So now I don't need to recompile and commit the manager program every time I want to test a different timeout value on mufasa! Mufasa is now on game 3 of the test round, out of 12. After FIVE HOURS. This particular game has lasted 1132 turns, with BLUE making "NO_MOVE" for the last 600 or so. The new victory condition will stop this sort of thing :) Merry Christmas! --- diff --git a/judge/manager/Makefile b/judge/manager/Makefile index 37b2c29..67ffda9 100644 --- a/judge/manager/Makefile +++ b/judge/manager/Makefile @@ -1,9 +1,9 @@ #Makefile for Stratego #Use this to build with graphics -#CPP = g++ -Wall -pedantic -lSDL -lGL -lpthread -g +CPP = g++ -Wall -pedantic -lSDL -lGL -lpthread -g #Use this to build without graphics -CPP = g++ -Wall -pedantic -lpthread -g +#CPP = g++ -Wall -pedantic -lpthread -g OBJ = main.o controller.o ai_controller.o human_controller.o program.o thread_util.o stratego.o graphics.o game.o BIN = stratego diff --git a/judge/manager/controller.cpp b/judge/manager/controller.cpp index 1b0a35d..f5c99ce 100644 --- a/judge/manager/controller.cpp +++ b/judge/manager/controller.cpp @@ -138,8 +138,11 @@ MovementResult Controller::MakeMove(string & buffer) case MovementResult::OK: buffer += " OK"; break; - case MovementResult::VICTORY: - buffer += " FLAG"; + case MovementResult::VICTORY_FLAG: + buffer += " VICTORY_FLAG"; + break; + case MovementResult::VICTORY_ATTRITION: + buffer += " VICTORY_ATTRITION"; break; case MovementResult::KILLS: buffer += " KILLS "; diff --git a/judge/manager/game.cpp b/judge/manager/game.cpp index df7a061..3df968c 100644 --- a/judge/manager/game.cpp +++ b/judge/manager/game.cpp @@ -7,7 +7,7 @@ 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) : 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) : 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) { gameCreated = false; if (gameCreated) @@ -28,18 +28,18 @@ Game::Game(const char * redPath, const char * bluePath, const bool enableGraphic if (strcmp(redPath, "human") == 0) red = new Human_Controller(Piece::RED, graphicsEnabled); else - red = new AI_Controller(Piece::RED, redPath); + 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); + blue = new AI_Controller(Piece::BLUE, bluePath, timeoutTime); } -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) : 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) { gameCreated = false; if (gameCreated) @@ -93,13 +93,13 @@ Piece::Colour Game::Setup(const char * redName, const char * blueName) { 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()) { @@ -243,13 +243,15 @@ void Game::HandleBrokenPipe(int sig) 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 { @@ -257,7 +259,7 @@ void Game::HandleBrokenPipe(int sig) } - 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); @@ -285,7 +287,7 @@ void Game::HandleBrokenPipe(int sig) 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); @@ -354,14 +356,17 @@ void Game::PrintEndMessage(const MovementResult & result) 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"); @@ -432,6 +437,7 @@ void Game::PrintEndMessage(const MovementResult & result) { logMessage("PRESS ENTER TO EXIT\n"); while (fgetc(stdin) != '\n'); + exit(EXIT_SUCCESS); //Might want to actually exit, you foolish fool } } @@ -485,7 +491,16 @@ MovementResult Game::Play() if (Board::HaltResult(result)) break; - if (stallTime > 0) + if (theBoard.MobilePieces(Piece::BLUE) == 0) + { + if (theBoard.MobilePieces(Piece::RED) == 0) + result = MovementResult::DRAW; + else + result = MovementResult::VICTORY_ATTRITION; + break; + } + + if (stallTime >= 0) Wait(stallTime); else ReadUserCommand(); @@ -517,17 +532,24 @@ MovementResult Game::Play() 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; + ++turnCount; } @@ -569,12 +591,18 @@ int Game::logMessage(const char * format, ...) */ 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[]) @@ -610,17 +638,86 @@ MovementResult FileController::QuerySetup(const char * opponentName, std::string 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 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 & 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(); +} + diff --git a/judge/manager/game.h b/judge/manager/game.h index 43f4d0e..aebda9d 100644 --- a/judge/manager/game.h +++ b/judge/manager/game.h @@ -9,12 +9,13 @@ /** * Class to manage the game + * Bit messy since I keep adding on parameters :P */ class Game { public: - Game(const char * redPath, const char * bluePath, const bool enableGraphics, double newStallTime = 1.0, const bool allowIllegal=false, FILE * newLog = NULL, const Piece::Colour & newRevealed = Piece::BOTH, int maxTurns = 5000, const bool printBoard = false); - Game(const char * fromFile, const bool enableGraphics, double newStallTime = 1.0, const bool allowIllegal=false, FILE * newLog = NULL, const Piece::Colour & newRevealed = Piece::BOTH, int maxTurns = 5000, const bool printBoard = false); + Game(const char * redPath, const char * bluePath, const bool enableGraphics, double newStallTime = 1.0, const bool allowIllegal=false, FILE * newLog = NULL, const Piece::Colour & newRevealed = Piece::BOTH, int maxTurns = 5000, const bool printBoard = false, double newTimeoutTime = 2.0); + Game(const char * fromFile, const bool enableGraphics, double newStallTime = 1.0, const bool allowIllegal=false, FILE * newLog = NULL, const Piece::Colour & newRevealed = Piece::BOTH, int maxTurns = 5000, const bool printBoard = false, double newTimeoutTime = 2.0); virtual ~Game(); @@ -34,6 +35,7 @@ class Game int TurnCount() const {return turnCount;} static Game * theGame; + static int Tokenise(std::vector & buffer, std::string & str, char split = ' '); //Helper - Split a string into tokens public: int logMessage(const char * format, ...); FILE * GetLogFile() const {return log;} @@ -63,6 +65,9 @@ class Game int maxTurns; const bool printBoard; + + private: + double timeoutTime; }; diff --git a/judge/manager/graphics.h b/judge/manager/graphics.h index 2a96932..caa57c6 100644 --- a/judge/manager/graphics.h +++ b/judge/manager/graphics.h @@ -1,4 +1,4 @@ -//#define BUILD_GRAPHICS +#define BUILD_GRAPHICS #ifdef BUILD_GRAPHICS #ifndef GRAPHICS_H diff --git a/judge/manager/main.cpp b/judge/manager/main.cpp index 9d2cad1..ce4cd20 100644 --- a/judge/manager/main.cpp +++ b/judge/manager/main.cpp @@ -62,8 +62,8 @@ int main(int argc, char ** argv) Piece::Colour SetupGame(int argc, char ** argv) { - char * red = NULL; char * blue = NULL; double timeout = 0.00001; bool graphics = false; bool allowIllegal = false; FILE * log = NULL; - Piece::Colour reveal = Piece::BOTH; char * inputFile = NULL; int maxTurns = 5000; bool printBoard = false; + char * red = NULL; char * blue = NULL; double stallTime = 0.0; bool graphics = false; bool allowIllegal = false; FILE * log = NULL; + Piece::Colour reveal = Piece::BOTH; char * inputFile = NULL; int maxTurns = 5000; bool printBoard = false; double timeoutTime = 2.0; for (int ii=1; ii < argc; ++ii) { if (argv[ii][0] == '-') @@ -73,15 +73,29 @@ Piece::Colour SetupGame(int argc, char ** argv) case 't': if (argc - ii <= 1) { - fprintf(stderr, "ARGUMENT_ERROR - Expected timeout value after -t switch!\n"); + fprintf(stderr, "ARGUMENT_ERROR - Expected stall time value after -t switch!\n"); exit(EXIT_FAILURE); } if (strcmp(argv[ii+1], "inf") == 0) - timeout = -1; + stallTime = -1; else - timeout = atof(argv[ii+1]); + stallTime = atof(argv[ii+1]); ++ii; break; + + case 'T': + if (argc - ii <= 1) + { + fprintf(stderr, "ARGUMENT_ERROR - Expected timeout value after -T switch!\n"); + exit(EXIT_FAILURE); + } + if (strcmp(argv[ii+1], "inf") == 0) + timeoutTime = -1; + else + timeoutTime = atof(argv[ii+1]); + ++ii; + break; + case 'g': #ifdef BUILD_GRAPHICS graphics = !graphics; @@ -192,7 +206,8 @@ Piece::Colour SetupGame(int argc, char ** argv) } } - + if (graphics && stallTime == 0.0) + stallTime = 0.00001; //Hack so that SDL events (ie SDL_QUIT) will have time to be captured when graphics are enabled if (inputFile == NULL) { @@ -201,11 +216,11 @@ Piece::Colour SetupGame(int argc, char ** argv) fprintf(stderr, "ARGUMENT_ERROR - Did not recieve enough players (did you mean to use the -f switch?)\n"); exit(EXIT_FAILURE); } - Game::theGame = new Game(red,blue, graphics, timeout, allowIllegal,log, reveal,maxTurns, printBoard); + Game::theGame = new Game(red,blue, graphics, stallTime, allowIllegal,log, reveal,maxTurns, printBoard, timeoutTime); } else { - Game::theGame = new Game(inputFile, graphics, timeout, allowIllegal,log, reveal,maxTurns, printBoard); + Game::theGame = new Game(inputFile, graphics, stallTime, allowIllegal,log, reveal,maxTurns, printBoard, timeoutTime); } if (Game::theGame == NULL) @@ -247,7 +262,8 @@ void PrintResults(const MovementResult & result, string & buffer) { switch (result.type) { - case MovementResult::VICTORY: + 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: diff --git a/judge/manager/movementresult.h b/judge/manager/movementresult.h index 5a2aed6..7cd9bf0 100644 --- a/judge/manager/movementresult.h +++ b/judge/manager/movementresult.h @@ -13,7 +13,7 @@ class Piece; class MovementResult { public: - typedef enum {OK, DIES, KILLS, BOTH_DIE, NO_BOARD, INVALID_POSITION, NO_SELECTION, NOT_YOUR_UNIT, IMMOBILE_UNIT, INVALID_DIRECTION, POSITION_FULL, VICTORY, SURRENDER, BAD_RESPONSE, NO_MOVE, COLOUR_ERROR, ERROR, DRAW_DEFAULT, DRAW, BAD_SETUP} Type; + typedef enum {OK, DIES, KILLS, BOTH_DIE, NO_BOARD, INVALID_POSITION, NO_SELECTION, NOT_YOUR_UNIT, IMMOBILE_UNIT, INVALID_DIRECTION, POSITION_FULL, VICTORY_FLAG, VICTORY_ATTRITION, SURRENDER, BAD_RESPONSE, NO_MOVE, COLOUR_ERROR, ERROR, DRAW_DEFAULT, DRAW, BAD_SETUP} Type; MovementResult(const Type & result = OK, const Piece::Type & newAttackerRank = Piece::NOTHING, const Piece::Type & newDefenderRank = Piece::NOTHING) : type(result), attackerRank(newAttackerRank), defenderRank(newDefenderRank) {} diff --git a/judge/manager/program.cpp b/judge/manager/program.cpp index 588f714..d5463f4 100644 --- a/judge/manager/program.cpp +++ b/judge/manager/program.cpp @@ -140,12 +140,13 @@ bool Program::SendMessage(const char * print, ...) */ bool Program::GetMessage(string & buffer, double timeout) { - if (!Running()) + if (!Running() || timeout == 0) return false; assert(&buffer != NULL); GetterThread getterThread(input, buffer); assert(&(getterThread.buffer) != NULL); + TimerThread timerThread(timeout*1000000); getterThread.Start(); @@ -164,7 +165,8 @@ bool Program::GetMessage(string & buffer, double timeout) } getterThread.Stop(); - timerThread.Stop(); + if (timeout > 0) + timerThread.Stop(); diff --git a/judge/manager/stratego.cpp b/judge/manager/stratego.cpp index e7cf60a..5acfaa3 100644 --- a/judge/manager/stratego.cpp +++ b/judge/manager/stratego.cpp @@ -375,7 +375,7 @@ MovementResult Board::MovePiece(int x, int y, const Direction & direction, int m if (defender->type == Piece::FLAG) { winner = target->colour; - return MovementResult(MovementResult::VICTORY); + return MovementResult(MovementResult::VICTORY_FLAG); } else if (defender->type == Piece::BOMB) { diff --git a/judge/manager/stratego.h b/judge/manager/stratego.h index c8f4f9d..d2e667f 100644 --- a/judge/manager/stratego.h +++ b/judge/manager/stratego.h @@ -124,12 +124,12 @@ class Board static bool LegalResult(const MovementResult & result) { - return (result == MovementResult::OK || result == MovementResult::DIES || result == MovementResult::KILLS || result == MovementResult::BOTH_DIE || result == MovementResult::VICTORY || result == MovementResult::DRAW || result == MovementResult::DRAW_DEFAULT || result == MovementResult::SURRENDER); + return (result == MovementResult::OK || result == MovementResult::DIES || result == MovementResult::KILLS || result == MovementResult::BOTH_DIE || result == MovementResult::VICTORY_FLAG || result == MovementResult::VICTORY_ATTRITION || result == MovementResult::DRAW || result == MovementResult::DRAW_DEFAULT || result == MovementResult::SURRENDER); } static bool HaltResult(const MovementResult & result) { - return (result == MovementResult::VICTORY || result == MovementResult::DRAW || result == MovementResult::DRAW_DEFAULT || result == MovementResult::SURRENDER || !LegalResult(result)); + return (result == MovementResult::VICTORY_FLAG || result == MovementResult::VICTORY_ATTRITION || result == MovementResult::DRAW || result == MovementResult::DRAW_DEFAULT || result == MovementResult::SURRENDER || !LegalResult(result)); } MovementResult MovePiece(int x, int y, const Direction & direction, int multiplier=1,const Piece::Colour & colour=Piece::NONE); //Move piece from position in direction diff --git a/judge/simulator/simulate.py b/judge/simulator/simulate.py index b84a525..b44012b 100755 --- a/judge/simulator/simulate.py +++ b/judge/simulator/simulate.py @@ -27,6 +27,8 @@ baseDirectory = "../.." #Base directory for results, logs, agents nGames = 2 #Number of games played by each agent against each opponent. Half will be played as RED, half as BLUE. If nGames <= 1, then no games will be played (useful for dry run?) nRounds = 1 +timeoutValue = 2 + if len(sys.argv) >= 2: nRounds = int(sys.argv[1]) if len(sys.argv) >= 3: @@ -226,7 +228,7 @@ for roundNumber in range(totalRounds, totalRounds + nRounds): if verbose: sys.stdout.write("Agents: \""+red["name"]+"\" and \""+blue["name"]+"\" playing game (ID: " + gameID + ") ... ") logFile = logDirectory + "round"+str(roundNumber) + "/"+red["name"]+".vs."+blue["name"]+"."+str(gameID) - outline = os.popen(managerPath + " -o " + logFile + " " + red["path"] + " " + blue["path"], "r").read() + outline = os.popen(managerPath + " -o " + logFile + " -T " + str(timeoutValue) + " " + red["path"] + " " + blue["path"], "r").read() results = outline.split(' ') if len(results) != 6: diff --git a/web/doc/manager_manual.txt b/web/doc/manager_manual.txt index 7a9c02c..bd9f0e1 100644 --- a/web/doc/manager_manual.txt +++ b/web/doc/manager_manual.txt @@ -5,7 +5,7 @@ WARNING This program is still a work in progress. Consider it a Beta version. SYNOPSIS - stratego {[-gpirb] [-o output_file ] [-t stall_time] [-m max_turns] {red_player blue_player | -f input_file} | {-h | --help} } + stratego {[-gpirb] [-o output_file ] [-t stall_time] [-T timeout_time] [-m max_turns] {red_player blue_player | -f input_file} | {-h | --help} } DESCRIPTION stratego manages a game of Stratego. It stores the state of the board, and uses a simple protocol to interface with AI programs. @@ -69,6 +69,15 @@ OPTIONS It is tentatively planned to allow the user to enter various commands to alter the game or proceed to specified turns. However this is slightly complicated. So it might never be done. + + -T + By default, stratego allows AI programs 2 seconds to respond before declaring their move ILLEGAL due to a timeout. + If the -T switch is present, AI programs will be allowed timeout_time to respond before their move is declared ILLEGAL. + + If timeout_time is negative or "inf", stratego will never declare moves illegal due to timeouts. + + Human players are never subject to timeouts. + -m By default, the game is declared a Draw after 5000 turns have ellapsed. Use this option to change the maximum number of turns. @@ -116,11 +125,12 @@ GAME RULES Each player's pieces are hidden from the other player. When two pieces encounter each other, the ranks will be revealed. - The objective is to destroy all Enemy Pieces (#) or capture the Enemy Flag (also #). + The objective is to either destroy all enemy pieces except the Bombs and Flag, or to capture the Flag. Since 20/12 Bombs reflect the traditional rules; they are only destroyed by Miners. In previous versions contact of an attacker other than a Miner with a Bomb destroyed the Bomb as well as the attacking piece. + PROTOCOL In order to interface with stratego, an AI program must satisfy the following protocol. @@ -181,8 +191,10 @@ PROTOCOL 4. TIMEOUTS - If a program fails to respond to a query within 2 (two) seconds, the game will end and that AI will be sent the ILLEGAL result. + If a program fails to respond to a query, the game will end and that AI will be sent the ILLEGAL result. Human players are not subject to the timeout restriction. + + Please see the information on the -T switch.