Commit 7f7bc054 authored by Sam Moore's avatar Sam Moore

Fixed segfault in manager program

Caused by the Program base class attempting to write EOF to programs
which had already exited. This caused a SIGPIPE signal.

The SIGPIPE handler function Game::HandleBrokenPipe then attempted to log the
event by calling Game::theGame->logMessage

However, since the program was exiting, DestroyGame had been called
and Game::theGame was in the process of being deleted.

Fixed by removing the fputc(output, EOF) line in Program::~Program.

It is ironic that this only became an issue since I modified the sample
AI to actually obey the protocol and exit as soon as "QUIT" is recieved...
parent 17a20de4
......@@ -117,8 +117,10 @@ MovementResult Controller::MakeMove(string & buffer)
}
else
{
//fprintf(stderr, "BAD_RESPONSE \"%s\"\n", buffer.c_str());
return MovementResult::BAD_RESPONSE; //Player gave bogus direction - it will lose by default.
if (Game::theGame->allowIllegalMoves)
return MovementResult::OK;
else
return MovementResult::BAD_RESPONSE; //Player gave bogus direction - it will lose by default.
}
int multiplier = 1;
......@@ -158,10 +160,15 @@ MovementResult Controller::MakeMove(string & buffer)
}
if (!Board::LegalResult(moveResult))
{
if (Game::theGame->allowIllegalMoves)
{
return MovementResult::OK; //HACK - Illegal results returned as legal! (Move not made)
}
else if (this->HumanController()) //Cut human controllers some slack and let them try again...
{
//Yes, checking type of object is "not the C++ way"
......
......@@ -205,6 +205,11 @@ void Game::Wait(double wait)
void Game::HandleBrokenPipe(int sig)
{
if (theGame == NULL)
{
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: ");
......
......@@ -88,7 +88,7 @@ Piece::Colour SetupGame(int argc, char ** argv)
printBoard = !printBoard;
break;
case 'i':
allowIllegal = !allowIllegal;
allowIllegal = true;
break;
case 'o':
......
......@@ -81,7 +81,7 @@ Program::~Program()
{
if (Running()) //Check if the process created is still running...
{
fputc(EOF, output); //If it was, tell it to stop with EOF
//fputc(EOF, output); //If it was, tell it to stop with EOF
TimerThread timer(2); //Wait for 2 seconds
timer.Start();
......
#Makefile for the sample AI programs for UCC progcomp 2012
CPP = g++ -Wall -pedantic -lSDL -lGL -g
dummy : dummy.o
$(CPP) -o dummy dummy.o
clean :
rm -f dummy.o
rm -f dummy
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <cassert>
#include <string>
#include <iostream>
#include <sstream>
using namespace std;
/**
* A suitably terrible program which combines C style IO with C++ style IO
* Enjoy!
* Mwuhahaha
*/
int main(int argc, char ** argv)
{
setbuf(stdout, NULL);
setbuf(stdin, NULL);
srand(time(NULL));
//Read in the colour, and choose a layout
int width = 14; int height = 14;
string colour; string opponent;
cin >> colour; cin >> opponent; cin >> width; cin >> height;
fgetc(stdin);
//fprintf(stderr, "Colour is \"%s\", width and height are (%d, %d), opponent is \"%s\"\n", colour.c_str(), width, height, opponent.c_str());
assert(width == 10 && height == 10); //Can't deal with other sized boards
if (colour == "RED")
{
fprintf(stdout, "FB8sB479B8\n");
fprintf(stdout, "BB31555583\n");
fprintf(stdout, "6724898974\n");
fprintf(stdout, "967B669999\n");
}
else if (colour == "BLUE")
{
fprintf(stdout, "967B669999\n");
fprintf(stdout, "6724898974\n");
fprintf(stdout, "BB31555583\n");
fprintf(stdout, "FB8sB479B8\n");
}
else
{
return 1;
}
char board[width][height];
vector<pair<int, int> > choices;
int myPid = (int)(getpid());
while (true)
{
//fprintf(stderr, "%s [%d] looping\n", argv[0], myPid);
choices.clear();
//fprintf(stderr, "%s Waiting for status line...\n", colour.c_str());
char c = fgetc(stdin);
while (c != '\n')
{
//fprintf(stderr,"%c",c);
c = fgetc(stdin);
}
//fprintf(stderr, "%s Got status, waiting for board line...\n", colour.c_str());
//Read in board
for (int y=0; y < height; ++y)
{
for (int x=0; x < width; ++x)
{
board[x][y] = fgetc(stdin);
if (board[x][y] == EOF)
exit(EXIT_SUCCESS);
if (board[x][y] != '.' && board[x][y] != '*' && board[x][y] != '#' && board[x][y] != '+')
{
choices.push_back(pair<int, int>(x, y));
}
}
assert(fgetc(stdin) == '\n');
}
int dir = 0; int startDir = 0; int choice = rand() % choices.size(); int startChoice = choice;
int x1 = 0; int y1 = 0;
do
{
pair<int,int> pear = choices[choice];
x1 = pear.first;
y1 = pear.second;
//fprintf(stderr,"Trying unit at %d %d...\n", x1, y1);
if (board[x1][y1] == 'B' || board[x1][y1] == 'F')
{
choice = (choice+1) % choices.size();
continue;
}
int x2 = x1;
int y2 = y1;
dir = rand() % 4; startDir = dir; int lastDir = dir;
bool okay = false;
while (!okay)
{
//fprintf(stderr," Trying direction %d...\n", dir);
x2 = x1; y2 = y1;
switch (dir)
{
case 0:
--y2;
break;
case 1:
++y2;
break;
case 2:
--x2;
break;
case 3:
++x2;
break;
}
okay = !(x2 < 0 || y2 < 0 || x2 >= width || y2 >= height || (board[x2][y2] != '.' && board[x2][y2] != '*' && board[x2][y2] != '#'));
if (!okay)
{
dir = (dir+1) % 4;
if (dir == startDir)
break;
}
}
choice = (choice+1) % choices.size();
if (dir != startDir)
break;
}
while (choice != startChoice);
string direction="";
switch (dir)
{
case 0:
direction = "UP";
break;
case 1:
direction = "DOWN";
break;
case 2:
direction = "LEFT";
break;
case 3:
direction = "RIGHT";
break;
}
printf("%d %d %s\n", x1, y1, direction.c_str());
//fprintf(stderr,"%s Made move, waiting for confirmation line\n", colour.c_str());
while (fgetc(stdin) != '\n'); //Read in result line
//fprintf(stderr, "%s Done turn\n", colour.c_str());
//fprintf(stderr,"%s - %d %d %s\n",colour.c_str(), x1, y1, direction.c_str() );
//fprintf(stderr, "%s [%d] computed move\n", argv[0], myPid);
}
}
#Makefile for Forfax
CPP = g++ -Wall -pedantic -lSDL -lGL -g
OBJ = main.o forfax.o
BIN = forfax
$(BIN) : $(OBJ)
$(CPP) -o $(BIN) $(OBJ)
%.o : %.cpp %.h
$(CPP) -c $<
clean :
$(RM) $(BIN) $(OBJ) $(LINKOBJ)
clean_full: #cleans up all backup files
$(RM) $(BIN) $(OBJ) $(LINKOBJ)
$(RM) *.*~
$(RM) *~
/**
* "forfax", a sample Stratego AI for the UCC Programming Competition 2012
* Implementations of classes Piece, Board and Forfax
* @author Sam Moore (matches) [SZM]
* @website http://matches.ucc.asn.au/stratego
* @email [email protected] or [email protected]
* @git git.ucc.asn.au/progcomp2012.git
*/
#include "forfax.h"
#include <cstdlib>
#include <sstream>
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <cmath>
//#define DEBUG
using namespace std;
/**
* The characters used to represent various pieces
* NOTHING, BOULDER, FLAG, SPY, SCOUT, MINER, SERGEANT, LIETENANT, CAPTAIN, MAJOR, COLONEL, GENERAL, MARSHAL, BOMB, ERROR
*/
char Piece::tokens[] = {'.','*','F','s','9','8','7','6','5','4','3','2','1','B','?'};
/**
* The number of units remaining for each colour
* Accessed by [COLOUR][TYPE]
* COLOUR: RED, BLUE
* TYPE: NOTHING, BOULDER, FLAG, SPY, SCOUT, MINER, SERGEANT, LIETENANT, CAPTAIN, MAJOR, COLONEL, GENERAL, MARSHAL, BOMB, ERROR
*/
int Forfax::remainingUnits[][15] = {{0,0,1,1,8,5,4,4,4,3,2,1,1,6,0},{0,0,1,1,8,5,4,4,4,3,2,1,1,6,0}};
/**
* Constructor for a piece of unknown rank
* @param newX - x coord
* @param newY - y coord
* @param newColour - colour
*/
Piece::Piece(int newX, int newY,const Colour & newColour)
: x(newX), y(newY), colour(newColour), minRank(Piece::FLAG), maxRank(Piece::BOMB), lastMove(0), lastx(newX), lasty(newY)
{
}
/**
* Constructor for a piece of known rank
* @param newX - x coord
* @param newY - y coord
* @param newColour - colour
* @param fixedRank - rank of the new piece
*/
Piece::Piece(int newX, int newY,const Colour & newColour, const Type & fixedRank)
: x(newX), y(newY), colour(newColour), minRank(fixedRank), maxRank(fixedRank), lastMove(0), lastx(newX), lasty(newY)
{
}
/**
* HELPER - Returns the Piece::Type matching a given character
* @param token - The character to match
* @returns A Piece::Type corresponding to the character, or Piece::ERROR if none was found
*/
Piece::Type Piece::GetType(char token)
{
for (int ii=0; ii < Piece::ERROR; ++ii)
{
if (Piece::tokens[ii] == token)
return (Type)(ii);
}
return Piece::ERROR;
}
/**
* Constructor for the board
* @param newWidth - width of the board
* @param newHeight - height of the board
*
*/
Board::Board(int newWidth, int newHeight) : width(newWidth), height(newHeight), board(NULL), red(), blue()
{
//Construct 2D array of Piece*'s
board = new Piece**[width];
for (int x=0; x < width; ++x)
{
board[x] = new Piece*[height];
for (int y=0; y < height; ++y)
board[x][y] = NULL;
}
}
/**
* Destroy the board
*/
Board::~Board()
{
//Destroy the 2D array of Piece*'s
for (int x=0; x < width; ++x)
{
for (int y=0; y < height; ++y)
delete board[x][y];
delete [] board[x];
}
}
/**
* Retrieve a piece from the board at specified coordinates
* @param x - x coord of the piece
* @param y - y coord of the piece
* @returns Piece* to the piece found at (x,y), or NULL if there was no piece, or the coords were invalid
*/
Piece * Board::Get(int x, int y) const
{
if (board == NULL || x < 0 || y < 0 || x >= width || y >= height)
return NULL;
return board[x][y];
}
/**
* Add a piece to the board
* Also updates the red or blue arrays if necessary
* @param x - x coord of the piece
* @param y - y coord of the piece
* @param newPiece - pointer to the piece to add
* @returns newPiece if the piece was successfully added, NULL if it was not (ie invalid coordinates specified)
*
*/
Piece * Board::Set(int x, int y, Piece * newPiece)
{
if (board == NULL || x < 0 || y < 0 || x >= width || y >= height)
return NULL;
board[x][y] = newPiece;
//if (newPiece->GetColour() == Piece::RED)
// red.push_back(newPiece);
//else if (newPiece->GetColour() == Piece::BLUE)
// blue.push_back(newPiece);
return newPiece;
}
/**
* HELPER - Convert a string to a direction
* @param str - The string to convert to a direction
* @returns The equivalent Direction
*/
Board::Direction Board::StrToDir(const string & str)
{
if (str == "UP")
return UP;
else if (str == "DOWN")
return DOWN;
else if (str == "LEFT")
return LEFT;
else if (str == "RIGHT")
return RIGHT;
return NONE;
}
/**
* HELPER - Convert a Direction to a string
* @param dir - the Direction to convert
* @param str - A buffer string, which will contain the string representation of the Direction once this function returns.
*/
void Board::DirToStr(const Direction & dir, string & str)
{
str.clear();
switch (dir)
{
case UP:
str = "UP";
break;
case DOWN:
str = "DOWN";
break;
case LEFT:
str = "LEFT";
break;
case RIGHT:
str = "RIGHT";
break;
default:
str = "NONE";
break;
}
}
/**
* HELPER - Translates the given coordinates in a specified direction
* @param x - x coord
* @param y - y coord
* @param dir - Direction to move in
* @param multiplier - Number of times to move
*
*/
void Board::MoveInDirection(int & x, int & y, const Direction & dir, int multiplier)
{
switch (dir)
{
case UP:
y -= multiplier;
break;
case DOWN:
y += multiplier;
break;
case LEFT:
x -= multiplier;
break;
case RIGHT:
x += multiplier;
break;
default:
break;
}
}
/**
* HELPER - Returns the best direction to move in to get from one point to another
* @param x1 - x coord of point 1
* @param y1 - y coord of point 1
* @param x2 - x coord of point 2
* @param y2 - y coord of point 2
* @returns The best direction to move in
*/
Board::Direction Board::DirectionBetween(int x1, int y1, int x2, int y2)
{
double xDist = (x2 - x1);
double yDist = (y2 - y1);
if (abs(xDist) >= abs(yDist))
{
if (xDist < 0)
return LEFT;
else
return RIGHT;
}
else
{
if (yDist < 0)
return UP;
else
return DOWN;
}
return NONE;
}
/**
* HELPER - Returns the number of moves between two points
* @param x1 x coordinate of the first point
* @param y1 y coordinate of the first point
* @param x2 x coordinate of the second point
* @param y2 y coordinate of the second point
* @returns The number of moves taken to progress from (x1, y1) to (x2, y2), assuming no obstacles
*/
int Board::NumberOfMoves(int x1, int y1, int x2, int y2)
{
return (abs(x2 - x1) + abs(y2 - y1)); //Pieces can't move diagonally, so this is pretty straight forward
}
/**
* Searches the board's red and blue arrays for the piece, and removes it
* DOES NOT delete the piece. Calling function should delete piece after calling this function.
* @param forget - The Piece to forget about
* @returns true if the piece was actually found
*/
bool Board::ForgetPiece(Piece * forget)
{
if (forget == NULL)
return false;
vector<Piece*> & in = GetPieces(forget->colour); bool result = false;
vector<Piece*>::iterator i=in.begin();
while (i != in.end())
{
if ((*i) == forget)
{
i = in.erase(i);
result = true;
continue;
}
++i;
}
return result;
}
/**
* Gets the closest Piece of a specified colour to a point
* @param x The x coordinate of the point
* @param y The y coordinate of the point
* @param colour The colour that the piece must match (may be Piece::BOTH to match either colour)
* @returns Piece* pointing to the closest piece of a matching colour, NULL if no piece found
*/
Piece * Board::GetClosest(int x, int y, const Piece::Colour & colour) const
{
if (x < 0 || y < 0 || x >= width || y >= height)
return NULL;
for (int dist = 0; dist < max<int>(width, height); ++dist)
{
for (int yy = y-dist; yy <= y+dist; ++yy)
{
Piece * get = Get(x+dist, y);
if ((get != NULL) && (get->colour == colour || colour == Piece::BOTH))
return get;
}
for (int yy = y-dist; yy <= y+dist; ++yy)
{