Commit 041c37d1 authored by Sam Moore's avatar Sam Moore
Browse files

Mostly messing with "forfax" AI

It would be nice to have an AI that doesn't segfault.

Currently segfault caused by Board::ForgetPiece.
valgrind outputs a lot of wierd crap about std::vector and uninitialised values
Uninitialised values created by std::vector::push_back()
All I am pushing is a simple pointer (Piece*), so I don't know WHY uninitialised values happen...
The std::list used in MakeMove is somehow using the same memory as the std::vectors of the board, which is causing invalid reads
Stupid, stupid stdlib.

I think that once that is fixed, forfax is pretty much done. I'd like to see how well it plays, but... segfaults.

I also fixed dummy to take into account the modified turn protocol which prints piece ranks. dummy just reads them and ignores them.

I plan to make the manager program more useful
	- Enable human players
	- Add command line arguments for things like timeouts, graphics on/off etc
	- Read a game from a file (so that games can be viewed after they are run)
I need to go through the manager program carefully and make sure that the way AI programs quit actually works
Ideally the AI program has a short period to exit gracefully before it is killed
I think for some reason the AI program always just gets killed.

At some point I need to setup a VM for this. I should probably do that.

I also might change minor things like the tokens (from random characters to digits + a few characters) and the internal ordering of the enum Piece::Type
parent 2ab27eb6
...@@ -146,7 +146,7 @@ MovementResult Controller::MakeMove(string & buffer) ...@@ -146,7 +146,7 @@ MovementResult Controller::MakeMove(string & buffer)
//I stored the ranks in the wrong order; rank 1 is the marshal, 2 is the general etc... //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 //So I am reversing them in the output... great work
s << (Piece::BOMB - moveResult.attackerRank) << " " << (Piece::BOMB - moveResult.defenderRank) << "\n"; s << (Piece::BOMB - moveResult.attackerRank) << " " << (Piece::BOMB - moveResult.defenderRank);
switch (moveResult.type) switch (moveResult.type)
{ {
case MovementResult::OK: case MovementResult::OK:
...@@ -174,9 +174,9 @@ MovementResult Controller::MakeMove(string & buffer) ...@@ -174,9 +174,9 @@ MovementResult Controller::MakeMove(string & buffer)
} }
if (!Board::LegalResult(moveResult)) //if (!Board::LegalResult(moveResult))
return MovementResult::OK; //HACK - Legal results returned! // return MovementResult::OK; //HACK - Legal results returned!
else //else
return moveResult; return moveResult;
} }
......
../samples/dummy
\ No newline at end of file
../samples/forfax/forfax
\ No newline at end of file
...@@ -28,17 +28,19 @@ int main(int argc, char ** argv) ...@@ -28,17 +28,19 @@ int main(int argc, char ** argv)
{ {
assert(argc == 3); assert(argc == 3);
for (int y = 5; y < 9; ++y)
for (int y = 4; y < 6; ++y)
{ {
for (int x = 3; x < 5; ++x) for (int x = 2; x < 4; ++x)
{ {
theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE); theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE);
} }
for (int x = 9; x < 11; ++x) for (int x = 6; x < 8; ++x)
{ {
theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE); theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE);
} }
} }
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]);
...@@ -90,7 +92,7 @@ int main(int argc, char ** argv) ...@@ -90,7 +92,7 @@ int main(int argc, char ** argv)
break; break;
#ifdef GRAPHICS #ifdef GRAPHICS
Board::theBoard.Draw(); Board::theBoard.Draw();
if (CheckForQuitWhilstWaiting(0.2)) if (CheckForQuitWhilstWaiting(0.5))
{ {
red->SendMessage("QUIT"); red->SendMessage("QUIT");
blue->SendMessage("QUIT"); blue->SendMessage("QUIT");
...@@ -112,7 +114,7 @@ int main(int argc, char ** argv) ...@@ -112,7 +114,7 @@ int main(int argc, char ** argv)
#ifdef GRAPHICS #ifdef GRAPHICS
Board::theBoard.Draw(); Board::theBoard.Draw();
if (CheckForQuitWhilstWaiting(0.2)) if (CheckForQuitWhilstWaiting(0.5))
{ {
red->SendMessage("QUIT"); red->SendMessage("QUIT");
blue->SendMessage("QUIT"); blue->SendMessage("QUIT");
...@@ -130,12 +132,14 @@ int main(int argc, char ** argv) ...@@ -130,12 +132,14 @@ int main(int argc, char ** argv)
printf("Final board state\n"); printf("Final board state\n");
#ifdef GRAPHICS #ifdef GRAPHICS
Board::theBoard.Draw(); Board::theBoard.Draw();
if (CheckForQuitWhilstWaiting(4)) if (CheckForQuitWhilstWaiting(4))
{ {
red->SendMessage("QUIT"); red->SendMessage("QUIT");
blue->SendMessage("QUIT"); blue->SendMessage("QUIT");
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
#else #else
Board::theBoard.Print(stderr); Board::theBoard.Print(stderr);
#endif //GRAPHICS #endif //GRAPHICS
...@@ -245,6 +249,16 @@ void BrokenPipe(int sig) ...@@ -245,6 +249,16 @@ void BrokenPipe(int sig)
fprintf(stderr,"Game ends on ERROR's turn - REASON: Broken pipe\n"); fprintf(stderr,"Game ends on ERROR's turn - REASON: Broken pipe\n");
} }
Board::theBoard.Draw();
while (true)
{
if (CheckForQuitWhilstWaiting(4000))
{
red->SendMessage("QUIT");
blue->SendMessage("QUIT");
exit(EXIT_SUCCESS);
}
}
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
......
...@@ -7,7 +7,7 @@ using namespace std; ...@@ -7,7 +7,7 @@ using namespace std;
/** /**
* Static variables * Static variables
*/ */
Board Board::theBoard(14,14); Board Board::theBoard(10,10);
//nothing, boulder, flag, spy, scout, miner, sergeant, lietenant, captain, major, colonel, general, marshal, bomb, error //nothing, boulder, flag, spy, scout, miner, sergeant, lietenant, captain, major, colonel, general, marshal, bomb, error
char Piece::tokens[] = {'.','+','F','y','s','n','S','L','c','m','C','G','M','B','?'}; char Piece::tokens[] = {'.','+','F','y','s','n','S','L','c','m','C','G','M','B','?'};
int Piece::maxUnits[] = {0,0,1,1,8,5,4,4,4,3,2,1,1,6,0}; int Piece::maxUnits[] = {0,0,1,1,8,5,4,4,4,3,2,1,1,6,0};
......
...@@ -5,6 +5,8 @@ CPP = g++ -Wall -pedantic -lSDL -lGL -g ...@@ -5,6 +5,8 @@ CPP = g++ -Wall -pedantic -lSDL -lGL -g
dummy : dummy.o dummy : dummy.o
$(CPP) -o dummy dummy.o $(CPP) -o dummy dummy.o
clean : clean :
$(RM) $(BIN) $(OBJ) $(LINKOBJ) $(RM) $(BIN) $(OBJ) $(LINKOBJ)
......
...@@ -30,20 +30,20 @@ int main(int argc, char ** argv) ...@@ -30,20 +30,20 @@ int main(int argc, char ** argv)
//fprintf(stderr, "Colour is \"%s\", width and height are (%d, %d), opponent is \"%s\"\n", colour.c_str(), width, height, opponent.c_str()); //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 == 14 && height == 14); //Can't deal with other sized boards assert(width == 10 && height == 10); //Can't deal with other sized boards
if (colour == "RED") if (colour == "RED")
{ {
fprintf(stdout, "FB..........B.\n"); fprintf(stdout, "FBnyBmSsBn\n");
fprintf(stdout, "BBCM....cccc.C\n"); fprintf(stdout, "BBCMccccnC\n");
fprintf(stdout, "LSGmnsBmSsnsSm\n"); fprintf(stdout, "LSGmnsnsSm\n");
fprintf(stdout, "sLSBLnLssssnyn\n"); fprintf(stdout, "sLSBLLssss\n");
} }
else if (colour == "BLUE") else if (colour == "BLUE")
{ {
fprintf(stdout, "sLSBLnLssssnyn\n"); fprintf(stdout, "sLSBLLssss\n");
fprintf(stdout, "LSGmnsBmSsnsSm\n"); fprintf(stdout, "LSGmnsnsSm\n");
fprintf(stdout, "BBCM....cccc.C\n"); fprintf(stdout, "BBCMccccnC\n");
fprintf(stdout, "FB..........B.\n"); fprintf(stdout, "FBnyBmSsBn\n");
} }
else else
{ {
...@@ -63,10 +63,12 @@ int main(int argc, char ** argv) ...@@ -63,10 +63,12 @@ int main(int argc, char ** argv)
//fprintf(stderr, "%s [%d] looping\n", argv[0], myPid); //fprintf(stderr, "%s [%d] looping\n", argv[0], myPid);
choices.clear(); choices.clear();
// fprintf(stderr, "%s Waiting for status line...\n", colour.c_str()); //fprintf(stderr, "%s Waiting for status line...\n", colour.c_str());
while (fgetc(stdin) != '\n') char c = fgetc(stdin);
while (c != '\n')
{ {
//fprintf(stderr,"."); //fprintf(stderr,"%c",c);
c = fgetc(stdin);
} }
//fprintf(stderr, "%s Got status, waiting for board line...\n", colour.c_str()); //fprintf(stderr, "%s Got status, waiting for board line...\n", colour.c_str());
......
This diff is collapsed.
/**
* "forfax", a sample Stratego AI for the UCC Programming Competition 2012
* Declarations for classes Piece, Board and Forfax, Declaration/Implementation of helper class MovementChoice
* @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
*/
#ifndef FORFAX_H #ifndef FORFAX_H
#define FORFAX_H #define FORFAX_H
#include <vector> //Uses C++ std::vectors to store pieces #include <vector> //Uses C++ std::vectors to store pieces
#include <string> //Uses C++ std::string #include <string> //Uses C++ std::string
/** #include <iostream> //For debug
* Header for the sample Stratego AI "forfax" #include <cassert> //For debug
* @author Sam Moore 2011
*/
class Board;
class Board; //Forward declaration used by class Piece
/** /**
* Class to represent a piece on the board * Class to represent a piece on the board
*/ */
...@@ -18,7 +29,7 @@ class Piece ...@@ -18,7 +29,7 @@ class Piece
typedef enum {RED=0, BLUE=1, NONE, BOTH} Colour; //Used for the allegiance of the pieces - terrain counts as NONE. typedef enum {RED=0, BLUE=1, NONE, BOTH} Colour; //Used for the allegiance of the pieces - terrain counts as NONE.
Piece(int newX, int newY,const Colour & newColour); Piece(int newX, int newY,const Colour & newColour);
Piece(int newX, int newY,const Colour & newColour, const Colour & rankKnownBy, const Type & fixedRank); Piece(int newX, int newY,const Colour & newColour, const Type & fixedRank);
virtual ~Piece() {} virtual ~Piece() {}
void SetCoords(int newX, int newY) {x = newX; y = newY;} void SetCoords(int newX, int newY) {x = newX; y = newY;}
...@@ -30,12 +41,18 @@ class Piece ...@@ -30,12 +41,18 @@ class Piece
static Type GetType(char fromToken); //Retrieves the type of a piece given its character token static Type GetType(char fromToken); //Retrieves the type of a piece given its character token
static Colour Opposite(const Colour & colour) {return colour == RED ? BLUE : RED;} static Colour Opposite(const Colour & colour) {return colour == RED ? BLUE : RED;}
bool Mobile() const
{
if (minRank == maxRank)
return (minRank != Piece::FLAG && minRank != Piece::BOMB);
else
return true;
}
int x; int y; int x; int y;
const Colour colour; //The colour of the piece const Colour colour; //The colour of the piece
Type minRank[2]; //The minimum possible rank of the piece, according to each colour Type minRank; //The minimum possible rank of the piece
Type maxRank[2]; //The maximum possible rank of the piece, according to each colour Type maxRank; //The maximum possible rank of the piece
int lastMove; int lastMove;
...@@ -57,6 +74,8 @@ class Board ...@@ -57,6 +74,8 @@ class Board
Piece * Get(int x, int y) const; //Retrieve single piece Piece * Get(int x, int y) const; //Retrieve single piece
Piece * Set(int x, int y, Piece * newPiece); //Add piece to board Piece * Set(int x, int y, Piece * newPiece); //Add piece to board
bool ValidPosition(int x, int y) const {return (x > 0 && x < width && y > 0 && y < height);}
int Width() const {return width;} int Width() const {return width;}
int Height() const {return height;} int Height() const {return height;}
...@@ -89,39 +108,52 @@ class Board ...@@ -89,39 +108,52 @@ class Board
}; };
/** /**
* Small class to manage the Forfax AI * Class to manage the Forfax AI
*/ */
class Forfax class Forfax
{ {
public: public:
Forfax(); Forfax();
virtual ~Forfax(); virtual ~Forfax();
bool Setup(); //Waits for input to determine colour and board size, and then responds with setup
bool MakeMove(); //Should be called each turn - determines Forfax's move
double CombatSuccessChance(Piece * attacker, Piece * defender, const Piece::Colour & accordingTo) const; typedef enum {OK, NO_NEWLINE, EXPECTED_ATTACKER, UNEXPECTED_DEFENDER, NO_ATTACKER, NO_DEFENDER, COLOUR_MISMATCH, INVALID_QUERY, BOARD_ERROR, VICTORY} Status;
double MovementBaseScore(Piece * move, const Board::Direction & dir, const Piece::Colour & accordingTo) const;
double MovementTotalScore(Piece * move, const Board::Direction & dir, const Piece::Colour & accordingTo) const; Status Setup(); //Waits for input to determine colour and board size, and then responds with setup
Status MakeMove(); //Should be called each turn - determines Forfax's move
//Move score functions
double MovementScore(Piece * move, const Board::Direction & dir) const; //Calculate total score
double CombatSuccessChance(Piece * attacker, Piece * defender) const; //Calculate chance of success in combat
double IntrinsicWorth(int x, int y) const; //How much a given point on the board is worth
double VictoryScore(Piece * attacker, Piece * defender) const; //How much killing the defender is worth
double DefeatScore(Piece * attacker, Piece * defender) const; //How much losing is worth
void PrintBoard(std::ostream & out);
protected: protected:
bool MakeFirstMove(); //Should only be called on the first turn Status MakeFirstMove(); //Should only be called on the first turn
bool InterpretMove(); Status InterpretMove();
private: private:
Board * board; //Forfax stores the state on a board Board * board; //Forfax stores the state on a board
Piece::Colour colour; //Forfax needs to know his colour Piece::Colour colour; //Forfax needs to know his colour
std::string strColour; //String of colour std::string strColour; //String of colour
int turnNumber; //Forfax needs to know what turn number it is int turnNumber; //Forfax needs to know what turn number it is
int remainingUnits[14][2][2]; //Known remaining units, accessed by (type, colour, accordingTo) static int remainingUnits[2][15]; //Known remaining units, accessed by [colour][type]
}; };
/**
* Helper class used to store various moves in the board, and their associated scores
*/
class MovementChoice class MovementChoice
{ {
public: public:
MovementChoice(Piece * newPiece, const Board::Direction & newDir, const Forfax & forfax, const Piece::Colour & accordingTo) : piece(newPiece), dir(newDir) MovementChoice(Piece * newPiece, const Board::Direction & newDir, const Forfax & forfax) : piece(newPiece), dir(newDir)
{ {
score = forfax.MovementBaseScore(piece, dir, accordingTo); score = forfax.MovementScore(piece, dir);
} }
MovementChoice(const MovementChoice & cpy) : piece(cpy.piece), dir(cpy.dir), score(cpy.score) MovementChoice(const MovementChoice & cpy) : piece(cpy.piece), dir(cpy.dir), score(cpy.score)
...@@ -143,14 +175,6 @@ class MovementChoice ...@@ -143,14 +175,6 @@ class MovementChoice
}; };
class MovementTotalChoice : public MovementChoice
{
public:
MovementTotalChoice(Piece * newPiece, const Board::Direction & newDir, const Forfax & forfax, const Piece::Colour & accordingTo) : MovementChoice(newPiece, newDir, forfax, accordingTo)
{
score = score/(forfax.MovementTotalScore(piece, dir, Piece::Opposite(accordingTo)));
}
};
#endif //FORFAX_H #endif //FORFAX_H
......
/**
* "forfax", a sample Stratego AI for the UCC Programming Competition 2012
* The main function for the "forfax" AI program
* @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 <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
...@@ -6,21 +15,73 @@ ...@@ -6,21 +15,73 @@
using namespace std; using namespace std;
#include <stdio.h> #include <stdio.h>
void quit();
/**
* The AI
*/
Forfax forfax; Forfax forfax;
/**
* The main function
* @param argc the number of arguments
* @param argv the arguments
* @returns exit code 0 for success, something else for error
* Do I really need to tell you this?
*/
int main(int argc, char ** argv) int main(int argc, char ** argv)
{ {
setbuf(stdin, NULL); setbuf(stdin, NULL);
setbuf(stdout, NULL); setbuf(stdout, NULL);
atexit(&quit);
if (!forfax.Setup()) Forfax::Status move = forfax.Setup();
exit(EXIT_SUCCESS);
while (move == Forfax::OK)
{
move = forfax.MakeMove();
}
switch (move)
{
case Forfax::OK:
cerr << argv[0] << " Error - Should never see this!\n";
break;
case Forfax::NO_NEWLINE:
cerr << argv[0] << " Error - Expected a new line!\n";
break;
case Forfax::EXPECTED_ATTACKER:
cerr << argv[0] << " Error - Attacking piece does not exist on board!\n";
break;
case Forfax::UNEXPECTED_DEFENDER:
cerr << argv[0] << " Error - Unexpected defending piece on board!\n";
break;
case Forfax::NO_ATTACKER:
cerr << argv[0] << " Error - Couldn't find attacker in list of pieces!\n";
break;
case Forfax::NO_DEFENDER:
cerr << argv[0] << " Error - Couldn't find defender in list of pieces!\n";
break;
while (forfax.MakeMove()); case Forfax::COLOUR_MISMATCH:
cerr << argv[0] << " Error - Colour of attacker and defender are the same!\n";
break;
case Forfax::INVALID_QUERY:
cerr << argv[0] << " Error - Query did not make sense\n";
break;
case Forfax::VICTORY:
cerr << argv[0] << " Game end - VICTORY!\n";
break;
case Forfax::BOARD_ERROR:
cerr << argv[0] << " Error - An error occurred with the board!\n";
break;
}
cerr << "Forfax threw a hissy fit, and exited!\n"; cerr << "Final board state:\n";
forfax.PrintBoard(cerr);
cerr << "Forfax is now exiting!\n";
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
return 0; return 0;
...@@ -28,7 +89,4 @@ int main(int argc, char ** argv) ...@@ -28,7 +89,4 @@ int main(int argc, char ** argv)
} }
void quit()
{
cerr << " Forfax quit\n";
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment