+/**
+ * "basic_cpp", a sample Stratego AI for the UCC Programming Competition 2012
+ * Implementations of main function, and Helper functions
+ *
+ * @author Sam Moore (matches) [SZM]
+ * @website http://matches.ucc.asn.au/stratego
+ * @git git.ucc.asn.au/progcomp2012.git
+ */
+
+#include "basic_cpp.h" //Needs class Base_Cpp and the includes in this file
+
+using namespace std;
+
+/**
+ * The characters used to represent various pieces
+ * NOTHING, BOULDER, FLAG, SPY, SCOUT, MINER, SERGEANT, LIETENANT, CAPTAIN, MAJOR, COLONEL, GENERAL, MARSHAL, BOMB, UNKNOWN
+ */
+char Piece::tokens[] = {'.','*','F','s','9','8','7','6','5','4','3','2','1','B','?'};
+
+/**
+ * Gets a rank from the character used to represent it
+ * Basic lookup of Piece::tokens
+ */
+Rank Piece::GetRank(char token)
+{
+ for (int ii=0; ii <= 14; ++ii)
+ {
+ if (tokens[ii] == token)
+ return (Rank)(ii);
+ }
+ return UNKNOWN;
+}
+
+/**
+ * IMPLEMENTATION of Helper FOLLOWS
+ */
+
+/**
+ * Convert string to direction
+ */
+Direction Helper::StrToDir(const string & dir)
+{
+ if (dir == "UP")
+ return UP;
+ else if (dir == "DOWN")
+ return DOWN;
+ else if (dir == "LEFT")
+ return LEFT;
+ else if (dir == "RIGHT")
+ return RIGHT;
+ else
+ return DIRECTION_ERROR;
+}
+
+/**
+ * Direction to String
+ */
+void Helper::DirToStr(const Direction & dir, std::string & buffer)
+{
+ switch (dir)
+ {
+ case UP:
+ buffer = "UP";
+ break;
+ case DOWN:
+ buffer = "DOWN";
+ break;
+ case LEFT:
+ buffer = "LEFT";
+ break;
+ case RIGHT:
+ buffer = "RIGHT";
+ break;
+ default:
+ buffer = "DIRECTION_ERROR";
+ break;
+ }
+}
+
+/**
+ * Move a point in a direction
+ */
+void Helper::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;
+ }
+
+}
+
+/**
+ * Tokenise a string
+ */
+int Helper::Tokenise(std::vector<string> & 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();
+}
+
+/**
+ * Convert string to integer
+ */
+int Helper::Integer(std::string & fromStr)
+{
+ stringstream s(fromStr);
+ int result = 0;
+ s >> result;
+ return result;
+}
+
+/**
+ * Read in a line from stdin
+ */
+void Helper::ReadLine(std::string & buffer)
+{
+ buffer = "";
+ for (char c = cin.get(); c != '\n' && cin.good(); c = cin.get())
+ {
+ buffer += c;
+ }
+}
+
+/**
+ * IMPLEMENTATION of Board FOLLOWS
+ */
+
+/**
+ * Constructer for Board
+ */
+Board::Board(int w, int h) : width(w), height(h), board(NULL)
+{
+ //Construct 2D array of P*'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;
+ }
+}
+
+/**
+ * Destructor for board
+ */
+Board::~Board()
+{
+ //Destroy the 2D array of P*'s
+ for (int x=0; x < width; ++x)
+ {
+ for (int y=0; y < height; ++y)
+ delete board[x][y];
+ delete [] board[x];
+ }
+}
+
+/**
+ * Retrieves a piece on the Board
+ * @param x x coordinate
+ * @param y y coordinate
+ * @returns A Piece* for the piece, or NULL if there is no piece at the point given
+ */
+Piece * Board::Get(int x, int y) const
+{
+ if (ValidPosition(x, y))
+ return board[x][y];
+ return NULL;
+}
+/**
+ * Sets a piece on the Board
+ * @param x x coordinate
+ * @param y y coordinate
+ * @param newPiece
+ * @param returns newPiece if successful, NULL if not
+ */
+Piece * Board::Set(int x, int y, Piece * newPiece)
+{
+ if (!ValidPosition(x, y))
+ return NULL;
+ board[x][y] = newPiece;
+ assert(Get(x,y) == newPiece);
+ return newPiece;
+}
+
+/**
+ * IMPLEMENTATION of Base_Cpp FOLLOWS
+ */
+
+/**
+ * Constructor for AI
+ */
+BasicAI::BasicAI() : turn(0), board(NULL), units(), enemyUnits(), colour(NONE), colourStr("")
+{
+ srand(time(NULL));
+ cin.rdbuf()->pubsetbuf(NULL, 0);
+ cout.rdbuf()->pubsetbuf(NULL, 0);
+}
+
+/**
+ * Destructor for AI
+ */
+BasicAI::~BasicAI()
+{
+ if (board != NULL)
+ delete board;
+}
+
+
+/**
+ * Setup the AI
+ * @returns true if successful, false on error
+ */
+bool BasicAI::Setup()
+{
+
+ cin >> colourStr;
+
+
+ std::string opponentName(""); //opponentName is unused, just read it
+ cin >> opponentName;
+
+ int width = 0; int height = 0;
+ cin >> width; cin >> height;
+
+ while(cin.get() != '\n' && cin.good()); //trim newline
+
+ board = new Board(width, height);
+
+ if (colourStr == "RED")
+ {
+ colour = RED;
+ cout << "FB8sB479B8\nBB31555583\n6724898974\n967B669999\n";
+ }
+ else if (colourStr == "BLUE")
+ {
+ colour = BLUE;
+ cout << "967B669999\n6724898974\nBB31555583\nFB8sB479B8\n";
+ }
+ else
+ return false;
+
+ return (board != NULL);
+}
+
+/**
+ * Performs a move, including the saving states bits
+ * @returns true if the game is to continue, false if it is to end
+ */
+bool BasicAI::MoveCycle()
+{
+ //cerr << "BasicAI at MoveCycle()\n";
+ if (!InterpretResult())
+ return false;
+ if (!ReadBoard())
+ return false;
+ if (!MakeMove())
+ return false;
+
+ turn++;
+ return InterpretResult();
+}
+
+/**
+ * Interprets the result of a move. Ignores the first move
+ * @returns true if successful, false if there was an error
+ */
+bool BasicAI::InterpretResult()
+{
+ //cerr << "BasicAI at InterpretResult()\n";
+ if (turn == 0)
+ {
+ while (cin.get() != '\n' && cin.good());
+ return true;
+ }
+
+
+ string resultLine; Helper::ReadLine(resultLine);
+ vector<string> tokens; Helper::Tokenise(tokens, resultLine, ' ');
+
+ if (tokens.size() <= 0)
+ {
+ //cerr << "No tokens!\n";
+ return false;
+ }
+
+ if (tokens[0] == "QUIT")
+ {
+ return false;
+ }
+
+ if (tokens[0] == "NO_MOVE")
+ {
+ return true;
+
+ }
+
+ if (tokens.size() < 4)
+ {
+ //cerr << "Only got " << tokens.size() << " tokens\n";
+ return false;
+ }
+
+
+ int x = Helper::Integer(tokens[0]);
+ int y = Helper::Integer(tokens[1]);
+
+
+
+ Direction dir = Helper::StrToDir(tokens[2]);
+ string & outcome = tokens[3];
+
+ int x2 = x; int y2 = y; Helper::MoveInDirection(x2,y2,dir);
+
+ Piece * attacker = board->Get(x,y);
+ if (attacker == NULL)
+ {
+ //cerr << "No attacker!\n";
+ return false;
+ }
+ Piece * defender = board->Get(x2,y2);
+ if (outcome == "OK")
+ {
+ board->Set(x2,y2, attacker);
+ board->Set(x,y,NULL);
+ attacker->x = x2; attacker->y = y2;
+ }
+ else if (outcome == "KILLS")
+ {
+ if (defender == NULL)
+ {
+ //cerr << "No defender!\n";
+ return false;
+ }
+
+ board->Set(x2,y2, attacker);
+ board->Set(x,y,NULL);
+ attacker->x = x2; attacker->y = y2;
+
+ attacker->rank = Piece::GetRank(tokens[4][0]);
+ ForgetUnit(defender);
+ }
+ else if (outcome == "DIES")
+ {
+ if (defender == NULL)
+ {
+ //cerr << "No defender!\n";
+ return false;
+ }
+
+
+ board->Set(x,y,NULL);
+ defender->rank = Piece::GetRank(tokens[5][0]);
+ ForgetUnit(attacker);
+
+
+ }
+ else if (outcome == "BOTHDIE")
+ {
+ board->Set(x,y,NULL);
+ board->Set(x2,y2, NULL);
+
+ ForgetUnit(attacker);
+ ForgetUnit(defender);
+ }
+ else if (outcome == "FLAG")
+ {
+ //cerr << "BasicAI - Flag was captured, exit!\n";
+ return false;
+ }
+ else if (outcome == "ILLEGAL")
+ {
+ //cerr << "BasicAI - Illegal move, exit!\n";
+ return false;
+ }
+
+ //cerr << "BasicAI finished InterpretResult()\n";
+ return true;
+}
+
+/**
+ * Performs a random move
+ * TODO: Overwrite with custom move
+ * @returns true if a move could be made (including NO_MOVE), false on error
+ */
+bool BasicAI::MakeMove()
+{
+ //cerr << "BasicAI at MakeMove()\n";
+ if (units.size() <= 0)
+ {
+ //cerr << " No units!\n";
+ return false;
+
+ }
+
+ int index = rand() % units.size();
+ int startIndex = index;
+ while (true)
+ {
+
+
+ Piece * piece = units[index];
+ if (piece != NULL && piece->Mobile())
+ {
+ int dirIndex = rand() % 4;
+ int startDirIndex = dirIndex;
+ while (true)
+ {
+ int x = piece->x; int y = piece->y;
+ assert(board->Get(x,y) == piece);
+ Helper::MoveInDirection(x,y,(Direction)(dirIndex));
+ if (board->ValidPosition(x,y))
+ {
+ Piece * target = board->Get(x,y);
+ if (target == NULL || (target->colour != piece->colour && target->colour != NONE))
+ {
+ string dirStr;
+ Helper::DirToStr((Direction)(dirIndex), dirStr);
+ cout << piece->x << " " << piece->y << " " << dirStr << "\n";
+ return true;
+ }
+ }
+
+ dirIndex = (dirIndex + 1) % 4;
+ if (dirIndex == startDirIndex)
+ break;
+ }
+ }
+
+ index = (index+1) % (units.size());
+ if (index == startIndex)
+ {
+ cout << "NO_MOVE\n";
+ return true;
+ }
+ }
+ return true;
+}
+
+/**
+ * Reads in the board
+ * On first turn, sets up Board
+ * On subsquent turns, takes no action
+ * @returns true on success, false on error
+ */
+bool BasicAI::ReadBoard()
+{
+ //cerr << "BasicAI at ReadBoard()\n";
+ for (int y = 0; y < board->Height(); ++y)
+ {
+ string row;
+ Helper::ReadLine(row);
+ for (unsigned int x = 0; x < row.size(); ++x)
+ {
+ if (turn == 0)
+ {
+ switch (row[x])
+ {
+ case '.':
+ break;
+ case '#':
+ board->Set(x,y, new Piece(x,y,Piece::Opposite(colour), UNKNOWN));
+ enemyUnits.push_back(board->Get(x,y));
+ break;
+ case '+':
+ board->Set(x,y, new Piece(x,y,NONE, BOULDER));
+ break;
+ default:
+ board->Set(x,y,new Piece(x,y,colour, Piece::GetRank(row[x])));
+ units.push_back(board->Get(x,y));
+ break;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+/**
+ * Removes a piece from memory
+ * @param piece The piece to delete
+ * @returns true if the piece was actually found
+ */
+bool BasicAI::ForgetUnit(Piece * piece)
+{
+ //cerr << "BasicAI at ForgetUnit()\n";
+ bool result = false;
+ vector<Piece*>::iterator i = units.begin();
+ while (i != units.end())
+ {
+ if ((*i) == piece)
+ {
+ i = units.erase(i); result = true;
+ continue;
+ }
+ ++i;
+ }
+
+ i = enemyUnits.begin();
+ while (i != enemyUnits.end())
+ {
+ if ((*i) == piece)
+ {
+ i = enemyUnits.erase(i); result = true;
+ continue;
+ }
+ ++i;
+ }
+
+
+ delete piece;
+ return result;
+}
+
+
+/**
+ * The main function
+ * @param argc
+ * @param argv
+ * @returns zero on success, non-zero on failure
+ */
+int main(int argc, char ** argv)
+{
+
+ srand(time(NULL));
+
+ BasicAI * basicAI = new BasicAI();
+ if (basicAI->Setup())
+ {
+ while (basicAI->MoveCycle());
+ }
+ delete basicAI;
+ exit(EXIT_SUCCESS);
+ return 0;
+}