Updated graphics to for displaying on the clubroom projector
[progcomp2012.git] / agents / basic_java / basic.java
1 /**
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
5  */
6
7 import java.lang.Exception;
8 import java.util.Vector;
9 import java.util.Random;
10
11
12
13 class BasicAI
14 {
15
16
17         
18         /**
19          * Moves a point in a direction, returns new point
20          * @param x x coord
21          * @param y y coord
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
26          */
27         public static int[] Move(int x, int y, String direction, int multiplier) throws Exception
28         {
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.
31
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)
39                         x += multiplier;
40                 else
41                 {
42                         throw new Exception("BasicAI.Move - Unrecognised direction " + direction);
43                 }
44
45                 int result[] = new int[2];
46                 result[0] = x; result[1] = y;
47                 return result;
48         }
49
50         /**
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"
55          */
56         public static String OppositeColour(String colour) throws Exception
57         {
58                 if (colour.compareTo("BLUE") == 0)
59                         return "RED";
60                 else if (colour.compareTo("RED") == 0)
61                         return "BLUE";
62                 else
63                         throw new Exception("BasicAI.OppositeColour - Unrecognised colour " + colour);
64         }
65
66         /**
67          * Tests if a value is an integer
68          * I cry at using exceptions for this
69          */
70         public static boolean IsInteger(String str)
71         {
72                 try
73                 {
74                         Integer.parseInt(str);
75                 }
76                 catch (NumberFormatException e)
77                 {
78                         return false;
79                 }
80                 return true;
81         }
82
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)
92
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
97
98         private static String directions[] = {"UP", "DOWN", "LEFT", "RIGHT"}; //All available directions
99         
100         private static Random rand = new Random(); //A random number generator
101         
102         /**
103          * Constructor
104          * Sets up a board, prepares to play
105          */
106         public BasicAI()
107         {
108                 turn = 0;
109                 board = null;
110                 units = new Vector<Piece>();
111                 enemyUnits = new Vector<Piece>();
112
113                 lastMoved = null;
114                 colour = null;
115                 opponentName = null;
116
117                 //HACK to get rid of stupid Javac warnings
118                 if (lastMoved == null && opponentName == null);
119         }
120
121         /**
122          * Implements Setup phase of protocol described in manager program man page
123          * Always uses the same setup. Override to create custom setups.
124          */
125         public void Setup() throws Exception
126         {
127                 Vector<String> setup = Reader.readTokens(); //Wierd java way of doing input from stdin, see Reader.java
128                 if (setup.size() != 4)
129                 {
130                         throw new Exception("BasicAI.Setup - Expected 4 tokens, got " + setup.size());
131                 }       
132                 colour = setup.elementAt(0);
133                 opponentName = setup.elementAt(1);
134                 width = Integer.parseInt(setup.elementAt(2));
135                 height = Integer.parseInt(setup.elementAt(3));
136
137                 if (width != 10 || height != 10)
138                         throw new Exception("BasicAI.Setup - Expected width and height of 10, got " + width + " and " + height);
139
140                 board = new Piece[width][height];
141                 for (int x=0; x < board.length; ++x)
142                 {
143                         for (int y = 0; y < board[x].length; ++y)
144                                 board[x][y] = null;
145                 }
146
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");
152                 else
153                         throw new Exception("BasicAI.Setup - Unrecognised colour of " + colour);
154
155         }
156
157         /**
158          * Cycles a move
159          */
160         public void MoveCycle() throws Exception
161         {
162                 InterpretResult();
163                 ReadBoard();
164                 MakeMove();
165                 InterpretResult();
166         }
167
168         /**
169          * Makes a move
170          * TODO: Rewrite move algorithm (currently uses random)
171          */
172         public void MakeMove() throws Exception
173         {
174                 if (units.size() <= 0)
175                         throw new Exception("BasicAI.MakeMove - No units left!");
176
177                 int index = rand.nextInt(units.size() - 1); //Pick index of unit to move
178                 int startIndex = index; //Remember it
179                 
180                 while (true) //Don't worry, there is a break
181                 {
182                         Piece piece = units.elementAt(index);
183                         if (piece == null)
184                                 throw new Exception("BasicAI.MakeMove - null unit ???");
185         
186                         if (piece.Mobile())
187                         {
188                                 int dirIndex = rand.nextInt(directions.length); //Pick a random direction index
189                                 int startDirIndex = dirIndex; //Remember
190                                 while (true)
191                                 {
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)
194                                         {
195                                                 Piece target = board[p[0]][p[1]];
196                                                 if (target == null || (target.colour != piece.colour && target.colour != "NONE" && target.colour != "BOTH"))
197                                                 {
198                                                         System.out.println(""+piece.x + " " + piece.y + " " + directions[dirIndex]);
199                                                         return;
200                                                 }
201                                                 
202                                         }
203                                         dirIndex = (dirIndex + 1) % directions.length;
204                                         if (dirIndex == startDirIndex)
205                                                 break;
206                                 }
207                         }
208                         index = (index + 1) % units.size();
209                         if (index == startIndex)
210                         {
211                                 System.out.println("NO_MOVE");
212                                 break;
213                         }
214                 }
215                 
216         }
217         
218         /**
219          * Reads the board
220          */
221         public void ReadBoard() throws Exception
222         {
223                 for (int y = 0; y < height; ++y)
224                 {
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)
229                         {
230                                 if (turn == 0)
231                                 {
232                                         switch (row.charAt(x))
233                                         {
234                                                 case '.':
235                                                         break;
236                                                 case '#':
237                                                         board[x][y] = new Piece(OppositeColour(colour), '?', x, y);
238                                                         enemyUnits.add(board[x][y]);
239                                                         break;
240                                                 case '+':
241                                                         board[x][y] = new Piece("NONE", '+', x, y);
242                                                         break;
243                                                 default:
244                                                         board[x][y] = new Piece(colour, row.charAt(x), x, y);
245                                                         units.add(board[x][y]);
246                                                         break;  
247                                         }
248                                                 
249                                 }
250                         }
251                 }
252         }
253
254         /**
255          * Removes a unit from the game
256          */
257         private void KillUnit(Piece kill) throws Exception
258         {
259                 Vector<Piece> removeFrom = null;
260                 if (kill.colour.compareTo(colour) == 0)
261                 {
262                         totalAllies[Piece.Index(kill.rank)] -= 1;
263                         removeFrom = units;
264                 }
265                 else if (kill.colour.compareTo(OppositeColour(colour)) == 0)
266                 {
267                         totalEnemies[Piece.Index(kill.rank)] -= 1;
268                         removeFrom = enemyUnits;
269                 }
270                 if (removeFrom == null)
271                         throw new Exception("BasicAI.KillUnit - Can't identify unit with colour " + kill.colour + "!");
272
273                 for (int ii=0; ii < removeFrom.size(); ++ii)
274                 {
275                         if (removeFrom.elementAt(ii) == kill)
276                         {
277                                 removeFrom.remove(ii);
278                                 return;
279                         }                               
280                 }
281                 throw new Exception("BasicAI.KillUnit - Couldn't find unit in unit list.");
282         }
283
284         /**
285          * Interprets the result of a move, updates all relevant variables
286          */
287         public void InterpretResult() throws Exception
288         {
289                 Vector<String> result = Reader.readTokens();
290                 if (turn == 0)
291                         return;
292                 if (result.elementAt(0).compareTo("QUIT") == 0)
293                         System.exit(0);
294                 if (result.elementAt(0).compareTo("NO_MOVE") == 0)
295                         return;
296
297                 if (result.size() < 4)
298                 {
299                         throw new Exception("BasicAI.InterpretResult - Expect at least 4 tokens, got " + result.size());
300                 }
301
302                 int x = Integer.parseInt(result.elementAt(0));
303                 int y = Integer.parseInt(result.elementAt(1));
304                 String direction = result.elementAt(2);
305
306                 int multiplier = 1;
307                 String outcome = result.elementAt(3);
308                 int outIndex = 3;
309                 if (IsInteger(outcome))
310                 {
311                         multiplier = Integer.parseInt(outcome);
312                         outcome = result.elementAt(4);
313                         outIndex = 4;
314                 }
315                 int p[] = Move(x,y,direction, multiplier);
316
317                 Piece attacker = board[x][y];
318                 board[x][y] = null;
319                 if (attacker == null)
320                         throw new Exception("BasicAI.InterpretResult - Couldn't find a piece to move at (" + x +"," + y+")");
321
322                 lastMoved = attacker;
323
324                 Piece defender = board[p[0]][p[1]];
325                 
326
327                 attacker.x = p[0]; attacker.y = p[1];
328                 attacker.positions.add(0, p);
329
330                 if (result.size() >= outIndex + 3)
331                 {
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)
336                         {
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;
341                                 else
342                                         throw new Exception("BasicAI.InterpretResult - Colour " + attacker.colour + " for moving piece makes no sense.");
343                         }
344                         attacker.beenRevealed = true;
345                         defender.rank = result.elementAt(outIndex+2).charAt(0); //ranks are 1 char long
346                         if (defender.beenRevealed == false)
347                         {
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;
352                                 else
353                                         throw new Exception("BasicAI.InterpretResult - Colour " + attacker.colour + " for defending piece makes no sense.");
354                         }
355                         defender.beenRevealed = true;
356                         
357                 }
358                 if (outcome.compareTo("OK") == 0)
359                         board[p[0]][p[1]] = attacker;
360                 else if (outcome.compareTo("KILLS") == 0)
361                 {
362                         board[p[0]][p[1]] = attacker;
363                         KillUnit(defender);
364                 }
365                 else if (outcome.compareTo("DIES") == 0)
366                 {
367                         KillUnit(attacker);
368                 }
369                 else if (outcome.compareTo("BOTHDIE") == 0)
370                 {
371                         board[p[0]][p[1]] = null;
372                         KillUnit(attacker);
373                         KillUnit(defender);
374                 }
375                 else
376                 {
377                         System.exit(0); //Game over
378                 }
379                 
380
381         }
382
383         /**
384          * The main function!
385          */
386         public static void main(String [] args)
387         {
388                 try
389                 {
390                         BasicAI theAI = new BasicAI();
391                         theAI.Setup();
392                         while (true)
393                                 theAI.MoveCycle();
394                 }
395                 catch (Exception e)
396                 {
397                         System.out.println("EXCEPTION: " + e.getMessage());
398                 }
399         }
400
401 }

UCC git Repository :: git.ucc.asn.au