From 8827bb58a733a2adf3e7fe6e174d12bd5c1a0846 Mon Sep 17 00:00:00 2001 From: Sam Moore Date: Thu, 15 Mar 2012 01:15:09 +0800 Subject: [PATCH] Added Java sample AI Just makes random moves Can't work out how to unbuffer stdio yet. The java interpreter doesn't like absolute path names, which is annoying. --- agents/basic_java/Makefile | 13 + agents/basic_java/Reader.java | 48 ++++ agents/basic_java/basic.java | 390 ++++++++++++++++++++++++++++++ agents/basic_java/basic_java.hack | 2 + agents/basic_java/piece.java | 81 +++++++ 5 files changed, 534 insertions(+) create mode 100644 agents/basic_java/Makefile create mode 100644 agents/basic_java/Reader.java create mode 100644 agents/basic_java/basic.java create mode 100755 agents/basic_java/basic_java.hack create mode 100644 agents/basic_java/piece.java diff --git a/agents/basic_java/Makefile b/agents/basic_java/Makefile new file mode 100644 index 0000000..b058cdf --- /dev/null +++ b/agents/basic_java/Makefile @@ -0,0 +1,13 @@ +#Makefile for basic_java +# Sample C++ Stratego AI +# UCC Programming Competition 2012 + +BasicAI.class : basic.java Reader.java piece.java + javac Reader.java + javac piece.java + javac basic.java + +clean : + rm *.class + + diff --git a/agents/basic_java/Reader.java b/agents/basic_java/Reader.java new file mode 100644 index 0000000..da51459 --- /dev/null +++ b/agents/basic_java/Reader.java @@ -0,0 +1,48 @@ +/** + * Needed to do reading from stdin in Java + * SERIOUSLY + * Stupid Java + * @author Some website somewhere + */ +import java.io.*; +import java.lang.Exception; +import java.util.Vector; + +class Reader +{ + public static String readLine() + { + String s = ""; + try + { + InputStreamReader converter = new InputStreamReader(System.in); + BufferedReader in = new BufferedReader(converter); + s = in.readLine(); + } + catch (Exception e) + { + System.out.println("EXCEPTION: Reader.readLine - "+e); + } + return s; + } + + public static Vector readTokens() + { + String r = readLine(); + String token = ""; + Vector result = new Vector(); + for (int ii=0; ii < r.length(); ++ii) + { + if (r.charAt(ii) == ' ' || r.charAt(ii) == '\n') + { + result.add(new String(token)); + //System.out.println("Token " + token); + token = ""; + } + else + token += r.charAt(ii); + } + result.add(new String(token)); + return result; + } +} diff --git a/agents/basic_java/basic.java b/agents/basic_java/basic.java new file mode 100644 index 0000000..242e6cb --- /dev/null +++ b/agents/basic_java/basic.java @@ -0,0 +1,390 @@ +/** + * Class to manage a Stratego playing AI in Java + * @author Sam Moore for the UCC::Progcomp 2012 + * @website http://progcomp.ucc.asn.au + */ + +import java.lang.Exception; +import java.util.Vector; +import java.util.Random; + + + +class BasicAI +{ + + + + /** + * Moves a point in a direction, returns new point + * @param x x coord + * @param y y coord + * @param direction Indicates direction. Must be "LEFT", "RIGHT", "UP", "DOWN" + * @param multiplier Spaces to move + * @returns An array of length 2, containing the new x and y coords + * @throws Exception on unrecognised direction + */ + public static int[] Move(int x, int y, String direction, int multiplier) throws Exception + { + //NOTE: The board is indexed so that the top left corner is x = 0, y = 0 + // Does not check that coordiantes would be valid in the board. + + if (direction.compareTo("DOWN") == 0) + y += multiplier; //Moving down increases y + else if (direction.compareTo("UP") == 0) + y -= multiplier; //Moving up decreases y + else if (direction.compareTo("LEFT") == 0) + x -= multiplier; //Moving left decreases x + else if (direction.compareTo("RIGHT") == 0) + x += multiplier; + else + { + throw new Exception("BasicAI.Move - Unrecognised direction " + direction); + } + + int result[] = new int[2]; + result[0] = x; result[1] = y; + return result; + } + + /** + * Returns the "opposite" colour to that given + * @param colour Must be "RED" or "BLUE" + * @returns The alternate String to colour + * @throws Exception if colour is not "RED" or "BLUE" + */ + public static String OppositeColour(String colour) throws Exception + { + if (colour.compareTo("BLUE") == 0) + return "RED"; + else if (colour.compareTo("RED") == 0) + return "BLUE"; + else + throw new Exception("BasicAI.OppositeColour - Unrecognised colour " + colour); + } + + /** + * Tests if a value is an integer + * I cry at using exceptions for this + */ + public static boolean IsInteger(String str) + { + try + { + Integer.parseInt(str); + } + catch (NumberFormatException e) + { + return false; + } + return true; + } + + private int turn; //The turn number of the game + private Piece board[][]; //The board + private Vector units; //All units + private Vector enemyUnits; //All enemy units + private Piece lastMoved; //Last moved piece + private String colour; //Colour of the AI + private String opponentName; //Name of the AI's opponent + private int width; //Width of the board (NOTE: Should always be 10) + private int height; //Height of the board (NOTE: Should always be 10) + + private static int totalAllies[] = {6,1,1,2,3,4,4,4,5,8,1,1}; //Numbers of allies, B -> F + private static int totalEnemies[] = {6,1,1,2,3,4,4,4,5,8,1,1}; //Numbers of enemies, B -> F + private static int hiddenEnemies[] = {6,1,1,2,3,4,4,4,5,8,1,1}; //Number of hidden enemies of each type + private static int hiddenAllies[] = {6,1,1,2,3,4,4,4,5,8,1,1}; //Number of hidden allies of each type + + private static String directions[] = {"UP", "DOWN", "LEFT", "RIGHT"}; //All available directions + + private static Random rand = new Random(); //A random number generator + + /** + * Constructor + * Sets up a board, prepares to play + */ + public BasicAI() + { + turn = 0; + board = null; + units = new Vector(); + enemyUnits = new Vector(); + + lastMoved = null; + colour = null; + opponentName = null; + + //HACK to get rid of stupid Javac warnings + if (lastMoved == null && opponentName == null); + } + + /** + * Implements Setup phase of protocol described in manager program man page + * Always uses the same setup. Override to create custom setups. + */ + public void Setup() throws Exception + { + Vector setup = Reader.readTokens(); //Wierd java way of doing input from stdin, see Reader.java + if (setup.size() != 4) + { + throw new Exception("BasicAI.Setup - Expected 4 tokens, got " + setup.size()); + } + colour = setup.elementAt(0); + opponentName = setup.elementAt(1); + width = Integer.parseInt(setup.elementAt(2)); + height = Integer.parseInt(setup.elementAt(3)); + + if (width != 10 || height != 10) + throw new Exception("BasicAI.Setup - Expected width and height of 10, got " + width + " and " + height); + + board = new Piece[width][height]; + for (int x=0; x < board.length; ++x) + { + for (int y = 0; y < board[x].length; ++y) + board[x][y] = null; + } + + //TODO: Modify this setup + if (colour.compareTo("RED") == 0) + System.out.println("FB8sB479B8\nBB31555583\n6724898974\n967B669999"); + else if (colour.compareTo("BLUE") == 0) + System.out.println("967B669999\n6724898974\nBB31555583\nFB8sB479B8"); + else + throw new Exception("BasicAI.Setup - Unrecognised colour of " + colour); + + } + + /** + * Cycles a move + */ + public void MoveCycle() throws Exception + { + InterpretResult(); + ReadBoard(); + MakeMove(); + InterpretResult(); + } + + /** + * Makes a move + * TODO: Rewrite move algorithm (currently uses random) + */ + public void MakeMove() throws Exception + { + if (units.size() <= 0) + throw new Exception("BasicAI.MakeMove - No units left!"); + + int index = rand.nextInt(units.size() - 1); //Pick index of unit to move + int startIndex = index; //Remember it + + while (true) //Don't worry, there is a break + { + Piece piece = units.elementAt(index); + if (piece == null) + throw new Exception("BasicAI.MakeMove - null unit ???"); + + if (piece.Mobile()) + { + int dirIndex = rand.nextInt(directions.length); //Pick a random direction index + int startDirIndex = dirIndex; //Remember + while (true) + { + int p[] = Move(piece.x, piece.y, directions[dirIndex], 1); + if (p[0] >= 0 && p[0] < width && p[1] >= 0 && p[1] < height) + { + Piece target = board[p[0]][p[1]]; + if (target == null || (target.colour != piece.colour && target.colour != "NONE" && target.colour != "BOTH")) + { + System.out.println(""+piece.x + " " + piece.y + " " + directions[dirIndex]); + return; + } + + } + dirIndex = (dirIndex + 1) % directions.length; + if (dirIndex == startDirIndex) + break; + } + } + index = (index + 1) % units.size(); + if (index == startIndex) + { + System.out.println("NO_MOVE"); + break; + } + } + + } + + /** + * Reads the board + */ + public void ReadBoard() throws Exception + { + for (int y = 0; y < height; ++y) + { + String row = Reader.readLine(); + if (row.length() != width) + throw new Exception("BasicAI.ReadBoard - Row " + y + " has width " + row.length() + " instead of " + width); + for (int x = 0; x < width; ++x) + { + if (turn == 0) + { + switch (row.charAt(x)) + { + case '.': + break; + case '#': + board[x][y] = new Piece(OppositeColour(colour), '?', x, y); + enemyUnits.add(board[x][y]); + break; + case '+': + board[x][y] = new Piece("NONE", '+', x, y); + break; + default: + board[x][y] = new Piece(colour, row.charAt(x), x, y); + units.add(board[x][y]); + break; + } + + } + } + } + } + + /** + * Removes a unit from the game + */ + private void KillUnit(Piece kill) throws Exception + { + if (kill.colour.compareTo(colour) == 0) + { + totalAllies[Piece.Index(kill.rank)] -= 1; + if (units.remove(kill) == false) + throw new Exception("BasicAI.KillUnit - Couldn't remove allied Piece from units Vector!"); + } + else if (kill.colour.compareTo(OppositeColour(colour)) == 0) + { + totalEnemies[Piece.Index(kill.rank)] -= 1; + if (enemyUnits.remove(kill) == false) + throw new Exception("BasicAI.KillUnit - Couldn't remove enemy Piece from enemyUnits Vector!"); + } + } + + /** + * Interprets the result of a move, updates all relevant variables + */ + public void InterpretResult() throws Exception + { + Vector result = Reader.readTokens(); + if (turn == 0) + return; + if (result.elementAt(0).compareTo("QUIT") == 0) + System.exit(0); + if (result.elementAt(0).compareTo("NO_MOVE") == 0) + return; + + if (result.size() < 4) + { + throw new Exception("BasicAI.InterpretResult - Expect at least 4 tokens, got " + result.size()); + } + + int x = Integer.parseInt(result.elementAt(0)); + int y = Integer.parseInt(result.elementAt(1)); + String direction = result.elementAt(2); + + int multiplier = 1; + String outcome = result.elementAt(3); + int outIndex = 3; + if (IsInteger(outcome)) + { + multiplier = Integer.parseInt(outcome); + outcome = result.elementAt(4); + outIndex = 4; + } + int p[] = Move(x,y,direction, multiplier); + + Piece attacker = board[x][y]; + board[x][y] = null; + if (attacker == null) + throw new Exception("BasicAI.InterpretResult - Couldn't find a piece to move at (" + x +"," + y+")"); + + lastMoved = attacker; + + Piece defender = board[p[0]][p[1]]; + + + attacker.x = p[0]; attacker.y = p[1]; + attacker.positions.add(0, p); + + if (result.size() >= outIndex + 3) + { + if (defender == null) + throw new Exception("BasicAI.InterpretResult - Result suggests a defender at ("+p[0]+","+p[1]+"), but none found"); + attacker.rank = result.elementAt(outIndex+1).charAt(0); //ranks are 1 char long + if (attacker.beenRevealed == false) + { + if (attacker.colour.compareTo(colour) == 0) + hiddenAllies[Piece.Index(attacker.rank)] -= 1; + else if (attacker.colour.compareTo(OppositeColour(colour)) == 0) + hiddenEnemies[Piece.Index(attacker.rank)] -= 1; + else + throw new Exception("BasicAI.InterpretResult - Colour " + attacker.colour + " for moving piece makes no sense."); + } + attacker.beenRevealed = true; + defender.rank = result.elementAt(outIndex+2).charAt(0); //ranks are 1 char long + if (defender.beenRevealed == false) + { + if (defender.colour.compareTo(colour) == 0) + hiddenAllies[Piece.Index(defender.rank)] -= 1; + else if (attacker.colour.compareTo(OppositeColour(colour)) == 0) + hiddenEnemies[Piece.Index(defender.rank)] -= 1; + else + throw new Exception("BasicAI.InterpretResult - Colour " + attacker.colour + " for defending piece makes no sense."); + } + defender.beenRevealed = true; + + } + if (outcome.compareTo("OK") == 0) + board[p[0]][p[1]] = attacker; + else if (outcome.compareTo("KILLS") == 0) + { + board[p[0]][p[1]] = attacker; + KillUnit(defender); + } + else if (outcome.compareTo("DIES") == 0) + { + KillUnit(attacker); + } + else if (outcome.compareTo("BOTHDIE") == 0) + { + board[p[0]][p[1]] = null; + KillUnit(attacker); + KillUnit(defender); + } + else + { + System.exit(0); //Game over + } + + + } + + /** + * The main function! + */ + public static void main(String [] args) + { + try + { + BasicAI theAI = new BasicAI(); + theAI.Setup(); + while (true) + theAI.MoveCycle(); + } + catch (Exception e) + { + System.out.println("EXCEPTION: " + e.getMessage()); + } + } + +} diff --git a/agents/basic_java/basic_java.hack b/agents/basic_java/basic_java.hack new file mode 100755 index 0000000..f148fbc --- /dev/null +++ b/agents/basic_java/basic_java.hack @@ -0,0 +1,2 @@ +#!/bin/bash +exec java BasicAI.class diff --git a/agents/basic_java/piece.java b/agents/basic_java/piece.java new file mode 100644 index 0000000..90db2ac --- /dev/null +++ b/agents/basic_java/piece.java @@ -0,0 +1,81 @@ +/** + * Class to represent a Piece + * @author Sam Moore + * @website http://progcomp.ucc.asn.au + */ + +import java.lang.Exception; +import java.util.Vector; + +class Piece +{ + //Normally in the Java Way (TM) you would have to make these private, and add "Getters" and "Setters" and all sorts of crap. + // Disclaimer: The author is not responsible for the repercussions of not following the Java Way (TM) + public int x; //x coord + public int y; //y coord + public char rank; //Rank of the piece + public String colour; //The colour of the Piece + public Vector positions; //All positions the piece has been at + public boolean beenRevealed; //True if the piece has been revealed in combat + + public static char ranks[] = {'B','1','2','3','4','5','6','7','8','9','s','F', '?', '+'}; //List of all the possible ranks + //'?' is an unknown piece, '+' is an obstacle + + /** + * Constructor + * @param c The new colour + * @param r The new rank + * @param xx The new x coord + * @param yy The new y coord + */ + public Piece(String c, char r, int xx, int yy) + { + + this.colour = c; + this.rank = r; + this.x = xx; + this.y = yy; + this.positions = new Vector(); + this.beenRevealed = false; + + positions.add(new int[2]); + positions.elementAt(0)[0] = x; + positions.elementAt(0)[1] = y; + } + + /** + * @returns True if the piece can move, based on its rank + */ + public boolean Mobile() + { + return (rank != 'F' && rank != 'B' && rank != '+' && rank != '?'); + } + + /** + * @returns The value of the piece's rank + */ + public int ValuedRank() + { + for (int ii=0; ii < ranks.length; ++ii) + { + if (ranks[ii] == rank) + return (ranks.length - 2 - ii); + } + return 0; + } + + /** + * @returns the index in ranks of a rank + * @throws Exception if the rank doesn't exist + */ + public static int Index(char rank) throws Exception + { + for (int ii=0; ii < ranks.length; ++ii) + { + if (ranks[ii] == rank) + return ii; + } + throw new Exception("Piece.Index - No such rank as " + rank); + + } +} -- 2.20.1