/* * Nathaniel Lim * 4/21/08 * Darwin 2.0 Part 1 Lab * CS136 Williams College * njl2@williams.edu */ import java.util.Random; import java.util.Stack; import java.util.concurrent.atomic.AtomicInteger; import java.awt.Point; import java.awt.Dimension; import java.util.ArrayList; import java.lang.Boolean; public class SmartCreature extends Creature { //Static Variables shared by all instances of SmartCreature public static ObservationArray2D map; public static AtomicInteger numAlive = new AtomicInteger(0); public static Boolean hasMadeMap = new Boolean(false); public Stack<Point> lastVisited = new Stack<Point>(); public String getAuthorName() { return "Nathaniel Lim"; } public String getDescription() { return " SmartCreature have a global understanding of the playing " + "field by looking around and recording Observations of items " + "in a static map variable. There are issues concerning the synchronizing " + "of the operations to the map, so that operations in one thread are not" + "interrupted by other threads."; } public void run(){ synchronized(hasMadeMap){ if (map == null) { map = new ObservationArray2D(getMapDimensions(), getClass().getName()); } } update(); numAlive.getAndAdd(1); try{ while (true){ //Implement a strategy using SmartCreature framework strategy(); } } catch (ConvertedError e){ //Conversion occured synchronized(map){ map.set(getPosition(), new Observation(getPosition(), "ENEMY", 0, Direction.EAST, getTime())); } } finally{ //Regardless of how the creature died/stopped running //The population must be atomically decremented. numAlive.getAndAdd(-1); } } /* * Override this method for extending SmartCreature * making it utilize its brain power. * * This simple strategy does not utilize the global * understanding of the map */ private void strategy() { Observation o = look(); //Turns around from thorns or Flytraps //Attack if its an Apple, Enemy, if (o.type == Type.CREATURE && !o.className.equals(getClass().getName())){ if (!o.className.equals("Flytrap")) { int d = this.distance(o.position); if (d == 1){ attack(); } else { if (moveForward(d-1)){ attack(); } else { attack(); turn180(); } } } } if(o.type == Type.THORN){ turn180(); } Random r = new Random(); int i = r.nextInt(2); if (i == 0){ if (! moveForward()){ turn90Random(); } } else { turn90Random(); } } public void turn180(){ turnRight(); turnRight(); } //Does nothing if this is facing d already public void turn(Direction d){ Direction current = getDirection(); if(current.opposite() == d){ turn180(); } else if (current.left() == d){ turnLeft(); } else if( current.right() == d){ turnRight(); } } public void turn90Random(){ Random r = new Random(); int x = r.nextInt(2);// 0 or 1 if(x == 0){ turnLeft(); } else { turnRight(); } } /* * Returns false if it didn't move n spaces * True if it was unimpeded. * If it was impeded, it stops trying to move * forward. */ public boolean moveForward(int n){ boolean moved = false; for (int i = 0; i < n; i++) { moved = moveForward(); if (!moved){ break; } } return moved; } /* * Figures out which axis[ (N-S) or (E-W) ] has the greatest * distance to be traveled on to get closer to p, and \ * determines which way to point towards p. */ public Direction directionTo(Point p){ Point here = getPosition(); int x2 = p.x; int y2 = p.y; int x1 = here.x; int y1 = here.y; int dx = x2-x1; int dy = x2-x1; if (Math.abs(dx) > Math.abs(dy)){ if (dx > 0){ return Direction.EAST; } else { return Direction.WEST; } } else { if (dy > 0){ return Direction.SOUTH; } else { return Direction.NORTH; } } } //Turn to the direction that will //get SmartCreature to p. public void turn(Point p) { turn(directionTo(p)); } public int getPopulation(){ return numAlive.intValue(); } public void update(){ // System.out.println("Num Alive: " + numAlive.intValue()); Point here = getPosition(); Point last = null; if (!lastVisited.isEmpty()){ last = lastVisited.peek(); } if (!here.equals(last)){ //Add the current position to the Stack of last visited points lastVisited.push(here); //This insures that the multiple Runnable SmartCreatures on the map //do not try to update their understanding of the map at the same time. //This prevents problems with writing to the same spot at the same time. synchronized(map){ //Set the spot that the creature moved out of to be empty if (last!= null){ map.set(new Observation(last, Type.EMPTY, getTime())); } //Set the spot the creature is currently on to an Observation of itself //System.out.println("Width: " + map.map.size() + " Height: " + map.map.get(0).size()); map.set(here, new Observation(here, map.myName, getId(), getDirection(), getTime())); } } //If the creature didn't move, there is no new information, no update needed. // System.out.println(this); } /* * These moving methods are overridden to * update the understanding of the map after * each move; */ public boolean moveForward(){ boolean b = super.moveForward(); update(); return b; } public void turnLeft(){ super.turnLeft(); update(); } public void turnRight(){ super.turnRight(); update(); } /* v * Returns the global understanding of the map * Static var: map * (Should be the same for all instances) */ public String toString(){ //change this String out = "" + numAlive.intValue(); synchronized(map){ return out +="\n"+ map.toString(); } } public synchronized Observation recall(Point p){ Observation o = map.get(p); return o; } protected synchronized Observation look() { //Although no other instance of SmartCreature can look //while another is looking, one SmartCreature could be //looking while another is updating, potentially doing //operations on the map at the same time, leaving the threat //of interruption. Therefore, within this method, map operations //must be synchronized. Observation o = super.look(); synchronized(map){ map.set(o); } //The spaces between the SmartCreature instance and the position //of the observed object are all empty. And these "observations" //occurred at the time time as o int space = 1; while (!(this.getMovePosition(space).equals(o.position))){ Point spotOn = this.getMovePosition(space); synchronized(map){ map.set(spotOn, new Observation(spotOn, Type.EMPTY, o.time)); } space++; } return o; } //General Helper Class for a 2D Map protected static class Array2D<T>{ ArrayList<ArrayList<T>> map; public Array2D(Dimension d){ map = new ArrayList<ArrayList<T>>(); for (int i = 0; i < d.height; i ++) { ArrayList<T> row = new ArrayList<T>(); for (int j = 0; j < d.width; j++){ row.add(null); } map.add(row); } } public T get(Point p){ return get(p.x, p.y); } public T get(int x, int y){ return (map.get(y)).get(x); } public void set(int x, int y, T v){ (map.get(y)).set(x, v); } public void set (Point p, T v){ set(p.x, p.y, v); } public int getWidth(){ return (map.get(0)).size(); } public int getHeight(){ return map.size(); } public boolean inBounds(Point p){ return inBounds(p.x, p.y); } public boolean inBounds(int x, int y) { return (x < getWidth() && x >= 0) && (y < getHeight() && y >=0); } public String toString(){ String out = ""; for (ArrayList<T> row : map){ for (T item: row){ out+= toString(item); } out+= "\n"; } return out; } protected String toString (T t) { return t.toString(); } } //Helper Class: Extension of Array2D, for Observations protected static class ObservationArray2D extends Array2D<Observation>{ private String myName; public ObservationArray2D(Dimension d, String myClassName){ super(d); myName = myClassName; } public void set(Observation obs){ Point p = obs.position; set(p, obs); } protected String toString(Observation obs) { if (obs == null){ return "?"; } if (obs.type == Type.EMPTY){ return " " ; } else if (obs.type == Type.WALL){ return "X"; } else if (obs.type == Type.CREATURE){ String ident = obs.className; if (ident.equals( myName)){ return "m"; } else if (ident.equals( "Apple")){ return "a"; } else if (ident.equals("Flytrap")){ return "f"; } else { return "c"; } } else { return "?"; } } } }