+#define ASSERTIONS 1
+#define DEBUG_RULES 0
+#define DEBUG 0
+#define SAVE_OUTPUT 1
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <cstdlib>
+#include <iostream>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <vector>
+#include <cassert>
+#include <sstream>
+
+using namespace std; // two of two, yay!
+
+typedef bool Boolean;
+typedef unsigned long UInt32;
+
+#include "peternlewis.h"
+
+const char *gPieceCharacters = kPieceCharacters;
+#if SAVE_OUTPUT
+ofstream gOutputFile;
+#endif
+
+template <class T>
+inline std::string to_string (const T& t)
+{
+ std::stringstream ss;
+ ss << t;
+ return ss.str();
+}
+
+
+/*
+ Author: Peter N Lewis
+
+ Originally: Submitted in 1997 to MacTech Programming Challenge and won.
+ <http://www.mactech.com/articles/mactech/Vol.13/13.11/Nov97Challenge/index.html>
+
+ Assumptions:
+ Only time we spend thinking is counted against out 10 seconds (not time in GetMove/ReportMove)
+
+ Method:
+ Basically we keep track of the board and what we know and what they know.
+ Each opponent piece has a bit map associated with it describing what pieces it could be.
+ As we see more pieces, the bit map is culled. If the piece moves, the bomb & flag bits are removed.
+ If we've seen all Scouts (for example), then the Scout bit is removed from all remaining pieces.
+ If all but one bit is remvoed, then we know what the piece is.
+
+ At each turn, we simply apply a sequence of actions (listed below) and take the first action that works.
+
+ It does very little in the way of lookahead (it plans out a path, but doesn't remember it and
+ doesn't take it to account any movement by the opposition)
+
+ It keeps a CRC of recent board positions (since the last strike) and doesn't replay any boards
+ (we want to win, not draw!).
+
+ If we exceed 10 seconds thinking time, we resign. Not that this is particularly likely,
+ in the games I tried, it spend less than half a second total.
+
+ Optimizations:
+ None.
+
+ Comment:
+ It actually plays a half decent game! The end game is not as good as I'd like, but time is up!
+*/
+
+/*
+USE SPY
+ If our spy is next to their 1, kill it
+
+DEFEND AGAINST SPY
+ if we have seen the spy, ignore this case
+
+ If an unknown piece is next to the 1, then
+ run, attack, have another piece attack, or ignore depending on a table
+
+ATTACK WEAKER
+ If a known piece is next to a weaker known piece, attack it
+ except if it places that piece in a dangerous location
+
+EXPLORE ATTACK
+ If a 6,7,9 is next to an unknown piece, attack it
+
+RETREAT
+ If a known piece is next to a stronger known piece, run away
+ (preferably towards something that can kill it
+ or if it's lowly, towards an unknown piece)
+
+SCOUT
+ Try advancing scouts rapidly
+
+ATTACK DISTANT
+ If a known piece is distant, but a clear path leads a slightly better piece towards it, advance the better piece
+ (includes miners)
+
+EXPLORE DISTANT
+ Try exploring (advance lowly pieces towards unknown pieces)
+
+ATTACK KNOWN WITH SAME DISTANT
+ If a known piece can be attacked by a known identical piece, attack it
+
+FIND FLAG
+ When few unmoved pieces remain, start assuming they are bombs/flags
+
+MOVE FORWARD
+ Move any piece we can forward
+
+MOVE
+ Move any piece we can
+
+RESIGN
+ Give up
+*/
+
+static void Output( string what )
+{
+#if SAVE_OUTPUT
+ gOutputFile << "<< " << what << "\n";
+#endif
+ cout << what << "\n";
+}
+
+static void SaveInput( string what )
+{
+#if SAVE_OUTPUT
+ gOutputFile << ">> " << what << "\n";
+#endif
+}
+
+static void Log( string what )
+{
+#if SAVE_OUTPUT
+ gOutputFile << what << "\n";
+#endif
+ cerr << what << "\n";
+}
+
+static void Debug( string what )
+{
+#if SAVE_OUTPUT
+ gOutputFile << what << "\n";
+#endif
+#if DEBUG
+ cerr << what << "\n";
+#endif
+}
+
+static void Assert( short must, string what )
+{
+ if ( !must ) {
+#if ASSERTIONS
+ Log( string("Assert failed! ") + what + "\n" );
+#endif
+ }
+}
+
+std::vector<string> gLineBuffer;
+
+enum {
+ kNoNothing = 0x00001FFE,
+ kStationaryBits = ((1 << kBomb) | (1 << kFlag))
+};
+
+enum {
+ kRepeatedBoards = 1000
+};
+
+typedef struct Square {
+ PlayerColor color;
+ PieceRank rank;
+ UInt32 possibilities;
+} Square;
+
+typedef Square OurBoard[kBoardSize][kBoardSize];
+
+typedef int Counts[kFlag+1];
+
+typedef UInt32 BoardPossibilities[kBoardSize][kBoardSize];
+
+typedef struct Storage {
+ OurBoard board;
+ Counts our_pieces;
+ Counts their_pieces;
+ Boolean do_getmove;
+ Boolean victory;
+ Square blankSquare;
+ PlayerColor playerColor;
+ PlayerColor theirColor;
+ BoardPossibilities dangers;
+ BoardPossibilities known_dangers;
+ UInt32 repeated_board[kRepeatedBoards];
+ UInt32 repeated_board_count;
+} Storage, *StoragePtr;
+
+static void DumpBoard( StoragePtr storage )
+{
+#if DEBUG
+ Debug( "DumpBoard:" );
+ for ( int row = 0; row < kBoardSize; row++ ) {
+ string line;
+ for ( int col = 0; col < kBoardSize; col++ ) {
+ PieceRank rank = storage->board[row][col].rank;
+ if ( kMarshall <= rank && rank <= kFlag ) {
+ char left, right;
+ if ( storage->board[row][col].color == kRed ) {
+ left = '{';
+ right = '}';
+ } else {
+ left = '[';
+ right = ']';
+ }
+ line += left;
+ line += gPieceCharacters[rank];
+ line += right;
+ line += ' ';
+ } else {
+ line += ' ';
+ if ( rank == kEmpty ) {
+ line += '.';
+ } else if ( rank == kWater ) {
+ line += '*';
+ } else if ( rank == kMoved ) {
+ line += 'M';
+ } else if ( rank == kAddForRankish ) {
+ line += '%';
+ } else if ( rank == kUnknown ) {
+ line += '?';
+ } else {
+ line += '@';
+ }
+ line += ' ';
+ line += ' ';
+ }
+ }
+ Debug( line );
+ }
+ Debug( "" );
+#endif
+}
+
+static PieceRank HelperGetRank(char token)
+{
+ for ( unsigned int ii=0; ii <= strlen(gPieceCharacters); ++ii ) {
+ if (gPieceCharacters[ii] == token) {
+ return (PieceRank)(ii);
+ }
+ }
+ return kUnknown;
+}
+
+/**
+ * Tokenise a string
+ */
+static int HelperTokenise(std::vector<string> & buffer, std::string & str, char split = ' ')
+{
+ buffer.clear();
+ 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
+ */
+static int HelperInteger(std::string & fromStr)
+{
+ stringstream s(fromStr);
+ int result = 0;
+ s >> result;
+ return result;
+}
+
+/**
+ * Read in a line from stdin
+ */
+static void HelperReadLine()
+{
+ std::string buffer = "";
+ for (char c = cin.get(); c != '\n' && cin.good(); c = cin.get()) {
+ buffer += c;
+ }
+ SaveInput( buffer );
+ HelperTokenise( gLineBuffer, buffer );
+ if ( gLineBuffer.size() == 0 ) {
+ Log( "No tokens on line" );
+ exit( 99 );
+ }
+ if ( gLineBuffer.size() == 0 ) {
+ Log( "No tokens on line" );
+ exit( 99 );
+ }
+ if ( gLineBuffer[0] == "QUIT" || gLineBuffer[0] == "NO_MOVE") {
+ Log( "QUIT token found" );
+ exit( 99 );
+ }
+}
+
+static void HelperJunkLine( int lines = 1 )
+{
+ while ( lines > 0 ) {
+ HelperReadLine();
+ lines--;
+ }
+}
+
+enum MoveOutcome {
+ kMR_OK,
+ kMR_Kills,
+ kMR_Dies,
+ kMR_BothDie
+};
+
+static void HelperReadMove( StoragePtr storage, PiecePosition& from, PiecePosition& to, MoveOutcome& result, PieceRank& attacker, PieceRank& defender )
+{
+ HelperReadLine();
+ if ( gLineBuffer.size() < 4 ) {
+ Log( "Less than 4 tokens" );
+ exit( 99 );
+ }
+ from.col = HelperInteger( gLineBuffer[0] );
+ from.row = HelperInteger( gLineBuffer[1] );
+ int move = HelperInteger( gLineBuffer[3] );
+ if ( move == 0 ) {
+ gLineBuffer.insert( gLineBuffer.begin()+3, "1" );
+ move = 1;
+ }
+ to = from;
+ std::string dir = gLineBuffer[2];
+ if (dir == "UP") {
+ to.row -= move;
+ } else if (dir == "DOWN") {
+ to.row += move;
+ } else if (dir == "LEFT") {
+ to.col -= move;
+ } else if (dir == "RIGHT") {
+ to.col += move;
+ } else {
+ Log( "DIRECTION ERROR" );
+ exit( 99 );
+ }
+ attacker = kUnknown;
+ defender = kUnknown;
+ std::string res = gLineBuffer[4];
+ if ( res == "ILLEGAL" ) {
+ Log( "Opponent (hopefully) made ILLEGAL move" );
+ exit( 99 );
+ } else if ( res == "OK" ) {
+ result = kMR_OK;
+ } else {
+ if ( res == "KILLS" ) {
+ result = kMR_Kills;
+ } else if ( res == "DIES" ) {
+ result = kMR_Dies;
+ } else if ( res == "BOTHDIE" ) {
+ result = kMR_BothDie;
+ } else {
+ Log( string("Unknown move result ") + res );
+ exit( 99 );
+ }
+ attacker = HelperGetRank( gLineBuffer[5][0] );
+ defender = HelperGetRank( gLineBuffer[6][0] );
+ }
+
+}
+
+#ifdef false
+
+static const char *board_setup_1[4] = { // 1 = Marshal, ..., 9 = Scout, : = Spy, ; = Bomb, < = Flag
+ "8;<;77;6;7",
+ "75;586896;",
+ "6989954893",
+ "943159:249",
+};
+
+static const char *bobs_board_setup[4] = { // 1 = Marshal, ..., 9 = Scout, : = Spy, ; = Bomb, < = Flag
+ "<8;645:1;6",
+ "8277984893",
+ ";;5756;7;8",
+ "9949359969",
+};
+
+#endif
+
+static const char *board_setup[4] = { // 1 = Marshal, ..., 9 = Scout, : = Spy, ; = Bomb, < = Flag
+// "8;<;67;;77",
+ "8;<;67;7;7",
+ "48;3862;89",
+ "6359954865",
+ "997159:499",
+};
+
+static const char *start_piece_counts = "0112344458161";
+
+static int dR[4] = { 1, 0, -1, 0 };
+static int dC[4] = { 0, -1, 0, 1 };
+
+#if ASSERTIONS
+
+static void AssertValidBoard( StoragePtr storage )
+{
+ int piece;
+ int count1 = 0;
+ int count2 = 0;
+ int row, col;
+
+ for ( piece = kMarshall; piece <= kFlag; piece++ ) {
+ count1 += storage->their_pieces[piece];
+ }
+
+ for ( row = 0; row < kBoardSize; row++ ) {
+ for( col = 0; col < kBoardSize; col++ ) {
+ if ( storage->board[row][col].color == storage->theirColor
+ && storage->board[row][col].rank == kUnknown ) {
+ count2++;
+ }
+ }
+ }
+
+ Assert( count1 == count2, "count1 == count2" );
+}
+
+#else
+
+#define AssertValidBoard( storage )
+
+#endif
+
+static void PositionPieces(
+ StoragePtr storage, /* 1MB of preinitialized storage for your use */
+ PlayerColor playerColor, /* you play red or blue, with red playing first */
+ Board *theBoard /* provide the initial position of your pieces */
+)
+{
+ int row, our_row, their_row, col, board_col;
+ PlayerColor theirColor;
+ int piece;
+ Boolean reverse = (time( NULL ) & 1) != 0;
+
+ Assert( strlen(board_setup[0]) == kBoardSize, "strlen(board_setup[0]) == kBoardSize" );
+ Assert( strlen(board_setup[1]) == kBoardSize, "strlen(board_setup[1]) == kBoardSize" );
+ Assert( strlen(board_setup[2]) == kBoardSize, "strlen(board_setup[2]) == kBoardSize" );
+ Assert( strlen(board_setup[3]) == kBoardSize, "strlen(board_setup[3]) == kBoardSize" );
+
+ for ( row = 0; row <= 3; row++ ) {
+ if ( playerColor == kRed ) {
+ our_row = row;
+ their_row = (kBoardSize-1)-row;
+ theirColor = kBlue;
+ } else {
+ their_row = row;
+ our_row = (kBoardSize-1)-row;
+ theirColor = kRed;
+ }
+ for ( col = 0; col < 10; col++ ) {
+ board_col = reverse ? (kBoardSize-1) - col : col;
+ (*theBoard)[our_row][col].thePieceRank = (PieceRank) (board_setup[row][board_col] - '0');
+ (*theBoard)[our_row][col].thePieceColor = playerColor;
+
+ storage->board[our_row][col].color = playerColor;
+ storage->board[our_row][col].rank = (*theBoard)[our_row][col].thePieceRank;
+ storage->board[our_row][col].possibilities = kNoNothing;
+
+ storage->board[their_row][col].color = theirColor;
+ storage->board[their_row][col].rank = kUnknown;
+ storage->board[their_row][col].possibilities = kNoNothing;
+ }
+ }
+
+ for ( row = 4; row <= 5; row++ ) {
+ for( col = 0; col < kBoardSize; col++ ) {
+ storage->board[row][col].color = (PlayerColor)kNoColor;
+ storage->board[row][col].rank = (PieceRank) ((col/2 % 2 == 1) ? kWater : kEmpty);
+ storage->board[row][col].possibilities = 0;
+ }
+ }
+
+ for ( piece = kMarshall; piece <= kFlag; piece++ ) {
+ storage->our_pieces[piece] = start_piece_counts[piece] - '0';
+ storage->their_pieces[piece] = start_piece_counts[piece] - '0';
+ }
+
+ storage->do_getmove = (playerColor == kBlue);
+ storage->victory = false;
+ storage->blankSquare = storage->board[4][0];
+ storage->playerColor = playerColor;
+ storage->theirColor = playerColor == kRed ? kBlue : kRed;
+ storage->repeated_board_count = 0;
+
+ AssertValidBoard( storage );
+}
+
+static void Learn( StoragePtr storage, Boolean them, int row, int col, PieceRank rank )
+{
+ Boolean gotall;
+ PlayerColor thiscolor;
+ int r, c;
+
+ if ( storage->board[row][col].rank == kUnknown ) {
+
+ if ( rank == kMoved ) {
+ UInt32 possibilities = storage->board[row][col].possibilities;
+ possibilities &= ~kStationaryBits;
+
+ if ( (possibilities & (possibilities-1)) == 0 ) { // only one bit on! Now we know!
+ int newrank;
+ newrank = 0;
+ while ( (possibilities & 1) == 0 ) {
+ possibilities >>= 1;
+ newrank++;
+ }
+ rank = (PieceRank)newrank;
+ } else {
+ storage->board[row][col].possibilities = possibilities;
+ }
+ }
+
+ if ( rank != kMoved ) {
+ storage->board[row][col].rank = rank;
+ storage->board[row][col].possibilities = (1 << rank);
+ if ( them ) {
+ gotall = --storage->their_pieces[rank] == 0;
+ } else {
+ gotall = --storage->our_pieces[rank] == 0;
+ }
+ if ( gotall ) {
+ thiscolor = storage->board[row][col].color;
+ for ( r = 0; r < kBoardSize; r++ ) {
+ for ( c = 0; c < kBoardSize; c++ ) {
+ if ( storage->board[r][c].rank == kUnknown
+ && storage->board[r][c].color == thiscolor ) {
+ UInt32 possibilities = storage->board[r][c].possibilities;
+ possibilities &= ~ (1 << rank);
+ storage->board[r][c].possibilities = possibilities;
+ if ( (possibilities & (possibilities-1)) == 0 ) { // only one bit on!
+ int newrank;
+ newrank = 0;
+ while ( (possibilities & 1) == 0 ) {
+ possibilities >>= 1;
+ newrank++;
+ }
+ Learn( storage, them, r, c, (PieceRank)newrank );
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ Assert( rank == kMoved || storage->board[row][col].rank == rank, "rank == kMoved || storage->board[row][col].rank == rank" );
+ }
+}
+
+static void HandleTheirMove( StoragePtr storage, const PiecePosition moveFrom, const PiecePosition moveTo, Boolean moveStrike, const MoveResult moveResult )
+{
+ Assert( moveResult.legalMove, "moveResult.legalMove" ); // They must have made a legal move or we would not be called
+ Assert( !moveResult.victory, "!moveResult.victory" ); // If they won we would not be called
+ if ( moveStrike ) {
+ Learn( storage, true, moveFrom.row, moveFrom.col, moveResult.rankOfAttacker.thePieceRank );
+ Learn( storage, false, moveTo.row, moveTo.col, moveResult.rankOfDefender.thePieceRank );
+ if ( moveResult.attackerRemoved && moveResult.defenderRemoved ) {
+ storage->board[moveFrom.row][moveFrom.col] = storage->blankSquare;
+ storage->board[moveTo.row][moveTo.col] = storage->blankSquare;
+ } else if ( moveResult.attackerRemoved ) {
+// if ( storage->board[moveTo.row][moveTo.col].rank == kBomb ) {
+ storage->board[moveFrom.row][moveFrom.col] = storage->blankSquare;
+// } else {
+// storage->board[moveFrom.row][moveFrom.col] = storage->board[moveTo.row][moveTo.col];
+// storage->board[moveTo.row][moveTo.col] = storage->blankSquare;
+// }
+ } else {
+ Assert( moveResult.defenderRemoved, "moveResult.defenderRemoved" );
+ storage->board[moveTo.row][moveTo.col] = storage->board[moveFrom.row][moveFrom.col];
+ storage->board[moveFrom.row][moveFrom.col] = storage->blankSquare;
+ }
+ } else {
+ storage->board[moveTo.row][moveTo.col] = storage->board[moveFrom.row][moveFrom.col];
+ storage->board[moveFrom.row][moveFrom.col] = storage->blankSquare;
+ if ( abs(moveTo.row - moveFrom.row) + abs(moveTo.col - moveFrom.col) > 1 ) {
+ Learn( storage, true, moveTo.row, moveTo.col, kScout );
+ } else {
+ Learn( storage, true, moveTo.row, moveTo.col, (PieceRank)kMoved );
+ }
+ }
+
+ AssertValidBoard( storage );
+}
+
+static Boolean FindPiece( StoragePtr storage, PlayerColor color, PieceRank rank, int *row, int *col )
+{
+ int r, c;
+
+ for ( r = 0; r < kBoardSize; r++ ) {
+ for( c = 0; c < kBoardSize; c++ ) {
+ if ( storage->board[r][c].color == color
+ && storage->board[r][c].rank == rank ) {
+ *row = r;
+ *col = c;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static Boolean IsOnBoardWeak( int row, int col )
+{
+ return 0 <= row && row < kBoardSize && 0 <= col && col < kBoardSize;
+}
+
+static Boolean IsOnBoard( int row, int col )
+{
+ if ( 0 <= row && row < kBoardSize && 0 <= col && col < kBoardSize ) {
+ if ( row <= 3 || row >= 6 ) {
+ return true;
+ }
+ if ( col <= 1 || col >= 8 ) {
+ return true;
+ }
+ if ( 4 <= col && col <= 5 ) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static Boolean IsColorPiece( StoragePtr storage, int row, int col, PlayerColor color )
+{
+ Assert( IsOnBoardWeak( row, col ), "IsOnBoardWeak( row, col )" );
+ return storage->board[row][col].color == color;
+}
+
+static Boolean IsOurPiece( StoragePtr storage, int row, int col )
+{
+ Assert( IsOnBoardWeak( row, col ), "IsOnBoardWeak( row, col )" );
+ return storage->board[row][col].color == storage->playerColor;
+}
+
+static Boolean IsTheirPiece( StoragePtr storage, int row, int col )
+{
+ Assert( IsOnBoardWeak( row, col ), "IsOnBoardWeak( row, col )" );
+ return storage->board[row][col].color == storage->theirColor;
+}
+
+static Boolean IsUnknownPiece( StoragePtr storage, int row, int col )
+{
+ Assert( IsOnBoardWeak( row, col ), "IsOnBoardWeak( row, col )" );
+ return storage->board[row][col].rank == kUnknown;
+}
+
+static Boolean IsRankPiece( StoragePtr storage, int row, int col, PieceRank rank )
+{
+ Assert( IsOnBoardWeak( row, col ), "IsOnBoardWeak( row, col )" );
+ return storage->board[row][col].rank == rank;
+}
+
+static Boolean IsEmptySquare( StoragePtr storage, int row, int col )
+{
+ Assert( IsOnBoardWeak( row, col ), "IsOnBoardWeak( row, col )" );
+ return storage->board[row][col].rank == (PieceRank)kEmpty;
+}
+
+static Boolean IsWaterSquare( StoragePtr storage, int row, int col )
+{
+ Assert( IsOnBoardWeak( row, col ), "IsOnBoardWeak( row, col )" );
+ return storage->board[row][col].rank == (PieceRank)kWater;
+}
+
+static Boolean IsLowlyRank( PieceRank rank )
+{
+ return kCaptain <= rank && rank <= kScout && rank != kMiner;
+}
+
+static Boolean IsLowlyPiece( StoragePtr storage, int row, int col )
+{
+ Assert( IsOnBoard( row, col ), "IsOnBoard( row, col )" );
+ return IsLowlyRank( storage->board[row][col].rank );
+}
+
+static Boolean IsMovedPiece( StoragePtr storage, int row, int col )
+{
+ Assert( IsOnBoard( row, col ), "IsOnBoard( row, col )" );
+ return (storage->board[row][col].possibilities & kStationaryBits) == 0;
+}
+
+static Boolean IsRevealedPiece( StoragePtr storage, int row, int col )
+{
+ Assert( IsOnBoard( row, col ), "IsOnBoard( row, col )" );
+ Assert( IsOurPiece( storage, row, col ), "IsOurPiece( storage, row, col )" );
+ UInt32 possibilities = storage->board[row][col].possibilities;
+ return ( (possibilities & (possibilities-1)) == 0 );
+}
+
+static int CountAdjacentUnknownPieces( StoragePtr storage, PlayerColor color, int row, int col )
+{
+ int d;
+ int unknowns = 0;
+
+ for ( d = 0; d < 4; d++ ) {
+ int r = row + dR[d];
+ int c = col + dC[d];
+
+ if ( IsOnBoard( r, c ) && IsColorPiece( storage, r, c, color ) && IsUnknownPiece( storage, r, c ) ) {
+ unknowns++;
+ }
+ }
+
+ return unknowns;
+}
+
+static const char *defend_spy_table = "RARROAOORARRRARRXAXAOAOOXAXAXAXA";
+// Run/Attack/Other/Nothing, >1 unknown:other:danger:moved
+
+static Boolean LowlyCanAttack( StoragePtr storage, int row, int col, int *otherRow, int *otherCol )
+{
+ for ( int d = 0; d < 4; d++ ) {
+ int r = row + dR[d];
+ int c = col + dC[d];
+
+ if ( IsOnBoard( r, c )
+ && IsOurPiece( storage, r, c )
+ && IsLowlyPiece( storage, r, c ) ) {
+ *otherRow = r;
+ *otherCol = c;
+ return true;
+ }
+ }
+ return false;
+}
+
+static void UpdateDangerPossibilities( StoragePtr storage )
+{
+ int row, col;
+
+ for ( row = 0; row < kBoardSize; row++ ) {
+ for( col = 0; col < kBoardSize; col++ ) {
+ storage->dangers[row][col] = 0;
+ storage->known_dangers[row][col] = 0;
+ }
+ }
+
+ for ( row = 0; row < kBoardSize; row++ ) {
+ for( col = 0; col < kBoardSize; col++ ) {
+ if ( IsTheirPiece( storage, row, col ) ) {
+ UInt32 possibilities = (storage->board[row][col].possibilities & ~kStationaryBits);
+ UInt32 known_possibilities = 0;
+
+ if ( storage->board[row][col].rank != kUnknown ) {
+ known_possibilities = possibilities;
+ }
+
+ for ( int d = 0; d < 4; d++ ) {
+ int r = row + dR[d];
+ int c = col + dC[d];
+
+ if ( IsOnBoard( r, c ) ) {
+ storage->dangers[r][c] |= possibilities;
+ storage->known_dangers[r][c] |= known_possibilities;
+ }
+ }
+
+ }
+ }
+ }
+
+}
+
+static UInt32 GetDangerPossibilities( StoragePtr storage, int row, int col )
+{
+ Assert( IsOnBoard( row, col ), "IsOnBoard( row, col )" );
+ return storage->dangers[row][col];
+}
+
+static Boolean PossibilitiesCouldKill( PieceRank rank, UInt32 possibilities )
+{
+ if ( (possibilities & ~kStationaryBits) == 0 ) {
+ return false;
+ }
+
+ switch ( rank ) {
+ case kFlag:
+ return true;
+ case kBomb:
+ return (possibilities & (1 << kMiner)) != 0;
+ case kMarshall:
+ return (possibilities & ((1 << kMarshall) + (1<< kSpy))) != 0;
+ default:
+ return (possibilities & ((1 << (rank+1)) - 1)) != 0;
+ }
+}
+
+static Boolean PossibilitiesCouldKillSafely( PieceRank rank, UInt32 possibilities )
+{
+ if ( (possibilities & ~kStationaryBits) == 0 ) {
+ return false;
+ }
+
+ switch ( rank ) {
+ case kFlag:
+ return true;
+ case kBomb:
+ return (possibilities & (1 << kMiner)) != 0;
+ case kMarshall:
+ return (possibilities & ((1<< kSpy))) != 0;
+ default:
+ return (possibilities & ((1 << rank) - 1)) != 0;
+ }
+}
+
+static Boolean WillKillPossibilities( PieceRank rank, UInt32 possibilities )
+{
+ Assert( possibilities != 0, "possibilities != 0" );
+
+ switch ( rank ) {
+ case kFlag:
+ return false;
+ case kBomb:
+ return false;
+ case kMiner:
+ return (possibilities & ~((1 << kScout) + (1 << kBomb) + (1 << kFlag))) == 0;
+ case kSpy:
+ return (possibilities & ~(1 << kMarshall)) == 0;
+ default:
+ return (possibilities & (((1 << (rank + 1)) - 1) + (1 << kBomb))) == 0;
+ }
+}
+
+static Boolean WillKillOrSuicidePossibilities( PieceRank rank, UInt32 possibilities )
+{
+ Assert( possibilities != 0, "possibilities != 0" );
+
+ switch ( rank ) {
+ case kFlag:
+ return false;
+ case kBomb:
+ return false;
+ case kMiner:
+ return (possibilities & ~((1 << kScout) + (1 << kMiner) + (1 << kBomb) + (1 << kFlag))) == 0;
+ case kSpy:
+ return (possibilities & ~((1 << kMarshall) + (1 << kSpy))) == 0;
+ default:
+ return (possibilities & (((1 << rank) - 1) + (1 << kBomb))) == 0;
+ }
+}
+
+static Boolean WillPossibilitiesKill( UInt32 possibilities, PieceRank rank )
+{
+ Assert( possibilities != 0, "possibilities != 0" );
+ possibilities &= ~kStationaryBits;
+ if ( possibilities == 0 ) {
+ return false;
+ }
+
+ switch ( rank ) {
+ case kFlag:
+ return true;
+ case kBomb:
+ return possibilities == (1 << kMiner);
+ default:
+ return (possibilities & ~((1 << (rank+1))-1)) == 0;
+ }
+}
+
+static Boolean FindSafeSquare( StoragePtr storage, int row, int col, int *safeRow, int *safeCol )
+{
+ Assert( IsOnBoard( row, col ), "IsOnBoard( row, col )" );
+
+ PieceRank rank = storage->board[row][col].rank;
+ int doff = (storage->playerColor == kBlue ? 0 : 2); // Try backwards first
+
+ for ( int d = 0; d < 4; d++ ) {
+ int dr = dR[(d + doff) % 4];
+ int dc = dC[(d + doff) % 4];
+ int r = row + dr;
+ int c = col + dc;
+
+ while ( IsOnBoard( r, c ) && IsEmptySquare( storage, r, c ) ) {
+ if ( !PossibilitiesCouldKill( rank, GetDangerPossibilities( storage, r, c ) ) ) {
+ *safeRow = r;
+ *safeCol = c;
+ return true;
+ }
+ if ( rank != kScout ) {
+ break;
+ }
+ r += dr;
+ c += dc;
+ }
+ }
+ return false;
+}
+
+static void CountEnemies( StoragePtr storage, int row, int col, int *knowns, int *unknowns )
+{
+ *knowns = 0;
+ *unknowns = 0;
+
+ for ( int d = 0; d < 4; d++ ) {
+ int r = row + dR[d];
+ int c = col + dC[d];
+
+ if ( IsOnBoard( r, c ) && IsTheirPiece( storage, r, c ) ) {
+ if ( storage->board[r][c].rank == kUnknown ) {
+ *unknowns += 1;
+ } else {
+ *knowns += 1;
+ }
+ }
+ }
+}
+
+/*
+static Boolean CanRun( StoragePtr storage, int row, int col, int *runRow, int *runCol )
+{
+ for ( int d = 0; d < 4; d++ ) {
+ int r = row + dR[(d + (storage->playerColor == kBlue ? 0 : 2)) % 4]; // Try backwards first
+ int c = col + dC[(d + (storage->playerColor == kBlue ? 0 : 2)) % 4];
+
+ if ( IsOnBoard( r, c ) && (storage->board[r][c].rank == kEmpty) ) {
+ *runRow = r;
+ *runCol = c;
+ return true;
+ }
+ }
+ return false;
+}
+*/
+
+static Boolean FindSafePath( StoragePtr storage, Boolean very_safe, Boolean suicide_ok, int from_row, int from_col, int to_row, int to_col, int *best_path, int *first_row, int *first_col )
+{
+ Assert( IsOurPiece( storage, from_row, from_col ), "IsOurPiece( storage, from_row, from_col )" );
+
+ PieceRank rank = storage->board[from_row][from_col].rank;
+ BoardPossibilities *dangers = very_safe ? &storage->dangers : &storage->known_dangers;
+
+ if ( abs( from_row - to_row ) + abs( from_col - to_col ) > *best_path ) {
+ return false;
+ }
+
+ if ( abs( from_row - to_row ) + abs( from_col - to_col ) == 1 ) {
+ *best_path = 0;
+ *first_row = to_row;
+ *first_col = to_col;
+ return true;
+ }
+
+ int path_length_to[kBoardSize][kBoardSize];
+ PiecePosition que[kBoardSize * kBoardSize];
+ int que_start = 0;
+ int que_fin = 0;
+ int que_next_len = 0;
+ int current_len = 0;
+ int row, col;
+
+ for ( row = 0; row < kBoardSize; row++ ) {
+ for( col = 0; col < kBoardSize; col++ ) {
+ path_length_to[row][col] = -1;
+ }
+ }
+
+ que[que_fin].row = from_row;
+ que[que_fin].col = from_col;
+ path_length_to[from_row][from_col] = 0;
+ que_fin++;
+ que_next_len = que_fin;
+
+ while ( que_fin > que_start ) {
+ row = que[que_start].row;
+ col = que[que_start].col;
+ que_start++;
+
+ for ( int d = 0; d < 4; d++ ) {
+ int dr = dR[d];
+ int dc = dC[d];
+// scout moves NYI
+ int r = row + dr;
+ int c = col + dc;
+
+ if ( IsOnBoard( r, c ) && path_length_to[r][c] == -1
+ && IsEmptySquare( storage, r, c ) ) {
+ if ( suicide_ok
+ ? !PossibilitiesCouldKillSafely( rank, (*dangers)[r][c] )
+ : !PossibilitiesCouldKill( rank, (*dangers)[r][c] ) ) {
+ path_length_to[r][c] = current_len + 1;
+ if ( abs( to_row - r ) + abs( to_col - c ) == 1 ) {
+ *best_path = current_len + 1;
+ while ( current_len > 0 ) {
+ for ( int d = 0; d < 4; d++ ) {
+ int backr = r + dR[d];
+ int backc = c + dC[d];
+
+ if ( path_length_to[backr][backc] == current_len ) {
+ r = backr;
+ c = backc;
+ break;
+ }
+ }
+ current_len--;
+ }
+ *first_row = r;
+ *first_col = c;
+ return true;
+ }
+ que[que_fin].row = r;
+ que[que_fin].col = c;
+ que_fin++;
+ } else {
+ path_length_to[r][c] = 1000; // Cant go here
+ }
+ }
+ }
+
+ if ( que_start == que_next_len ) {
+ que_next_len = que_fin;
+ current_len++;
+ }
+ }
+
+ return false;
+}
+
+static UInt32 CalcBoardCRC( StoragePtr storage, int from_row, int from_col, int to_row, int to_col )
+{
+ Assert( !IsOnBoard( from_row, from_col ) || IsOurPiece( storage, from_row, from_col ), "!IsOnBoard( from_row, from_col ) || IsOurPiece( storage, from_row, from_col )" );
+ Assert( !IsOnBoard( to_row, to_col ) || IsEmptySquare( storage, to_row, to_col ), "!IsOnBoard( to_row, to_col ) || IsEmptySquare( storage, to_row, to_col )" );
+
+ UInt32 result = 0;
+
+ int row, col;
+ int rankish;
+
+ for ( row = 0; row < kBoardSize; row++ ) {
+ for( col = 0; col < kBoardSize; col++ ) {
+ if ( row == from_row && col == from_col ) {
+ rankish = 0;
+ } else if ( row == to_row && col == to_col ) {
+ rankish = storage->board[from_row][from_col].rank;
+ } else if ( IsEmptySquare( storage, row, col ) || IsWaterSquare( storage, row, col ) ) {
+ rankish = 0;
+ } else if ( IsOurPiece( storage, row, col ) ) {
+ rankish = storage->board[row][col].rank;
+ } else {
+ rankish = storage->board[row][col].rank + kAddForRankish;
+ }
+ result += rankish; // Hmm, not a very good CRC
+ result = result * 11 + (result >> 25);
+ }
+ }
+
+ return result;
+}
+
+static Boolean OKMove( StoragePtr storage, int from_row, int from_col, int to_row, int to_col )
+{
+ if ( IsTheirPiece( storage, to_row, to_col ) ) {
+ return true;
+ }
+
+ UInt32 crc = CalcBoardCRC( storage, from_row, from_col, to_row, to_col );
+ for ( UInt32 i = 0; i < storage->repeated_board_count; i++ ) {
+ if ( crc == storage->repeated_board[i] ) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static void AppendRepeatedBoard( StoragePtr storage )
+{
+ UInt32 crc = CalcBoardCRC( storage, -1, -1, -1, -1 );
+
+ if ( storage->repeated_board_count == kRepeatedBoards ) {
+ storage->repeated_board_count--;
+ memcpy( &storage->repeated_board[0], &storage->repeated_board[1], storage->repeated_board_count * sizeof(storage->repeated_board[0]) );
+ }
+ storage->repeated_board[storage->repeated_board_count++] = crc;
+}
+
+#if DEBUG_RULES
+ #define RETURN( x ) Log( x ); return
+#else
+ #define RETURN( x ) return
+#endif
+
+static void FigureOutOurMove( StoragePtr storage, PiecePosition *moveFrom, PiecePosition *moveTo )
+{
+ int ourRow, ourCol, theirRow, theirCol, row, col, runRow, runCol;
+ int rowFirst = storage->playerColor == kRed ? 0 : kBoardSize - 1;
+ int rowLast = storage->playerColor == kRed ? kBoardSize - 1 : 0;
+ int rowAdd = storage->playerColor == kRed ? 1 : -1;
+ int bestUnknowns;
+ int bestPath;
+ int thisPath;
+
+ UpdateDangerPossibilities( storage );
+
+// USE SPY
+ if ( FindPiece( storage, storage->theirColor, kMarshall, &theirRow, &theirCol )
+ && FindPiece( storage, storage->playerColor, kSpy, &ourRow, &ourCol )
+ && abs( theirRow - ourRow ) + abs( theirCol - ourCol ) == 1 ) {
+ moveFrom->row = ourRow;
+ moveFrom->col = ourCol;
+ moveTo->row = theirRow;
+ moveTo->col = theirCol;
+ RETURN( "USE SPY" );
+ }
+
+// DEFEND AGAINST SPY
+ if (storage->their_pieces[kSpy] > 0) {
+ if ( FindPiece( storage, storage->playerColor, kMarshall, &ourRow, &ourCol ) ) {
+ int unknowns = CountAdjacentUnknownPieces( storage, storage->theirColor, ourRow, ourCol );
+
+ if ( unknowns ) {
+ int base_index = 0;
+ Boolean canrun = FindSafeSquare( storage, ourRow, ourCol, &runRow, &runCol );
+ if ( !canrun ) {
+ base_index += 16;
+ }
+ if ( unknowns > 1 ) {
+ base_index += 8;
+ }
+
+ for ( int d = 0; d < 4; d++ ) {
+ int r = ourRow + dR[d];
+ int c = ourCol + dC[d];
+ int otherRow, otherCol;
+
+ if ( IsOnBoard( r, c )
+ && IsTheirPiece( storage, r, c )
+ && IsUnknownPiece( storage, r, c ) ) {
+ int index = base_index;
+ if ( LowlyCanAttack( storage, r, c, &otherRow, &otherCol ) ) {
+ index += 4;
+ }
+ if ( CountAdjacentUnknownPieces( storage, storage->theirColor, r, c ) > 0 ) {
+ index += 2;
+ }
+ if ( IsMovedPiece( storage, r, c ) ) {
+ index += 1;
+ }
+
+ if ( defend_spy_table[index] == 'A' ) { // Attack
+ moveFrom->row = ourRow;
+ moveFrom->col = ourCol;
+ moveTo->row = r;
+ moveTo->col = c;
+ RETURN( "DEFEND AGAINST SPY 1" );
+ } else if ( defend_spy_table[index] == 'O' ) { // Attack
+ moveFrom->row = otherRow;
+ moveFrom->col = otherCol;
+ moveTo->row = r;
+ moveTo->col = c;
+ RETURN( "DEFEND AGAINST SPY 2" );
+ }
+ }
+ }
+
+ if ( canrun && OKMove( storage, ourRow, ourCol, runRow, runCol ) ) {
+ moveFrom->row = ourRow;
+ moveFrom->col = ourCol;
+ moveTo->row = runRow;
+ moveTo->col = runCol;
+ RETURN( "DEFEND AGAINST SPY 3" );
+ }
+ // Give up! Next ruleÉ
+ }
+ }
+ }
+
+// ATTACK WEAKER
+ for ( row = rowFirst; 0 <= row && row < kBoardSize; row += rowAdd ) {
+ for( col = 0; col < kBoardSize; col++ ) {
+ if ( IsTheirPiece( storage, row, col ) ) {
+ UInt32 enemy = storage->board[row][col].possibilities;
+ UInt32 danger = GetDangerPossibilities( storage, row, col );
+
+ int bestDir = -1;
+ Boolean isBestRevealed = true;
+ PieceRank bestRank = kUnknown;
+
+ for ( int d = 0; d < 4; d++ ) {
+ int r = row + dR[d];
+ int c = col + dC[d];
+
+ if ( IsOnBoard( r, c ) && IsOurPiece( storage, r, c ) ) {
+ if ( !PossibilitiesCouldKill( storage->board[r][c].rank, danger ) ) {
+ if ( WillKillPossibilities( storage->board[r][c].rank, enemy ) ) {
+ Boolean thisRevealed = IsRevealedPiece( storage, r, c );
+ if ( isBestRevealed || !thisRevealed ) {
+ if ( bestDir == -1 || (storage->board[r][c].rank > bestRank) ) {
+ bestDir = d;
+ bestRank = storage->board[r][c].rank;
+ isBestRevealed = thisRevealed;
+ }
+ }
+ }
+ }
+ }
+ }
+ if ( bestDir != -1 ) {
+ moveFrom->row = row + dR[bestDir];
+ moveFrom->col = col + dC[bestDir];
+ moveTo->row = row;
+ moveTo->col = col;
+ RETURN( "ATTACK WEAKER" );
+ }
+ }
+ }
+ }
+
+// EXPLORE ATTACK
+ for ( int rnk = kScout; rnk >= kMarshall; rnk-- ) {
+ PieceRank rank = (PieceRank) rnk;
+ if ( IsLowlyRank( rank ) ) {
+
+ for ( row = rowLast; 0 <= row && row < kBoardSize; row -= rowAdd ) {
+ for( col = 0; col < kBoardSize; col++ ) {
+ if ( IsOurPiece( storage, row, col )
+ && IsRankPiece( storage, row, col, rank ) ) {
+
+ for ( int d = 0; d < 4; d++ ) {
+ int r = row + dR[d];
+ int c = col + dC[d];
+
+ if ( IsOnBoard( r, c )
+ && IsTheirPiece( storage, r, c )
+ && IsRankPiece( storage, r, c, kUnknown ) ) {
+ moveFrom->row = row;
+ moveFrom->col = col;
+ moveTo->row = r;
+ moveTo->col = c;
+ RETURN( "EXPLORE ATTACK" );
+ }
+ }
+ }
+ }
+ }
+
+ }
+ }
+
+// RETREAT
+ for ( row = rowLast; 0 <= row && row < kBoardSize; row -= rowAdd ) {
+ for( col = 0; col < kBoardSize; col++ ) {
+ if ( IsOurPiece( storage, row, col )
+ && IsMovedPiece( storage, row, col ) ) {
+
+ for ( int d = 0; d < 4; d++ ) {
+ int r = row + dR[d];
+ int c = col + dC[d];
+
+ if ( IsOnBoard( r, c )
+ && IsTheirPiece( storage, r, c )
+ && WillPossibilitiesKill( storage->board[r][c].possibilities, storage->board[row][col].rank ) ) {
+ bestPath = 1000;
+ for ( int to_row = rowLast; 0 <= to_row && to_row < kBoardSize; to_row -= rowAdd ) {
+ for( int to_col = 0; to_col < kBoardSize; to_col++ ) {
+ thisPath = bestPath;
+ if ( IsTheirPiece( storage, to_row, to_col )
+ && (IsRankPiece( storage, to_row, to_col, kUnknown )
+ || WillKillPossibilities( storage->board[row][col].rank, storage->board[to_row][to_col].possibilities ))
+ && FindSafePath( storage, false, true, row, col, to_row, to_col, &thisPath, &runRow, &runCol )
+ && OKMove( storage, row, col, runRow, runCol ) ) {
+ bestPath = thisPath;
+ moveFrom->row = row;
+ moveFrom->col = col;
+ moveTo->row = runRow;
+ moveTo->col = runCol;
+ }
+ }
+ }
+ if ( bestPath < 1000 ) {
+ RETURN( "RETREAT" );
+ }
+ }
+ }
+ }
+ }
+ }
+
+// SCOUT
+ bestUnknowns = 0;
+
+ for ( row = rowLast; 0 <= row && row < kBoardSize; row -= rowAdd ) {
+ for( col = 0; col < kBoardSize; col++ ) {
+ if ( IsOurPiece( storage, row, col )
+ && IsRankPiece( storage, row, col, kScout ) ) {
+ for ( int d = 0; d < 4; d++ ) {
+ int r = row + dR[d];
+ int c = col + dC[d];
+
+ while ( IsOnBoard( r, c ) && IsEmptySquare( storage, r, c ) ) {
+
+ int knowns, unknowns;
+ CountEnemies( storage, r, c, &knowns, &unknowns );
+ if ( knowns == 0 && unknowns > bestUnknowns && OKMove( storage, row, col, r, c ) ) {
+ bestUnknowns = unknowns;
+ ourRow = row;
+ ourCol = col;
+ runRow = r;
+ runCol = c;
+ }
+ r += dR[d];
+ c += dC[d];
+ }
+ }
+ }
+ }
+ }
+
+ if ( bestUnknowns > 0 ) {
+ moveFrom->row = ourRow;
+ moveFrom->col = ourCol;
+ moveTo->row = runRow;
+ moveTo->col = runCol;
+ RETURN( "SCOUT" );
+ }
+
+// ATTACK DISTANT
+
+ bestPath = 1000;
+
+ for ( row = rowFirst; 0 <= row && row < kBoardSize; row += rowAdd ) {
+ for( col = 0; col < kBoardSize; col++ ) {
+ if ( IsTheirPiece( storage, row, col ) ) {
+ UInt32 possibilities = storage->board[row][col].possibilities;
+ UInt32 danger = GetDangerPossibilities( storage, row, col );
+
+ if ( (possibilities & ((1 << kBomb) | (1 << kMarshall))) != ((1 << kBomb) | (1 << kMarshall)) ) {
+ for ( int r = rowFirst; 0 <= r && r < kBoardSize; r += rowAdd ) {
+ for( int c = 0; c < kBoardSize; c++ ) {
+ if ( IsOurPiece( storage, r, c ) ) {
+ if ( WillKillPossibilities( storage->board[r][c].rank, possibilities ) ) {
+ if ( storage->board[r][c].rank >= kCaptain || !PossibilitiesCouldKill( storage->board[r][c].rank, danger ) ) {
+ thisPath = bestPath;
+ if ( FindSafePath( storage, true, false, r, c, row, col, &thisPath, &runRow, &runCol ) ) {
+ if ( OKMove( storage, r, c, runRow, runCol ) ) {
+ bestPath = thisPath;
+ moveFrom->row = r;
+ moveFrom->col = c;
+ moveTo->row = runRow;
+ moveTo->col = runCol;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+ }
+ }
+
+ if ( bestPath < 1000 ) {
+ RETURN( "ATTACK DISTANT" );
+ }
+
+// EXPLORE DISTANT
+
+ bestPath = 1000;
+
+ for ( row = rowFirst; 0 <= row && row < kBoardSize; row += rowAdd ) {
+ for( col = 0; col < kBoardSize; col++ ) {
+ if ( IsTheirPiece( storage, row, col ) && storage->board[row][col].rank == kUnknown ) {
+
+ for ( int r = rowFirst; 0 <= r && r < kBoardSize; r += rowAdd ) {
+ for( int c = 0; c < kBoardSize; c++ ) {
+ if ( IsOurPiece( storage, r, c ) && IsLowlyPiece( storage, r, c ) ) {
+ thisPath = bestPath;
+ if ( FindSafePath( storage, false, true, r, c, row, col, &thisPath, &runRow, &runCol ) ) {
+ if ( OKMove( storage, r, c, runRow, runCol ) ) {
+ bestPath = thisPath;
+ moveFrom->row = r;
+ moveFrom->col = c;
+ moveTo->row = runRow;
+ moveTo->col = runCol;
+ }
+ }
+ }
+ }
+ }
+
+ }
+ }
+ }
+
+ if ( bestPath < 1000 ) {
+ RETURN( "EXPLORE DISTANT" );
+ }
+
+// ATTACK KNOWN WITH SAME DISTANT
+
+ bestPath = 1000;
+
+ for ( row = rowFirst; 0 <= row && row < kBoardSize; row += rowAdd ) {
+ for( col = 0; col < kBoardSize; col++ ) {
+ if ( IsTheirPiece( storage, row, col ) ) {
+ UInt32 possibilities = storage->board[row][col].possibilities;
+
+ if ( (possibilities & ((1 << kBomb) | (1 << kMarshall))) != ((1 << kBomb) | (1 << kMarshall)) ) {
+ for ( int r = rowFirst; 0 <= r && r < kBoardSize; r += rowAdd ) {
+ for( int c = 0; c < kBoardSize; c++ ) {
+ if ( IsOurPiece( storage, r, c ) ) {
+ if ( WillKillOrSuicidePossibilities( storage->board[r][c].rank, possibilities ) ) {
+ thisPath = bestPath;
+ if ( FindSafePath( storage, true, true, r, c, row, col, &thisPath, &runRow, &runCol ) ) {
+ if ( OKMove( storage, r, c, runRow, runCol ) ) {
+ bestPath = thisPath;
+ moveFrom->row = r;
+ moveFrom->col = c;
+ moveTo->row = runRow;
+ moveTo->col = runCol;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+ }
+ }
+
+ if ( bestPath < 1000 ) {
+ RETURN( "ATTACK KNOWN WITH SAME DISTANT" );
+ }
+
+// FIND FLAG
+// NYI
+
+// MOVE FORWARD
+
+ for ( row = rowLast; 0 <= row && row < kBoardSize; row -= rowAdd ) {
+ for( col = 0; col < kBoardSize; col++ ) {
+ if ( IsOurPiece( storage, row, col ) ) {
+ PieceRank rank = storage->board[row][col].rank;
+ if ( rank != kBomb && rank != kFlag ) {
+ int r = row + rowAdd;
+ if ( IsOnBoard( r, col ) && !IsOurPiece( storage, r, col ) && OKMove( storage, row, col, r, col ) ) {
+ moveFrom->row = row;
+ moveFrom->col = col;
+ moveTo->row = r;
+ moveTo->col = col;
+ RETURN( "MOVE FORWARD" );
+ }
+ }
+ }
+ }
+ }
+
+// MOVE
+
+ for ( row = rowLast; 0 <= row && row < kBoardSize; row -= rowAdd ) {
+ for( col = 0; col < kBoardSize; col++ ) {
+ if ( IsOurPiece( storage, row, col ) ) {
+ PieceRank rank = storage->board[row][col].rank;
+ if ( rank != kBomb && rank != kFlag ) {
+
+ for ( int d = 0; d < 4; d++ ) {
+ int r = row + dR[d];
+ int c = col + dC[d];
+
+ if ( IsOnBoard( r, c ) && !IsOurPiece( storage, r, c ) && OKMove( storage, row, col, r, c ) ) {
+ moveFrom->row = row;
+ moveFrom->col = col;
+ moveTo->row = r;
+ moveTo->col = c;
+ RETURN( "MOVE" );
+ }
+ }
+ }
+ }
+ }
+ }
+
+// RESIGN
+ moveFrom->row = -1;
+ moveFrom->col = -1;
+ moveTo->row = -1;
+ moveTo->col = -1;
+ RETURN( "RESIGN" );
+
+}
+
+static void HandleOurMove( StoragePtr storage, PiecePosition moveFrom, PiecePosition moveTo, const MoveResult moveResult )
+{
+ Boolean moveStrike;
+
+ if ( IsOnBoard( moveTo.row, moveTo.col ) ) {
+ moveStrike = storage->board[moveTo.row][moveTo.col].color != kNoColor;
+ } else {
+ moveStrike = false;
+ }
+
+ if ( moveResult.victory ) { // We Win! :-)
+ storage->victory = true;
+ } else if ( !moveResult.legalMove ) { // We Lose! :-(
+ } else {
+ if ( moveStrike ) {
+ storage->repeated_board_count = 0;
+ Learn( storage, true, moveTo.row, moveTo.col, moveResult.rankOfDefender.thePieceRank );
+ Learn( storage, false, moveFrom.row, moveFrom.col, moveResult.rankOfAttacker.thePieceRank );
+
+ if ( moveResult.attackerRemoved && moveResult.defenderRemoved ) {
+ storage->board[moveFrom.row][moveFrom.col] = storage->blankSquare;
+ storage->board[moveTo.row][moveTo.col] = storage->blankSquare;
+ } else if ( moveResult.attackerRemoved ) {
+// if ( storage->board[moveTo.row][moveTo.col].rank == kBomb ) {
+ storage->board[moveFrom.row][moveFrom.col] = storage->blankSquare;
+// } else {
+// storage->board[moveFrom.row][moveFrom.col] = storage->board[moveTo.row][moveTo.col];
+// storage->board[moveTo.row][moveTo.col] = storage->blankSquare;
+// }
+ } else {
+ Assert( moveResult.defenderRemoved, "moveResult.defenderRemoved" );
+ storage->board[moveTo.row][moveTo.col] = storage->board[moveFrom.row][moveFrom.col];
+ storage->board[moveFrom.row][moveFrom.col] = storage->blankSquare;
+ }
+
+ } else {
+ if ( abs( moveTo.row - moveFrom.row ) + abs( moveTo.col - moveFrom.col ) > 1 ) {
+ Assert( storage->board[moveFrom.row][moveFrom.col].rank == kScout, "storage->board[moveFrom.row][moveFrom.col].rank == kScout" );
+ Learn( storage, false, moveFrom.row, moveFrom.col, kScout );
+ } else {
+ Learn( storage, false, moveFrom.row, moveFrom.col, (PieceRank)kMoved );
+ }
+ storage->board[moveTo.row][moveTo.col] = storage->board[moveFrom.row][moveFrom.col];
+ storage->board[moveFrom.row][moveFrom.col] = storage->blankSquare;
+ }
+ AppendRepeatedBoard( storage );
+ }
+
+ AssertValidBoard( storage );
+}
+
+/*
+Boolean MakeAMove(
+ StoragePtr storage, / * 1MB of storage from PositionPieces * /
+ PlayerColor playerColor, / * you play red or blue, with red playing first * /
+ GetOpponentMove *GetMove, / * callback used to find about opponents last move * /
+ ReportYourMove *ReportMove / * callback used to make a move * /
+)
+{
+ if ( storage->do_getmove ) {
+ HandleTheirMove( storage, *GetMove );
+ }
+ storage->do_getmove = true;
+
+ HandleOurMove( storage, *ReportMove );
+
+ return storage->victory;
+}
+*/
+
+// Code to map UCC Challenge to MacTech Challenge
+
+static PlayerColor ReadPlayerLine()
+{
+ HelperReadLine();
+
+ std::string colourStr = gLineBuffer[0];
+
+ if ( colourStr == "RED" ) {
+ return kRed;
+ } else if ( colourStr == "BLUE" ) {
+ return kBlue;
+ } else {
+ Log( string("What color? ") + colourStr );
+ exit( 99 );
+ }
+}
+
+static PlayerColor OtherPlayerColor( PlayerColor player )
+{
+ return (player == kRed) ? kBlue : kRed;
+}
+
+int main(int argc, char ** argv)
+{
+ srand(time(NULL));
+ cin.rdbuf()->pubsetbuf(NULL, 0);
+ cout.rdbuf()->pubsetbuf(NULL, 0);
+ cout.setf(std::ios::unitbuf);
+#if SAVE_OUTPUT
+ gOutputFile.open( (string("/tmp/peternlewis-output-") + to_string(time( NULL )) + "-" + to_string(rand() & 0xFFFF) + ".log").c_str() );
+ gOutputFile.setf(std::ios::unitbuf);
+#endif
+
+ Storage storage;
+ Board board;
+
+ PlayerColor player = ReadPlayerLine();
+
+ PositionPieces( &storage, player, &board );
+ if ( player == kRed ) {
+ for ( int r = 0; r <= 3; r++ ) {
+ string line;
+ for ( int c = 0; c < kBoardSize; c++ ) {
+ line += gPieceCharacters[board[r][c].thePieceRank];
+ }
+ Output( line );
+ }
+ } else {
+ for ( int r = kBoardSize - 4; r < kBoardSize; r++ ) {
+ string line;
+ for ( int c = 0; c < kBoardSize; c++ ) {
+ line += gPieceCharacters[board[r][c].thePieceRank];
+ }
+ Output( line );
+ }
+ }
+
+ bool expectStart = (player == kRed);
+ while ( 1 ) {
+ Debug( "LOOP" );
+ if ( expectStart ) {
+ HelperReadLine();
+ expectStart = false;
+ } else {
+ PiecePosition from;
+ PiecePosition to;
+ MoveOutcome result;
+ PieceRank attacker;
+ PieceRank defender;
+ HelperReadMove( &storage, from, to, result, attacker, defender );
+ Debug( to_string(from.col) + "," + to_string(from.row) + " -> " + to_string(to.col) + "," + to_string(to.row) );
+ MoveResult moveResult;
+ moveResult.rankOfAttacker.thePieceRank = attacker;
+ moveResult.rankOfAttacker.thePieceColor = OtherPlayerColor( player );
+ moveResult.rankOfDefender.thePieceRank = defender;
+ moveResult.rankOfDefender.thePieceColor = player;
+ moveResult.attackerRemoved = (result == kMR_Dies) || (result == kMR_BothDie);
+ moveResult.defenderRemoved = (result == kMR_Kills) || (result == kMR_BothDie);
+ moveResult.victory = false;
+ moveResult.legalMove = true;
+ HandleTheirMove( &storage, from, to, (result != kMR_OK), moveResult );
+ }
+ HelperJunkLine( kBoardSize );
+ DumpBoard( &storage );
+
+ PiecePosition moveFrom;
+ PiecePosition moveTo;
+
+ FigureOutOurMove( &storage, &moveFrom, &moveTo );
+ Debug( to_string(moveFrom.col) + ',' + to_string(moveFrom.row) + " -> " + to_string(moveTo.col) + ',' + to_string(moveTo.row) );
+ if ( moveFrom.row < 0 ) {
+ Output( "SURRENDER" );
+ exit(EXIT_SUCCESS);
+ }
+ std::string dir;
+ int move;
+ if ( moveTo.col > moveFrom.col ) {
+ dir = "RIGHT";
+ move = moveTo.col - moveFrom.col;
+ } else if ( moveTo.col < moveFrom.col ) {
+ dir = "LEFT";
+ move = moveFrom.col - moveTo.col;
+ } else if ( moveTo.row < moveFrom.row ) {
+ dir = "UP";
+ move = moveFrom.row - moveTo.row;
+ } else if ( moveTo.row > moveFrom.row ) {
+ dir = "DOWN";
+ move = moveTo.row - moveFrom.row;
+ }
+ Output( to_string(moveFrom.col) + ' ' + to_string(moveFrom.row) + ' ' + dir + ' ' + to_string(move) );
+ {
+ PiecePosition from;
+ PiecePosition to;
+ MoveOutcome result;
+ PieceRank attacker;
+ PieceRank defender;
+ HelperReadMove( &storage, from, to, result, attacker, defender );
+ MoveResult moveResult;
+ moveResult.rankOfAttacker.thePieceRank = attacker;
+ moveResult.rankOfAttacker.thePieceColor = player;
+ moveResult.rankOfDefender.thePieceRank = defender;
+ moveResult.rankOfDefender.thePieceColor = OtherPlayerColor( player );
+ moveResult.attackerRemoved = (result == kMR_Dies) || (result == kMR_BothDie);
+ moveResult.defenderRemoved = (result == kMR_Kills) || (result == kMR_BothDie);
+ moveResult.victory = false;
+ moveResult.legalMove = true;
+ HandleOurMove( &storage, from, to, moveResult );
+ DumpBoard( &storage );
+ }
+ }
+
+ exit(EXIT_SUCCESS);
+ return 0;
+}