Commit 2ab27eb6 authored by Sam Moore's avatar Sam Moore
Browse files

Modified Turn Response Protocol, added handling for SIGPIPE, changed placeholder images

The "outcome" of a move is now listed as:

TYPE [ATTACKER_RANK] [DEFENDER_RANK]

Where ATTACKER_RANK and DEFENDER_RANK will be present if TYPE is one of: KILLS, DIES, BOTHDIE, and indicate the ranks of the pieces involved.
This involved adding a class MovementResult, which stores the ranks of pieces in addition to an enum, replacing the enum Board::MovementResult

The sample agent "forfax" was causing broken pipes, which caused the manager program to exit.
I added a handler for SIGPIPE in manager/main.cpp to ensure that the manager program reports a DEFAULT victory to the other AI, and exits gracefully.
However, I still don't know WHY forfax causes broken pipes, but hopefully its a problem with forfax and not with the manager program.

I edited the images used by the graphical display to show the ordered ranks of the pieces, rather than some obscure characters.
Unfortunately I have just realised that the enum used for Piece::Type stores ranks in the wrong order.
In the actual game, LOWER numbers are better, in my enum, HIGHER numbers are better.
To make things more confusing, I made the printed ATTACKER_RANK and DEFENDER_RANK correspond to the traditional numbering, not the enum numbering...
parent f91a915d
...@@ -8,9 +8,10 @@ using namespace std; ...@@ -8,9 +8,10 @@ using namespace std;
/** /**
* Queries the AI program to setup its pieces * Queries the AI program to setup its pieces
* @param opponentName - string containing the name/id of the opponent AI program
* @returns the result of the response * @returns the result of the response
*/ */
Board::MovementResult Controller::Setup(const char * opponentName) MovementResult Controller::Setup(const char * opponentName)
{ {
int y; int y;
switch (colour) switch (colour)
...@@ -48,12 +49,12 @@ Board::MovementResult Controller::Setup(const char * opponentName) ...@@ -48,12 +49,12 @@ Board::MovementResult Controller::Setup(const char * opponentName)
if (!GetMessage(line, 2.5)) if (!GetMessage(line, 2.5))
{ {
fprintf(stderr, "Timeout on setup\n"); fprintf(stderr, "Timeout on setup\n");
return Board::BAD_RESPONSE; return MovementResult::BAD_RESPONSE;
} }
if ((int)(line.size()) != Board::theBoard.Width()) if ((int)(line.size()) != Board::theBoard.Width())
{ {
fprintf(stderr, "Bad length of \"%s\" on setup\n", line.c_str()); fprintf(stderr, "Bad length of \"%s\" on setup\n", line.c_str());
return Board::BAD_RESPONSE; return MovementResult::BAD_RESPONSE;
} }
for (int x = 0; x < (int)(line.size()); ++x) for (int x = 0; x < (int)(line.size()); ++x)
...@@ -69,7 +70,7 @@ Board::MovementResult Controller::Setup(const char * opponentName) ...@@ -69,7 +70,7 @@ Board::MovementResult Controller::Setup(const char * opponentName)
if (usedUnits[type] > Piece::maxUnits[(int)type]) if (usedUnits[type] > Piece::maxUnits[(int)type])
{ {
fprintf(stderr, "Too many units of type %c\n", Piece::tokens[(int)(type)]); fprintf(stderr, "Too many units of type %c\n", Piece::tokens[(int)(type)]);
return Board::BAD_RESPONSE; return MovementResult::BAD_RESPONSE;
} }
Board::theBoard.AddPiece(x, y+ii, type, colour); Board::theBoard.AddPiece(x, y+ii, type, colour);
...@@ -79,10 +80,10 @@ Board::MovementResult Controller::Setup(const char * opponentName) ...@@ -79,10 +80,10 @@ Board::MovementResult Controller::Setup(const char * opponentName)
if (usedUnits[(int)Piece::FLAG] <= 0) if (usedUnits[(int)Piece::FLAG] <= 0)
{ {
return Board::BAD_RESPONSE; //You need to include a flag! return MovementResult::BAD_RESPONSE; //You need to include a flag!
} }
return Board::OK; return MovementResult::OK;
} }
...@@ -90,11 +91,11 @@ Board::MovementResult Controller::Setup(const char * opponentName) ...@@ -90,11 +91,11 @@ Board::MovementResult Controller::Setup(const char * opponentName)
* Queries the AI program to respond to a state of Board::theBoard * Queries the AI program to respond to a state of Board::theBoard
* @returns The result of the response and/or move if made * @returns The result of the response and/or move if made
*/ */
Board::MovementResult Controller::MakeMove(string & buffer) MovementResult Controller::MakeMove(string & buffer)
{ {
if (!Running()) if (!Running())
return Board::NO_MOVE; //AI has quit return MovementResult::NO_MOVE; //AI has quit
Board::theBoard.Print(output, colour); Board::theBoard.Print(output, colour);
...@@ -103,7 +104,7 @@ Board::MovementResult Controller::MakeMove(string & buffer) ...@@ -103,7 +104,7 @@ Board::MovementResult Controller::MakeMove(string & buffer)
buffer.clear(); buffer.clear();
if (!GetMessage(buffer,2)) if (!GetMessage(buffer,2))
{ {
return Board::NO_MOVE; //AI did not respond. It will lose by default. return MovementResult::NO_MOVE; //AI did not respond. It will lose by default.
} }
int x; int y; string direction=""; int x; int y; string direction="";
...@@ -133,29 +134,39 @@ Board::MovementResult Controller::MakeMove(string & buffer) ...@@ -133,29 +134,39 @@ Board::MovementResult Controller::MakeMove(string & buffer)
else else
{ {
fprintf(stderr, "BAD_RESPONSE \"%s\"\n", buffer.c_str()); fprintf(stderr, "BAD_RESPONSE \"%s\"\n", buffer.c_str());
return Board::BAD_RESPONSE; //AI gave bogus direction - it will lose by default. return MovementResult::BAD_RESPONSE; //AI gave bogus direction - it will lose by default.
} }
int multiplier = 1; int multiplier = 1;
if (s.peek() != EOF) if (s.peek() != EOF)
s >> multiplier; s >> multiplier;
Board::MovementResult moveResult = Board::theBoard.MovePiece(x, y, dir, multiplier, colour); MovementResult moveResult = Board::theBoard.MovePiece(x, y, dir, multiplier, colour);
switch (moveResult)
s.clear(); s.str("");
//I stored the ranks in the wrong order; rank 1 is the marshal, 2 is the general etc...
//So I am reversing them in the output... great work
s << (Piece::BOMB - moveResult.attackerRank) << " " << (Piece::BOMB - moveResult.defenderRank) << "\n";
switch (moveResult.type)
{ {
case Board::OK: case MovementResult::OK:
buffer += " OK"; buffer += " OK";
break; break;
case Board::VICTORY: case MovementResult::VICTORY:
buffer += " FLAG"; buffer += " FLAG";
break; break;
case Board::KILLS: case MovementResult::KILLS:
buffer += " KILLS"; buffer += " KILLS ";
buffer += s.str();
break; break;
case Board::DIES: case MovementResult::DIES:
buffer += " DIES"; buffer += " DIES ";
buffer += s.str();
break; break;
case Board::BOTH_DIE: case MovementResult::BOTH_DIE:
buffer += " BOTHDIE"; buffer += " BOTHDIE ";
buffer += s.str();
break; break;
default: default:
buffer += " ILLEGAL"; buffer += " ILLEGAL";
...@@ -164,7 +175,7 @@ Board::MovementResult Controller::MakeMove(string & buffer) ...@@ -164,7 +175,7 @@ Board::MovementResult Controller::MakeMove(string & buffer)
} }
if (!Board::LegalResult(moveResult)) if (!Board::LegalResult(moveResult))
return Board::OK; //HACK - Legal results returned! return MovementResult::OK; //HACK - Legal results returned!
else else
return moveResult; return moveResult;
......
...@@ -15,9 +15,9 @@ class Controller : public Program ...@@ -15,9 +15,9 @@ class Controller : public Program
Controller(const Piece::Colour & newColour, const char * executablePath) : Program(executablePath), colour(newColour) {} Controller(const Piece::Colour & newColour, const char * executablePath) : Program(executablePath), colour(newColour) {}
virtual ~Controller() {} virtual ~Controller() {}
Board::MovementResult Setup(const char * opponentName); //Requests the AI program for the initial positioning of its pieces. MovementResult Setup(const char * opponentName); //Requests the AI program for the initial positioning of its pieces.
Board::MovementResult MakeMove(std::string & buffer); //Queries the AI program for a response to the state of Board::theBoard MovementResult MakeMove(std::string & buffer); //Queries the AI program for a response to the state of Board::theBoard
const Piece::Colour colour; //Colour identifying the side of the AI program. const Piece::Colour colour; //Colour identifying the side of the AI program.
......
../samples/forfax/forfax
\ No newline at end of file
manager/images/piece10.bmp

1.29 KB | W: | H:

manager/images/piece10.bmp

1.3 KB | W: | H:

manager/images/piece10.bmp
manager/images/piece10.bmp
manager/images/piece10.bmp
manager/images/piece10.bmp
  • 2-up
  • Swipe
  • Onion skin
manager/images/piece11.bmp

1.32 KB | W: | H:

manager/images/piece11.bmp

1.27 KB | W: | H:

manager/images/piece11.bmp
manager/images/piece11.bmp
manager/images/piece11.bmp
manager/images/piece11.bmp
  • 2-up
  • Swipe
  • Onion skin
manager/images/piece12.bmp

1.34 KB | W: | H:

manager/images/piece12.bmp

1.13 KB | W: | H:

manager/images/piece12.bmp
manager/images/piece12.bmp
manager/images/piece12.bmp
manager/images/piece12.bmp
  • 2-up
  • Swipe
  • Onion skin
manager/images/piece13.bmp

1.34 KB | W: | H:

manager/images/piece13.bmp

1.09 KB | W: | H:

manager/images/piece13.bmp
manager/images/piece13.bmp
manager/images/piece13.bmp
manager/images/piece13.bmp
  • 2-up
  • Swipe
  • Onion skin
manager/images/piece2.bmp

1.21 KB | W: | H:

manager/images/piece2.bmp

1.07 KB | W: | H:

manager/images/piece2.bmp
manager/images/piece2.bmp
manager/images/piece2.bmp
manager/images/piece2.bmp
  • 2-up
  • Swipe
  • Onion skin
manager/images/piece3.bmp

1.28 KB | W: | H:

manager/images/piece3.bmp

1.38 KB | W: | H:

manager/images/piece3.bmp
manager/images/piece3.bmp
manager/images/piece3.bmp
manager/images/piece3.bmp
  • 2-up
  • Swipe
  • Onion skin
manager/images/piece4.bmp

1.28 KB | W: | H:

manager/images/piece4.bmp

1.31 KB | W: | H:

manager/images/piece4.bmp
manager/images/piece4.bmp
manager/images/piece4.bmp
manager/images/piece4.bmp
  • 2-up
  • Swipe
  • Onion skin
manager/images/piece5.bmp

1.23 KB | W: | H:

manager/images/piece5.bmp

1.34 KB | W: | H:

manager/images/piece5.bmp
manager/images/piece5.bmp
manager/images/piece5.bmp
manager/images/piece5.bmp
  • 2-up
  • Swipe
  • Onion skin
manager/images/piece6.bmp

1.22 KB | W: | H:

manager/images/piece6.bmp

1.2 KB | W: | H:

manager/images/piece6.bmp
manager/images/piece6.bmp
manager/images/piece6.bmp
manager/images/piece6.bmp
  • 2-up
  • Swipe
  • Onion skin
manager/images/piece7.bmp

1.31 KB | W: | H:

manager/images/piece7.bmp

1.35 KB | W: | H:

manager/images/piece7.bmp
manager/images/piece7.bmp
manager/images/piece7.bmp
manager/images/piece7.bmp
  • 2-up
  • Swipe
  • Onion skin
manager/images/piece8.bmp

1.18 KB | W: | H:

manager/images/piece8.bmp

1.24 KB | W: | H:

manager/images/piece8.bmp
manager/images/piece8.bmp
manager/images/piece8.bmp
manager/images/piece8.bmp
  • 2-up
  • Swipe
  • Onion skin
manager/images/piece9.bmp

1.25 KB | W: | H:

manager/images/piece9.bmp

1.19 KB | W: | H:

manager/images/piece9.bmp
manager/images/piece9.bmp
manager/images/piece9.bmp
manager/images/piece9.bmp
  • 2-up
  • Swipe
  • Onion skin
...@@ -18,8 +18,12 @@ using namespace std; ...@@ -18,8 +18,12 @@ using namespace std;
Controller * red; Controller * red;
Controller * blue; Controller * blue;
Colour turn;
void cleanup(); void cleanup();
void BrokenPipe(int sig);
int main(int argc, char ** argv) int main(int argc, char ** argv)
{ {
assert(argc == 3); assert(argc == 3);
...@@ -39,17 +43,18 @@ int main(int argc, char ** argv) ...@@ -39,17 +43,18 @@ int main(int argc, char ** argv)
red = new Controller(Piece::RED, argv[1]); red = new Controller(Piece::RED, argv[1]);
blue = new Controller(Piece::BLUE, argv[2]); blue = new Controller(Piece::BLUE, argv[2]);
atexit(cleanup); atexit(cleanup);
signal(SIGPIPE, BrokenPipe);
Board::MovementResult redSetup = red->Setup(argv[2]); MovementResult redSetup = red->Setup(argv[2]);
Board::MovementResult blueSetup = blue->Setup(argv[1]); MovementResult blueSetup = blue->Setup(argv[1]);
if (redSetup != Board::OK) if (redSetup != MovementResult::OK)
{ {
fprintf(stderr, "Blue wins by DEFAULT!\n"); fprintf(stderr, "Blue wins by DEFAULT!\n");
red->SendMessage("ILLEGAL"); red->SendMessage("ILLEGAL");
blue->SendMessage("DEFAULT"); blue->SendMessage("DEFAULT");
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
if (blueSetup != Board::OK) if (blueSetup != MovementResult::OK)
{ {
fprintf(stderr, "Red wins by DEFAULT!\n"); fprintf(stderr, "Red wins by DEFAULT!\n");
red->SendMessage("DEFAULT"); red->SendMessage("DEFAULT");
...@@ -57,7 +62,7 @@ int main(int argc, char ** argv) ...@@ -57,7 +62,7 @@ int main(int argc, char ** argv)
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
Board::MovementResult result = Board::OK; MovementResult result(MovementResult::OK);
system("clear"); system("clear");
int count = 1; int count = 1;
...@@ -70,17 +75,17 @@ int main(int argc, char ** argv) ...@@ -70,17 +75,17 @@ int main(int argc, char ** argv)
string buffer; string buffer;
red->SendMessage("START"); red->SendMessage("START");
Colour turn = Piece::RED; turn = Piece::RED;
while (Board::LegalResult(result)) while (Board::LegalResult(result))
{ {
fprintf(stderr, "This is move %d...\n", count);
fprintf(stderr,"---RED's turn---\n");
turn = Piece::RED; turn = Piece::RED;
fprintf(stderr, "%d RED: ", count);
result = red->MakeMove(buffer); result = red->MakeMove(buffer);
red->SendMessage(buffer); red->SendMessage(buffer);
blue->SendMessage(buffer); blue->SendMessage(buffer);
fprintf(stderr, "%s\n", buffer.c_str());
if (!Board::LegalResult(result)) if (!Board::LegalResult(result))
break; break;
#ifdef GRAPHICS #ifdef GRAPHICS
...@@ -92,11 +97,13 @@ int main(int argc, char ** argv) ...@@ -92,11 +97,13 @@ int main(int argc, char ** argv)
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
#endif //GRAPHICS #endif //GRAPHICS
fprintf(stderr,"---BLUE's turn---\n");
turn = Piece::BLUE; turn = Piece::BLUE;
fprintf(stderr, "%d BLU: ", count);
result = blue->MakeMove(buffer); result = blue->MakeMove(buffer);
blue->SendMessage(buffer); blue->SendMessage(buffer);
red->SendMessage(buffer); red->SendMessage(buffer);
fprintf(stderr, "%s\n", buffer.c_str());
if (!Board::LegalResult(result)) if (!Board::LegalResult(result))
break; break;
...@@ -148,36 +155,36 @@ int main(int argc, char ** argv) ...@@ -148,36 +155,36 @@ int main(int argc, char ** argv)
fprintf(stderr,"Game ends on ERROR's turn - REASON: "); fprintf(stderr,"Game ends on ERROR's turn - REASON: ");
} }
switch (result) switch (result.type)
{ {
case Board::NO_BOARD: case MovementResult::NO_BOARD:
fprintf(stderr,"Board does not exit?!\n"); fprintf(stderr,"Board does not exit?!\n");
break; break;
case Board::INVALID_POSITION: case MovementResult::INVALID_POSITION:
fprintf(stderr,"Coords outside board\n"); fprintf(stderr,"Coords outside board\n");
break; break;
case Board::NO_SELECTION: case MovementResult::NO_SELECTION:
fprintf(stderr,"Move does not select a piece\n"); fprintf(stderr,"Move does not select a piece\n");
break; break;
case Board::NOT_YOUR_UNIT: case MovementResult::NOT_YOUR_UNIT:
fprintf(stderr,"Selected piece belongs to other player\n"); fprintf(stderr,"Selected piece belongs to other player\n");
break; break;
case Board::IMMOBILE_UNIT: case MovementResult::IMMOBILE_UNIT:
fprintf(stderr,"Selected piece is not mobile (FLAG or BOMB)\n"); fprintf(stderr,"Selected piece is not mobile (FLAG or BOMB)\n");
break; break;
case Board::INVALID_DIRECTION: case MovementResult::INVALID_DIRECTION:
fprintf(stderr,"Selected unit cannot move that way\n"); fprintf(stderr,"Selected unit cannot move that way\n");
break; break;
case Board::POSITION_FULL: case MovementResult::POSITION_FULL:
fprintf(stderr,"Attempted move into square occupied by allied piece\n"); fprintf(stderr,"Attempted move into square occupied by allied piece\n");
break; break;
case Board::VICTORY: case MovementResult::VICTORY:
fprintf(stderr,"Captured the flag\n"); fprintf(stderr,"Captured the flag\n");
break; break;
case Board::BAD_RESPONSE: case MovementResult::BAD_RESPONSE:
fprintf(stderr,"Unintelligable response\n"); fprintf(stderr,"Unintelligable response\n");
break; break;
case Board::NO_MOVE: case MovementResult::NO_MOVE:
fprintf(stderr,"Did not make a move (may have exited)\n"); fprintf(stderr,"Did not make a move (may have exited)\n");
break; break;
} }
...@@ -221,4 +228,24 @@ void cleanup() ...@@ -221,4 +228,24 @@ void cleanup()
delete blue; delete blue;
} }
void BrokenPipe(int sig)
{
if (turn == Piece::RED)
{
fprintf(stderr,"Game ends on RED's turn - REASON: Broken pipe\n");
blue->SendMessage("DEFAULT");
}
else if (turn == Piece::BLUE)
{
fprintf(stderr,"Game ends on BLUE's turn - REASON: Broken pipe\n");
red->SendMessage("DEFAULT");
}
else
{
fprintf(stderr,"Game ends on ERROR's turn - REASON: Broken pipe\n");
}
exit(EXIT_SUCCESS);
}
#endif //GRAPHICS #endif //GRAPHICS
/**
* Contains declaration for MovementResult class
*/
#ifndef MOVERESULT_H
#define MOVERESULT_H
class Board;
class Piece;
/**
* Class used to indicate the result of a move in stratego
*/
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, BAD_RESPONSE, NO_MOVE} Type;
MovementResult(const Type & result = OK, const Piece::Type & newAttackerRank = Piece::NOTHING, const Piece::Type & newDefenderRank = Piece::NOTHING)
: type(result), attackerRank(newAttackerRank), defenderRank(newDefenderRank) {}
MovementResult(const MovementResult & cpy) : type(cpy.type), attackerRank(cpy.attackerRank), defenderRank(cpy.defenderRank) {}
virtual ~MovementResult() {}
bool operator==(const Type & equType) const {return type == equType;}
bool operator!=(const Type & equType) const {return type != equType;}
Type type;
Piece::Type attackerRank;
Piece::Type defenderRank;
};
#endif //MOVERESULT_H
//EOF
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
using namespace std; using namespace std;
/** /**
* Constructor * Constructor
* @param executablePath - path to the program that will be run * @param executablePath - path to the program that will be run
...@@ -21,6 +22,8 @@ using namespace std; ...@@ -21,6 +22,8 @@ using namespace std;
*/ */
Program::Program(const char * executablePath) : input(NULL), output(NULL), pid(0) Program::Program(const char * executablePath) : input(NULL), output(NULL), pid(0)
{ {
int readPipe[2]; int writePipe[2]; int readPipe[2]; int writePipe[2];
assert(pipe(readPipe) == 0); assert(pipe(readPipe) == 0);
assert(pipe(writePipe) == 0); assert(pipe(writePipe) == 0);
...@@ -88,6 +91,7 @@ Program::~Program() ...@@ -88,6 +91,7 @@ Program::~Program()
* Sends a message to the wrapped AI program * Sends a message to the wrapped AI program
* WARNING: Always prints a new line after the message (so don't include a new line) * WARNING: Always prints a new line after the message (so don't include a new line)
* This is because everything is always line buffered. * This is because everything is always line buffered.
* @returns true if the message was successfully sent; false if it was not (ie: the process was not running!)
*/ */
bool Program::SendMessage(const char * print, ...) bool Program::SendMessage(const char * print, ...)
{ {
...@@ -97,12 +101,15 @@ bool Program::SendMessage(const char * print, ...) ...@@ -97,12 +101,15 @@ bool Program::SendMessage(const char * print, ...)
va_list ap; va_list ap;
va_start(ap, print); va_start(ap, print);
vfprintf(output, print, ap); if (vfprintf(output, print, ap) < 0 || fprintf(output, "\n") < 0)
fprintf(output, "\n"); {
va_end(ap);
return false;
}
va_end(ap);
va_end(ap);
return true; return true;
} }
...@@ -161,3 +168,5 @@ bool Program::Running() const ...@@ -161,3 +168,5 @@ bool Program::Running() const
} }
...@@ -24,6 +24,8 @@ class Program ...@@ -24,6 +24,8 @@ class Program
bool Running() const; bool Running() const;
protected: protected:
FILE * input; //Stream used for sending information TO the process FILE * input; //Stream used for sending information TO the process
FILE * output; //Stream used for retrieving information FROM the process FILE * output; //Stream used for retrieving information FROM the process
......
...@@ -232,32 +232,32 @@ Piece * Board::GetPiece(int x, int y) ...@@ -232,32 +232,32 @@ Piece * Board::GetPiece(int x, int y)
* @param colour - Colour which the piece must match for the move to be valid * @param colour - Colour which the piece must match for the move to be valid
* @returns A MovementResult which indicates the result of the move - OK is good, VICTORY means that a flag was captured, anything else is an error * @returns A MovementResult which indicates the result of the move - OK is good, VICTORY means that a flag was captured, anything else is an error
*/ */
Board::MovementResult Board::MovePiece(int x, int y, const Direction & direction, int multiplier,const Piece::Colour & colour) MovementResult Board::MovePiece(int x, int y, const Direction & direction, int multiplier,const Piece::Colour & colour)
{ {
if (board == NULL) if (board == NULL)
{ {
return NO_BOARD; return MovementResult(MovementResult::NO_BOARD);
} }
if (!(x >= 0 && x < width && y >= 0 && y < height)) if (!(x >= 0 && x < width && y >= 0 && y < height))
{ {
return INVALID_POSITION; return MovementResult(MovementResult::INVALID_POSITION);
} }
Piece * target = board[x][y]; Piece * target = board[x][y];
if (target == NULL)