/* Copyright 2006 by Sean Luke and George Mason University Licensed under the Academic Free License version 3.0 See the file "LICENSE" for more information */ package sim.app.antsforage; import sim.field.grid.*; import sim.portrayal.*; import sim.portrayal.simple.*; import sim.util.*; import sim.engine.*; import java.awt.*; public class Ant extends OvalPortrayal2D implements Steppable { private static final long serialVersionUID = 1; public boolean getHasFoodItem() { return hasFoodItem; } public void setHasFoodItem(boolean val) { hasFoodItem = val; } public boolean hasFoodItem = false; double reward = 0; int x; int y; Int2D last; public Ant(double initialReward) { reward = initialReward; } // at present we have only one algorithm: value iteration. I might // revise this and add our alternate (TD) algorithm. See the papers. public void depositPheromone( final SimState state) { final AntsForage af = (AntsForage)state; Int2D location = af.buggrid.getObjectLocation(this); int x = location.x; int y = location.y; if (AntsForage.ALGORITHM == AntsForage.ALGORITHM_VALUE_ITERATION) { // test all around if (hasFoodItem) // deposit food pheromone { double max = af.toFoodGrid.field[x][y]; for(int dx = -1; dx < 2; dx++) for(int dy = -1; dy < 2; dy++) { int _x = dx+x; int _y = dy+y; if (_x < 0 || _y < 0 || _x >= AntsForage.GRID_WIDTH || _y >= AntsForage.GRID_HEIGHT) continue; // nothing to see here double m = af.toFoodGrid.field[_x][_y] * (dx * dy != 0 ? // diagonal corners af.diagonalCutDown : af.updateCutDown) + reward; if (m > max) max = m; } af.toFoodGrid.field[x][y] = max; } else { double max = af.toHomeGrid.field[x][y]; for(int dx = -1; dx < 2; dx++) for(int dy = -1; dy < 2; dy++) { int _x = dx+x; int _y = dy+y; if (_x < 0 || _y < 0 || _x >= AntsForage.GRID_WIDTH || _y >= AntsForage.GRID_HEIGHT) continue; // nothing to see here double m = af.toHomeGrid.field[_x][_y] * (dx * dy != 0 ? // diagonal corners af.diagonalCutDown : af.updateCutDown) + reward; if (m > max) max = m; } af.toHomeGrid.field[x][y] = max; } } reward = 0.0; } public void act( final SimState state ) { final AntsForage af = (AntsForage)state; Int2D location = af.buggrid.getObjectLocation(this); int x = location.x; int y = location.y; if (hasFoodItem) // follow home pheromone { double max = AntsForage.IMPOSSIBLY_BAD_PHEROMONE; int max_x = x; int max_y = y; int count = 2; for(int dx = -1; dx < 2; dx++) for(int dy = -1; dy < 2; dy++) { int _x = dx+x; int _y = dy+y; if ((dx == 0 && dy == 0) || _x < 0 || _y < 0 || _x >= AntsForage.GRID_WIDTH || _y >= AntsForage.GRID_HEIGHT || af.obstacles.field[_x][_y] == 1) continue; // nothing to see here double m = af.toHomeGrid.field[_x][_y]; if (m > max) { count = 2; } // no else, yes m > max is repeated if (m > max || (m == max && state.random.nextBoolean(1.0 / count++))) // this little magic makes all "==" situations equally likely { max = m; max_x = _x; max_y = _y; } } if (max == 0 && last != null) // nowhere to go! Maybe go straight { if (state.random.nextBoolean(af.momentumProbability)) { int xm = x + (x - last.x); int ym = y + (y - last.y); if (xm >= 0 && xm < AntsForage.GRID_WIDTH && ym >= 0 && ym < AntsForage.GRID_HEIGHT && af.obstacles.field[xm][ym] == 0) { max_x = xm; max_y = ym; } } } else if (state.random.nextBoolean(af.randomActionProbability)) // Maybe go randomly { int xd = (state.random.nextInt(3) - 1); int yd = (state.random.nextInt(3) - 1); int xm = x + xd; int ym = y + yd; if (!(xd == 0 && yd == 0) && xm >= 0 && xm < AntsForage.GRID_WIDTH && ym >= 0 && ym < AntsForage.GRID_HEIGHT && af.obstacles.field[xm][ym] == 0) { max_x = xm; max_y = ym; } } af.buggrid.setObjectLocation(this, new Int2D(max_x, max_y)); if (af.sites.field[max_x][max_y] == AntsForage.HOME) // reward me next time! And change my status { reward = af.reward ; hasFoodItem = ! hasFoodItem; } } else { double max = AntsForage.IMPOSSIBLY_BAD_PHEROMONE; int max_x = x; int max_y = y; int count = 2; for(int dx = -1; dx < 2; dx++) for(int dy = -1; dy < 2; dy++) { int _x = dx+x; int _y = dy+y; if ((dx == 0 && dy == 0) || _x < 0 || _y < 0 || _x >= AntsForage.GRID_WIDTH || _y >= AntsForage.GRID_HEIGHT || af.obstacles.field[_x][_y] == 1) continue; // nothing to see here double m = af.toFoodGrid.field[_x][_y]; if (m > max) { count = 2; } // no else, yes m > max is repeated if (m > max || (m == max && state.random.nextBoolean(1.0 / count++))) // this little magic makes all "==" situations equally likely { max = m; max_x = _x; max_y = _y; } } if (max == 0 && last != null) // nowhere to go! Maybe go straight { if (state.random.nextBoolean(af.momentumProbability)) { int xm = x + (x - last.x); int ym = y + (y - last.y); if (xm >= 0 && xm < AntsForage.GRID_WIDTH && ym >= 0 && ym < AntsForage.GRID_HEIGHT && af.obstacles.field[xm][ym] == 0) { max_x = xm; max_y = ym; } } } else if (state.random.nextBoolean(af.randomActionProbability)) // Maybe go randomly { int xd = (state.random.nextInt(3) - 1); int yd = (state.random.nextInt(3) - 1); int xm = x + xd; int ym = y + yd; if (!(xd == 0 && yd == 0) && xm >= 0 && xm < AntsForage.GRID_WIDTH && ym >= 0 && ym < AntsForage.GRID_HEIGHT && af.obstacles.field[xm][ym] == 0) { max_x = xm; max_y = ym; } } af.buggrid.setObjectLocation(this, new Int2D(max_x, max_y)); if (af.sites.field[max_x][max_y] == AntsForage.FOOD) // reward me next time! And change my status { reward = af.reward; hasFoodItem = ! hasFoodItem; } } last = location; } public void step( final SimState state ) { depositPheromone(state); act(state); } // a few tweaks by Sean private Color noFoodColor = Color.black; private Color foodColor = Color.red; public final void draw(Object object, Graphics2D graphics, DrawInfo2D info) { if( hasFoodItem ) graphics.setColor( foodColor ); else graphics.setColor( noFoodColor ); // this code was stolen from OvalPortrayal2D int x = (int)(info.draw.x - info.draw.width / 2.0); int y = (int)(info.draw.y - info.draw.height / 2.0); int width = (int)(info.draw.width); int height = (int)(info.draw.height); graphics.fillOval(x,y,width, height); } }