/**
* 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
*/
-Board::MovementResult Controller::Setup(const char * opponentName)
+MovementResult Controller::Setup(const char * opponentName)
{
int y;
switch (colour)
if (!GetMessage(line, 2.5))
{
fprintf(stderr, "Timeout on setup\n");
- return Board::BAD_RESPONSE;
+ return MovementResult::BAD_RESPONSE;
}
if ((int)(line.size()) != Board::theBoard.Width())
{
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)
if (usedUnits[type] > Piece::maxUnits[(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);
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;
}
* Queries the AI program to respond to a state of Board::theBoard
* @returns The result of the response and/or move if made
*/
-Board::MovementResult Controller::MakeMove(string & buffer)
+MovementResult Controller::MakeMove(string & buffer)
{
if (!Running())
- return Board::NO_MOVE; //AI has quit
+ return MovementResult::NO_MOVE; //AI has quit
Board::theBoard.Print(output, colour);
buffer.clear();
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="";
else
{
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;
if (s.peek() != EOF)
s >> multiplier;
- Board::MovementResult moveResult = Board::theBoard.MovePiece(x, y, dir, multiplier, colour);
- switch (moveResult)
+ MovementResult moveResult = Board::theBoard.MovePiece(x, y, dir, multiplier, colour);
+
+ 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";
break;
- case Board::VICTORY:
+ case MovementResult::VICTORY:
buffer += " FLAG";
break;
- case Board::KILLS:
- buffer += " KILLS";
+ case MovementResult::KILLS:
+ buffer += " KILLS ";
+ buffer += s.str();
+
break;
- case Board::DIES:
- buffer += " DIES";
+ case MovementResult::DIES:
+ buffer += " DIES ";
+ buffer += s.str();
break;
- case Board::BOTH_DIE:
- buffer += " BOTHDIE";
+ case MovementResult::BOTH_DIE:
+ buffer += " BOTHDIE ";
+ buffer += s.str();
break;
default:
buffer += " ILLEGAL";
}
if (!Board::LegalResult(moveResult))
- return Board::OK; //HACK - Legal results returned!
+ return MovementResult::OK; //HACK - Legal results returned!
else
return moveResult;
Controller(const Piece::Colour & newColour, const char * executablePath) : Program(executablePath), colour(newColour) {}
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.
--- /dev/null
+../samples/forfax/forfax
\ No newline at end of file
Controller * red;
Controller * blue;
+Colour turn;
void cleanup();
+
+void BrokenPipe(int sig);
+
int main(int argc, char ** argv)
{
assert(argc == 3);
red = new Controller(Piece::RED, argv[1]);
blue = new Controller(Piece::BLUE, argv[2]);
atexit(cleanup);
+ signal(SIGPIPE, BrokenPipe);
- Board::MovementResult redSetup = red->Setup(argv[2]);
- Board::MovementResult blueSetup = blue->Setup(argv[1]);
- if (redSetup != Board::OK)
+ MovementResult redSetup = red->Setup(argv[2]);
+ MovementResult blueSetup = blue->Setup(argv[1]);
+ if (redSetup != MovementResult::OK)
{
fprintf(stderr, "Blue wins by DEFAULT!\n");
red->SendMessage("ILLEGAL");
blue->SendMessage("DEFAULT");
exit(EXIT_SUCCESS);
}
- if (blueSetup != Board::OK)
+ if (blueSetup != MovementResult::OK)
{
fprintf(stderr, "Red wins by DEFAULT!\n");
red->SendMessage("DEFAULT");
exit(EXIT_SUCCESS);
}
- Board::MovementResult result = Board::OK;
+ MovementResult result(MovementResult::OK);
system("clear");
int count = 1;
string buffer;
red->SendMessage("START");
- Colour turn = Piece::RED;
+ turn = Piece::RED;
while (Board::LegalResult(result))
{
- fprintf(stderr, "This is move %d...\n", count);
- fprintf(stderr,"---RED's turn---\n");
+
turn = Piece::RED;
+ fprintf(stderr, "%d RED: ", count);
result = red->MakeMove(buffer);
red->SendMessage(buffer);
blue->SendMessage(buffer);
-
+ fprintf(stderr, "%s\n", buffer.c_str());
if (!Board::LegalResult(result))
break;
#ifdef GRAPHICS
exit(EXIT_SUCCESS);
}
#endif //GRAPHICS
- fprintf(stderr,"---BLUE's turn---\n");
+
turn = Piece::BLUE;
+ fprintf(stderr, "%d BLU: ", count);
result = blue->MakeMove(buffer);
blue->SendMessage(buffer);
red->SendMessage(buffer);
+ fprintf(stderr, "%s\n", buffer.c_str());
if (!Board::LegalResult(result))
break;
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");
break;
- case Board::INVALID_POSITION:
+ case MovementResult::INVALID_POSITION:
fprintf(stderr,"Coords outside board\n");
break;
- case Board::NO_SELECTION:
+ case MovementResult::NO_SELECTION:
fprintf(stderr,"Move does not select a piece\n");
break;
- case Board::NOT_YOUR_UNIT:
+ case MovementResult::NOT_YOUR_UNIT:
fprintf(stderr,"Selected piece belongs to other player\n");
break;
- case Board::IMMOBILE_UNIT:
+ case MovementResult::IMMOBILE_UNIT:
fprintf(stderr,"Selected piece is not mobile (FLAG or BOMB)\n");
break;
- case Board::INVALID_DIRECTION:
+ case MovementResult::INVALID_DIRECTION:
fprintf(stderr,"Selected unit cannot move that way\n");
break;
- case Board::POSITION_FULL:
+ case MovementResult::POSITION_FULL:
fprintf(stderr,"Attempted move into square occupied by allied piece\n");
break;
- case Board::VICTORY:
+ case MovementResult::VICTORY:
fprintf(stderr,"Captured the flag\n");
break;
- case Board::BAD_RESPONSE:
+ case MovementResult::BAD_RESPONSE:
fprintf(stderr,"Unintelligable response\n");
break;
- case Board::NO_MOVE:
+ case MovementResult::NO_MOVE:
fprintf(stderr,"Did not make a move (may have exited)\n");
break;
}
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
--- /dev/null
+/**
+ * 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
using namespace std;
+
/**
* Constructor
* @param executablePath - path to the program that will be run
*/
Program::Program(const char * executablePath) : input(NULL), output(NULL), pid(0)
{
+
+
int readPipe[2]; int writePipe[2];
assert(pipe(readPipe) == 0);
assert(pipe(writePipe) == 0);
* Sends a message to the wrapped AI program
* WARNING: Always prints a new line after the message (so don't include a new line)
* 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, ...)
{
va_list ap;
va_start(ap, print);
- vfprintf(output, print, ap);
- fprintf(output, "\n");
-
+ if (vfprintf(output, print, ap) < 0 || fprintf(output, "\n") < 0)
+ {
+ va_end(ap);
+ return false;
+ }
+ va_end(ap);
- va_end(ap);
+
return true;
}
}
+
+
bool Running() const;
+
+
protected:
FILE * input; //Stream used for sending information TO the process
FILE * output; //Stream used for retrieving information FROM the process
* @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
*/
-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)
{
- return NO_BOARD;
+ return MovementResult(MovementResult::NO_BOARD);
}
if (!(x >= 0 && x < width && y >= 0 && y < height))
{
- return INVALID_POSITION;
+ return MovementResult(MovementResult::INVALID_POSITION);
}
Piece * target = board[x][y];
if (target == NULL)
{
- return NO_SELECTION;
+ return MovementResult(MovementResult::NO_SELECTION);
}
if (!(colour == Piece::NONE || target->colour == colour))
{
- return NOT_YOUR_UNIT;
+ return MovementResult(MovementResult::NOT_YOUR_UNIT);
}
if (target->type == Piece::FLAG || target->type == Piece::BOMB || target->type == Piece::BOULDER)
{
- return IMMOBILE_UNIT;
+ return MovementResult(MovementResult::IMMOBILE_UNIT);
}
if (multiplier > 1 && target->type != Piece::SCOUT)
{
- return INVALID_DIRECTION; //Can only move a scout multiple times.
+ return MovementResult(MovementResult::INVALID_DIRECTION); //Can only move a scout multiple times.
}
int x2 = x; int y2 = y;
}
if (!(x2 >= 0 && x2 < width && y2 >= 0 && y2 < height))
{
- return INVALID_DIRECTION;
+ return MovementResult(MovementResult::INVALID_DIRECTION);
}
if (ii < multiplier-1 && board[x2][y2] != NULL)
{
- return POSITION_FULL;
+ return MovementResult(MovementResult::POSITION_FULL);
}
}
Piece * defender = board[x2][y2];
}
else if (defender->colour != target->colour)
{
+ Piece::Type defenderType = defender->type;
+ Piece::Type attackerType = target->type;
+
if (defender->colour == Piece::NONE)
{
- return POSITION_FULL;
+ return MovementResult(MovementResult::POSITION_FULL);
}
if (defender->type == Piece::FLAG)
{
winner = target->colour;
- return VICTORY;
+ return MovementResult(MovementResult::VICTORY);
}
else if (defender->type == Piece::BOMB)
{
if (target->type == Piece::MINER)
{
+
delete defender;
board[x][y] = NULL;
board[x2][y2] = target;
- return KILLS;
+ return MovementResult(MovementResult::KILLS, attackerType, defenderType);
}
else
{
delete target;
board[x][y] = NULL;
board[x2][y2] = NULL;
- return BOTH_DIE;
+ return MovementResult(MovementResult::BOTH_DIE, attackerType, defenderType);
}
}
else if (defender->type == Piece::MARSHAL && target->type == Piece::SPY)
delete defender;
board[x][y] = NULL;
board[x2][y2] = target;
- return KILLS;
+ return MovementResult(MovementResult::KILLS, attackerType, defenderType);
}
else if (target->operator > (*defender))
{
delete defender;
board[x][y] = NULL;
board[x2][y2] = target;
- return KILLS;
+ return MovementResult(MovementResult::KILLS, attackerType, defenderType);
}
else if (target->operator==(*defender) && rand() % 2 == 0)
{
delete defender;
board[x][y] = NULL;
board[x2][y2] = target;
- return KILLS;
+ return MovementResult(MovementResult::KILLS, attackerType, defenderType);
}
else
{
delete target;
board[x][y] = NULL;
- return DIES;
+ return MovementResult(MovementResult::DIES, attackerType, defenderType);
}
}
else
{
- return POSITION_FULL;
+ return MovementResult(MovementResult::POSITION_FULL);
}
- return OK;
+ return MovementResult(MovementResult::OK);
}
};
+#include "movementresult.h"
+
/**
* A Stratego board
*/
typedef enum {UP, DOWN, LEFT, RIGHT} Direction;
- 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} MovementResult; //The possible results from a move
- //WARNING: Some of the MovementResults are returned by the Controller class in "controller.h", in Controller::MakeMove
+
static bool LegalResult(const MovementResult & result)
{
- return (result == OK || result == DIES || result == KILLS || result == BOTH_DIE);
+ return (result == MovementResult::OK || result == MovementResult::DIES || result == MovementResult::KILLS || result == MovementResult::BOTH_DIE);
}
MovementResult MovePiece(int x, int y, const Direction & direction, int multiplier=1,const Piece::Colour & colour=Piece::NONE); //Move piece from position in direction
bool Forfax::InterpretMove()
{
- int x; int y; string direction; string result; int multiplier = 1;
+ int x; int y; string direction; string result; int multiplier = 1; int attackerVal = (int)(Piece::BOMB); int defenderVal = (int)(Piece::BOMB);
cerr << "Forfax " << strColour << " waiting for movement information...\n";
cin >> x; cin >> y; cin >> direction; cin >> result;
s >> multiplier;
result.clear();
cin >> result;
+
+ if (cin.peek() != '\n')
+ {
+ cerr << "Forfax " << strColour << " reading ranks of pieces\n";
+ s.clear(); s.str(result);
+ s >> attackerVal;
+ result.clear();
+ cin >> result;
+ s.clear(); s.str(result);
+ s >> defenderVal;
+ result.clear();
+
+ cin >> result;
+ }
}
if (cin.get() != '\n')
{
return false;
}
+ Piece::Type attackerRank = Piece::Type(Piece::BOMB - attackerVal);
+ Piece::Type defenderRank = Piece::Type(Piece::BOMB - defenderVal);
+
cerr << "Forfax " << strColour << " interpreting movement result of " << x << " " << y << " " << direction << " " << result << " ...\n";
using namespace std;
#include <stdio.h>
-
+void quit();
Forfax forfax;
int main(int argc, char ** argv)
{
setbuf(stdin, NULL);
setbuf(stdout, NULL);
+ atexit(&quit);
+
if (!forfax.Setup())
exit(EXIT_SUCCESS);
}
+
+void quit()
+{
+ cerr << " Forfax quit\n";
+}
<tr> <th> OUTCOME </th> <th> Description </th> </tr>
<tr> <td> OK </td> <td> The piece was successfully moved, nothing eventful happened </td> </tr>
<tr> <td> FLAG </td> <td> The piece moved onto the opposing Flag; the game will end shortly </td> </tr>
- <tr> <td> KILLS </td> <td> The piece landed on a square occupied by an enemy, and destroyed it, moving into the square </td> </tr>
- <tr> <td> DIES </td> <td> The piece landed on a square occupied by an enemy, and was destroyed. The piece is removed from the board. </td> </tr>
- <tr> <td> BOTHDIE </td> <td> The piece landed on a square occupied by an enemy, and <i>both</i> pieces were destroyed. </td> </tr>
+ <tr> <td> KILLS RANK1 RANK2 </td> <td> The piece landed on a square occupied by an enemy, and destroyed it, moving into the square </td> </tr>
+ <tr> <td> DIES RANK1 RANK2</td> <td> The piece landed on a square occupied by an enemy, and was destroyed. The piece is removed from the board. </td> </tr>
+ <tr> <td> BOTHDIE RANK1 RANK2</td> <td> The piece landed on a square occupied by an enemy, and <i>both</i> pieces were destroyed. </td> </tr>
<tr> <td> ILLEGAL </td> <td> The moving player attempted to make an illegal move, and has hence lost the game. The game will end shortly. </td> </tr>
</table>
+<p> If printed, RANK1 and RANK2 indicate the ranks of the moved piece and the defending piece (the piece who occupied the destination square) respectively. </p>
<h4> Additional Turns </h4>