Courses/CS 461/Winter 2006/Jeff Bailey/Homework 3
From CSWiki
Contents |
[edit] ZOMG! Zombies!!
Keeping with my desire to play with Repast, I wrote another simple model. Inspired by the incredibly addictive game, Urban Dead, and the Processing model which preceded it, I thought I would attempt to create a similar model in Repast.
BRAAAAINS!
The only portion of this simulation that was moderately interesting was coming up with an algorithm for creating the city. It turned out to be rather simple and most of my initial frustrations were the result of overthinking the solution. I did learn a few things about the repast spaces and how they differ.
I spent a lot of time trying to understand Repast Schedules without much luck. Schedules would allow me to give each agent unique schedules, facilitating certain features that were otherwise left out of this simulation.
Things such as:
- Different movement speeds (Currently everyone moves at the speed).
- Ability for humans to flee or attack a zombie within a specified range.
- Transition from NORMAL to INFECTED has intermediate DEAD phase that lasts some number of ticks.
[edit] Before
[edit] After
[edit] Source
Note: Once again, I have formatted this thing to be best displayed on the wiki. Comments have been stripped and lines compressed.
[edit] Agent
Abstract parent class for all agents in my model.
package com.stannum.repast.zombies;
import uchicago.src.sim.gui.*;
public abstract class Agent implements Drawable {
private int x;
private int y;
public Agent(int x, int y) {
this.x = x;
this.y = y;
}
public int getY() { return y; }
public void setY(int y) { this.y = y; }
public int getX() { return x; }
public void setX(int x) { this.x = x; }
}
[edit] Block
Represents an immovable/impassable location on the grid. A rectangle of n x m blocks is meant to simulate a building or other obstruction.
package com.stannum.repast.zombies;
import java.awt.*;
import uchicago.src.sim.gui.*;
public class Block extends Agent {
public Block(int x, int y) {
super(x, y);
}
public void draw(SimGraphics graphics) {
graphics.drawFastRect(Color.GRAY);
}
}
[edit] Person
The core of this simulation. Persons are either Normal or Zombies. Not sure whether I would polymorph this into multiple classes should this simulation be expanded or not. Again, the design of agents seems to be very important but I have not come up with a best practice as of yet.
package com.stannum.repast.zombies;
import java.awt.*;
import uchicago.src.sim.gui.*;
public class Person extends Agent {
public static final int NORMAL = 1;
public static final int PANIC = 2;
public static final int INFECTED = 3;
private int status;
public Person(int x, int y) {
this(x, y, NORMAL);
}
public Person(int x, int y, int status) {
super(x, y);
this.status = status;
}
public int getStatus() { return status; }
public void setStatus(int status) { this.status = status; }
public void draw(SimGraphics graphics) {
if (status == NORMAL) graphics.drawFastCircle(Color.LIGHT_GRAY);
else if (status == PANIC) graphics.drawFastCircle(Color.WHITE);
else if (status == INFECTED) graphics.drawFastCircle(Color.GREEN);
}
}
[edit] ZombieModel
The meat and potatoes of this simulation.
package com.stannum.repast.zombies;
import java.util.*;
import uchicago.src.sim.analysis.*;
import uchicago.src.sim.engine.*;
import uchicago.src.sim.gui.*;
import uchicago.src.sim.space.*;
import uchicago.src.sim.util.Random;
public class ZombieModel extends SimpleModel {
private int SIZE = 100;
private DisplaySurface surface;
private Object2DGrid space;
private OpenSequenceGraph plot;
private int buildings = 20;
private int people = 1000;
private int zombies = 1;
public void setup() {
super.setup();
if (surface != null) surface.dispose();
surface = new DisplaySurface(this, "surface");
if (plot != null) plot.dispose();
plot = new OpenSequenceGraph("plot", this);
plot.setYRange(0, (people + zombies + 1));
plot.setAxisTitles("time", "count");
plot.addSequence("people", new Sequence() {
public double getSValue() {
int count = 0;
for (int i=0; i<agentList.size(); i++) {
if (agentList.get(i) instanceof Person && ((Person) agentList.get(i)).getStatus() == Person.NORMAL) {
count++;
}
}
return count;
}
});
plot.addSequence("zombies", new Sequence() {
public double getSValue() {
int count = 0;
for (int i=0; i<agentList.size(); i++) {
if (agentList.get(i) instanceof Person && ((Person) agentList.get(i)).getStatus() == Person.INFECTED) {
count++;
}
}
return count;
}
});
}
public void buildModel() {
space = new Object2DGrid(SIZE, SIZE);
surface.addDisplayable(new Object2DDisplay(space), "display");
constructBuildings(buildings);
populate(people);
zombify(zombies);
surface.display();
plot.display();
}
protected void step() {
Iterator iterator = agentList.iterator();
while (iterator.hasNext()) {
Agent agent = (Agent) iterator.next();
if (agent instanceof Person) {
Person person = (Person) agent;
move(person);
if (person.getStatus() == Person.INFECTED) {
infect((Person) agent);
}
}
}
surface.updateDisplay();
plot.step();
plot.updateGraph();
}
public static void main(String[] args) {
SimInit init = new SimInit();
init.loadModel(new ZombieModel(), null, false);
}
private void constructBuildings(int count) {
Random.createUniform();
for (int i=0; i<count; i++) {
int x = Random.uniform.nextIntFromTo(0, SIZE);
int y = Random.uniform.nextIntFromTo(0, SIZE);
int width = Random.uniform.nextIntFromTo(Math.round(.10f * SIZE), Math.round(.20f * SIZE));
int height = Random.uniform.nextIntFromTo(Math.round(.10f * SIZE), Math.round(.20f * SIZE));
for (int j=x; j<Math.min(x + width, SIZE); j++) {
for (int k=y; k<Math.min(y + height, SIZE); k++) {
Block block = new Block(j, k);
space.putObjectAt(j, k, block);
agentList.add(block);
}
}
}
}
private void populate(int count) {
Random.createUniform();
int current = 0;
while (current < count) {
int x = Random.uniform.nextIntFromTo(0, SIZE-1);
int y = Random.uniform.nextIntFromTo(0, SIZE-1);
if (space.getObjectAt(x, y) == null) {
Person person = new Person(x, y);
space.putObjectAt(x, y, person);
agentList.add(person);
current++;
}
}
}
private void zombify(int count) {
Random.createUniform();
int current = 0;
while (current < count) {
int x = Random.uniform.nextIntFromTo(0, SIZE-1);
int y = Random.uniform.nextIntFromTo(0, SIZE-1);
if (space.getObjectAt(x, y) == null) {
Person person = new Person(x, y, Person.INFECTED);
space.putObjectAt(x, y, person);
agentList.add(person);
current++;
}
}
}
private void move(Person person) {
Random.createUniform();
int x = person.getX();
int y = person.getY();
int direction = Random.uniform.nextIntFromTo(0, 7);
switch (direction) {
case 0 : y--; break; // N
case 1 : x++; y--; break; // NE
case 2 : x++; break; // E
case 3 : x++; y++; break; // SE
case 4 : y++; break; // S
case 5 : x--; y++; break; // SW
case 6 : x--; break; // W
case 7 : x--; y--; break; // NW
}
if ((x >= 0) && (x < SIZE) && (y >= 0) && (y < SIZE) && (space.getObjectAt(x, y) == null)) {
space.putObjectAt(person.getX(), person.getY(), null);
person.setX(x);
person.setY(y);
space.putObjectAt(person.getX(), person.getY(), person);
}
}
private void infect(Person zombie) {
Vector vector = space.getMooreNeighbors(zombie.getX(), zombie.getY(), false);
Iterator iterator = vector.iterator();
while (iterator.hasNext()) {
Agent agent = (Agent) iterator.next();
if (agent instanceof Person && ((Person) agent).getStatus() != Person.INFECTED) {
((Person) agent).setStatus(Person.INFECTED);
}
}
}
}



