2 * Class to manage a Stratego playing AI in Java
3 * @author Sam Moore for the UCC::Progcomp 2012
4 * @website http://progcomp.ucc.asn.au
7 import java.lang.Exception;
8 import java.util.Vector;
9 import java.util.Random;
19 * Moves a point in a direction, returns new point
22 * @param direction Indicates direction. Must be "LEFT", "RIGHT", "UP", "DOWN"
23 * @param multiplier Spaces to move
24 * @returns An array of length 2, containing the new x and y coords
25 * @throws Exception on unrecognised direction
27 public static int[] Move(int x, int y, String direction, int multiplier) throws Exception
29 //NOTE: The board is indexed so that the top left corner is x = 0, y = 0
30 // Does not check that coordiantes would be valid in the board.
32 if (direction.compareTo("DOWN") == 0)
33 y += multiplier; //Moving down increases y
34 else if (direction.compareTo("UP") == 0)
35 y -= multiplier; //Moving up decreases y
36 else if (direction.compareTo("LEFT") == 0)
37 x -= multiplier; //Moving left decreases x
38 else if (direction.compareTo("RIGHT") == 0)
42 throw new Exception("BasicAI.Move - Unrecognised direction " + direction);
45 int result[] = new int[2];
46 result[0] = x; result[1] = y;
51 * Returns the "opposite" colour to that given
52 * @param colour Must be "RED" or "BLUE"
53 * @returns The alternate String to colour
54 * @throws Exception if colour is not "RED" or "BLUE"
56 public static String OppositeColour(String colour) throws Exception
58 if (colour.compareTo("BLUE") == 0)
60 else if (colour.compareTo("RED") == 0)
63 throw new Exception("BasicAI.OppositeColour - Unrecognised colour " + colour);
67 * Tests if a value is an integer
68 * I cry at using exceptions for this
70 public static boolean IsInteger(String str)
74 Integer.parseInt(str);
76 catch (NumberFormatException e)
83 private int turn; //The turn number of the game
84 private Piece board[][]; //The board
85 private Vector<Piece> units; //All units
86 private Vector<Piece> enemyUnits; //All enemy units
87 private Piece lastMoved; //Last moved piece
88 private String colour; //Colour of the AI
89 private String opponentName; //Name of the AI's opponent
90 private int width; //Width of the board (NOTE: Should always be 10)
91 private int height; //Height of the board (NOTE: Should always be 10)
93 private static int totalAllies[] = {6,1,1,2,3,4,4,4,5,8,1,1}; //Numbers of allies, B -> F
94 private static int totalEnemies[] = {6,1,1,2,3,4,4,4,5,8,1,1}; //Numbers of enemies, B -> F
95 private static int hiddenEnemies[] = {6,1,1,2,3,4,4,4,5,8,1,1}; //Number of hidden enemies of each type
96 private static int hiddenAllies[] = {6,1,1,2,3,4,4,4,5,8,1,1}; //Number of hidden allies of each type
98 private static String directions[] = {"UP", "DOWN", "LEFT", "RIGHT"}; //All available directions
100 private static Random rand = new Random(); //A random number generator
104 * Sets up a board, prepares to play
110 units = new Vector<Piece>();
111 enemyUnits = new Vector<Piece>();
117 //HACK to get rid of stupid Javac warnings
118 if (lastMoved == null && opponentName == null);
122 * Implements Setup phase of protocol described in manager program man page
123 * Always uses the same setup. Override to create custom setups.
125 public void Setup() throws Exception
127 Vector<String> setup = Reader.readTokens(); //Wierd java way of doing input from stdin, see Reader.java
128 if (setup.size() != 4)
130 throw new Exception("BasicAI.Setup - Expected 4 tokens, got " + setup.size());
132 colour = setup.elementAt(0);
133 opponentName = setup.elementAt(1);
134 width = Integer.parseInt(setup.elementAt(2));
135 height = Integer.parseInt(setup.elementAt(3));
137 if (width != 10 || height != 10)
138 throw new Exception("BasicAI.Setup - Expected width and height of 10, got " + width + " and " + height);
140 board = new Piece[width][height];
141 for (int x=0; x < board.length; ++x)
143 for (int y = 0; y < board[x].length; ++y)
147 //TODO: Modify this setup
148 if (colour.compareTo("RED") == 0)
149 System.out.println("FB8sB479B8\nBB31555583\n6724898974\n967B669999");
150 else if (colour.compareTo("BLUE") == 0)
151 System.out.println("967B669999\n6724898974\nBB31555583\nFB8sB479B8");
153 throw new Exception("BasicAI.Setup - Unrecognised colour of " + colour);
160 public void MoveCycle() throws Exception
170 * TODO: Rewrite move algorithm (currently uses random)
172 public void MakeMove() throws Exception
174 if (units.size() <= 0)
175 throw new Exception("BasicAI.MakeMove - No units left!");
177 int index = rand.nextInt(units.size() - 1); //Pick index of unit to move
178 int startIndex = index; //Remember it
180 while (true) //Don't worry, there is a break
182 Piece piece = units.elementAt(index);
184 throw new Exception("BasicAI.MakeMove - null unit ???");
188 int dirIndex = rand.nextInt(directions.length); //Pick a random direction index
189 int startDirIndex = dirIndex; //Remember
192 int p[] = Move(piece.x, piece.y, directions[dirIndex], 1);
193 if (p[0] >= 0 && p[0] < width && p[1] >= 0 && p[1] < height)
195 Piece target = board[p[0]][p[1]];
196 if (target == null || (target.colour != piece.colour && target.colour != "NONE" && target.colour != "BOTH"))
198 System.out.println(""+piece.x + " " + piece.y + " " + directions[dirIndex]);
203 dirIndex = (dirIndex + 1) % directions.length;
204 if (dirIndex == startDirIndex)
208 index = (index + 1) % units.size();
209 if (index == startIndex)
211 System.out.println("NO_MOVE");
221 public void ReadBoard() throws Exception
223 for (int y = 0; y < height; ++y)
225 String row = Reader.readLine();
226 if (row.length() != width)
227 throw new Exception("BasicAI.ReadBoard - Row " + y + " has width " + row.length() + " instead of " + width);
228 for (int x = 0; x < width; ++x)
232 switch (row.charAt(x))
237 board[x][y] = new Piece(OppositeColour(colour), '?', x, y);
238 enemyUnits.add(board[x][y]);
241 board[x][y] = new Piece("NONE", '+', x, y);
244 board[x][y] = new Piece(colour, row.charAt(x), x, y);
245 units.add(board[x][y]);
255 * Removes a unit from the game
257 private void KillUnit(Piece kill) throws Exception
259 Vector<Piece> removeFrom = null;
260 if (kill.colour.compareTo(colour) == 0)
262 totalAllies[Piece.Index(kill.rank)] -= 1;
265 else if (kill.colour.compareTo(OppositeColour(colour)) == 0)
267 totalEnemies[Piece.Index(kill.rank)] -= 1;
268 removeFrom = enemyUnits;
270 if (removeFrom == null)
271 throw new Exception("BasicAI.KillUnit - Can't identify unit with colour " + kill.colour + "!");
273 for (int ii=0; ii < removeFrom.size(); ++ii)
275 if (removeFrom.elementAt(ii) == kill)
277 removeFrom.remove(ii);
281 throw new Exception("BasicAI.KillUnit - Couldn't find unit in unit list.");
285 * Interprets the result of a move, updates all relevant variables
287 public void InterpretResult() throws Exception
289 Vector<String> result = Reader.readTokens();
292 if (result.elementAt(0).compareTo("QUIT") == 0)
294 if (result.elementAt(0).compareTo("NO_MOVE") == 0)
297 if (result.size() < 4)
299 throw new Exception("BasicAI.InterpretResult - Expect at least 4 tokens, got " + result.size());
302 int x = Integer.parseInt(result.elementAt(0));
303 int y = Integer.parseInt(result.elementAt(1));
304 String direction = result.elementAt(2);
307 String outcome = result.elementAt(3);
309 if (IsInteger(outcome))
311 multiplier = Integer.parseInt(outcome);
312 outcome = result.elementAt(4);
315 int p[] = Move(x,y,direction, multiplier);
317 Piece attacker = board[x][y];
319 if (attacker == null)
320 throw new Exception("BasicAI.InterpretResult - Couldn't find a piece to move at (" + x +"," + y+")");
322 lastMoved = attacker;
324 Piece defender = board[p[0]][p[1]];
327 attacker.x = p[0]; attacker.y = p[1];
328 attacker.positions.add(0, p);
330 if (result.size() >= outIndex + 3)
332 if (defender == null)
333 throw new Exception("BasicAI.InterpretResult - Result suggests a defender at ("+p[0]+","+p[1]+"), but none found");
334 attacker.rank = result.elementAt(outIndex+1).charAt(0); //ranks are 1 char long
335 if (attacker.beenRevealed == false)
337 if (attacker.colour.compareTo(colour) == 0)
338 hiddenAllies[Piece.Index(attacker.rank)] -= 1;
339 else if (attacker.colour.compareTo(OppositeColour(colour)) == 0)
340 hiddenEnemies[Piece.Index(attacker.rank)] -= 1;
342 throw new Exception("BasicAI.InterpretResult - Colour " + attacker.colour + " for moving piece makes no sense.");
344 attacker.beenRevealed = true;
345 defender.rank = result.elementAt(outIndex+2).charAt(0); //ranks are 1 char long
346 if (defender.beenRevealed == false)
348 if (defender.colour.compareTo(colour) == 0)
349 hiddenAllies[Piece.Index(defender.rank)] -= 1;
350 else if (attacker.colour.compareTo(OppositeColour(colour)) == 0)
351 hiddenEnemies[Piece.Index(defender.rank)] -= 1;
353 throw new Exception("BasicAI.InterpretResult - Colour " + attacker.colour + " for defending piece makes no sense.");
355 defender.beenRevealed = true;
358 if (outcome.compareTo("OK") == 0)
359 board[p[0]][p[1]] = attacker;
360 else if (outcome.compareTo("KILLS") == 0)
362 board[p[0]][p[1]] = attacker;
365 else if (outcome.compareTo("DIES") == 0)
369 else if (outcome.compareTo("BOTHDIE") == 0)
371 board[p[0]][p[1]] = null;
377 System.exit(0); //Game over
386 public static void main(String [] args)
390 BasicAI theAI = new BasicAI();
397 System.out.println("EXCEPTION: " + e.getMessage());