Courses/CS 461/Winter 2006/Jeff Bailey/Homework 4

From CSWiki

Jump to: navigation, search

Contents


[edit] Even More Zombies!!

After my initial experimentation with Repast and the Zombie model, I thought I would expand on it ever so slighty which resulted in a complete rewrite using a different strategy and a serious amount of learning. The first thing I learned is how to implement multiple agent spaces.

In my initial design, I treated my world much like the natural world, where each physical location can be occupied by a single Object. The problem with this idea is that non interactive objects become agents (even if they are simply ignored). As a reult, I implemented a custom space which contained two grids. One grid would contain the city objects while the other would maintain the people, zombies, and mutants.

Another change from my first model was the idea of spacial awareness among the agents. In the initial design, I was forced to perform all my steps at the action. This required me to iterate over all the agents and determine what to do at each step. Since a majority of these agents were irrelevant (such as buildings), performance was seriously compromised. In this new model, the list of agents is comprised of ONLY objects upon which I take an action, and each agent itself is responsible for handling its own action.

So, now that I had changed the model, it was time to make something more interesting than zombies overrunning a city. In this model I included the fact that some portion of the population may develop a mutation that protects them from zombification and kills zombies.

My expectation was that, eventually, the zombie population would dominate for a while but the mutant population would slowly take over and become 100% of the population.

This isn't what happened though. Looking at 1% population growth and 5% chance of mutation, the results were that the zombie population was wiped out when no more than 20% of the human population contained the gene. The other interesting factor was that there would be periodic "plagues" that would wipe out a huge bulk of the non mutated population. These seemed to come in semi regular intervals so long as there was a minimum (somewhere between 5 and 10) number of zombies.

There are a lot of changes that could improve this model and it is about 150% more verbose than it need be. I did this in order to force myself to do things the "hard way" as a means of learning some of the features that I was taking for granted in my prior model.

First I would like to implement a more complete lifecycle. If left to run for long enough, the human population will whipe out the zombie population then continue to overrun the city. A more natural lifecycle would provide better results.

The charting is not working with my custom schedule implementation.

There are other various things I would like to update but posting as is.

[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] AbstractDrawable

package com.stannum.repast.zombies;
import uchicago.src.sim.gui.*;

public abstract class AbstractDrawable implements Drawable {
    private int x;
    private int y;

    public int getX() { return x; }
    public int getY() { return y; }

    public void setPosition(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

[edit] AbstractDrawable

package com.stannum.repast.zombies;
import java.awt.*;
import uchicago.src.sim.gui.*;

public class Block extends AbstractDrawable {    
    public void draw(SimGraphics graphics) {
        graphics.drawFastRect(Color.GRAY);
    }
}

[edit] Person

package com.stannum.repast.zombies;

import java.awt.*;
import java.util.*;

import uchicago.src.sim.gui.*;
import uchicago.src.sim.util.*;
import uchicago.src.sim.util.Random;

public class Person extends AbstractDrawable {
    private int panicSteps = 5;
    private ZombieSpace space;

    private int countdown;
    private int direction;
    private boolean running;
    private int status;
    private int visionRange = 1;

    public Person(int status) {
        this.status = status;
        running = false;
    }


    public void setPanicSteps(int steps) {
        this.panicSteps = steps;
    }

    public void setSpace(ZombieSpace space) {
        this.space = space;
    }

    public void setVisionRange(int range) { this.visionRange = range; }


    public int getDirection() { return direction; }

    public int getStatus() { return status; }

    public boolean isRunning() { return running; }


    public void step() {
        direction = look(this.getX(), this.getY(), visionRange);
        move(direction);

        if (status == Status.ZOMBIE) {
            infectAll(this.getX(), this.getY());
        }

        if (running && countdown <= 0) {
            running = false;
            countdown = 0;
        }
    }


    public void panic() {
        running = true;
        this.countdown = panicSteps;
    }

    public void infect() {
        this.status = Status.ZOMBIE;
        this.countdown = 0;
    }


    public void draw(SimGraphics graphics) {
        switch (status) {
            case Status.NORMAL   : if (running) graphics.drawFastCircle(Color.WHITE); else graphics.drawFastCircle(Color.LIGHT_GRAY); break;
            case Status.MUTANT   : if (running) graphics.drawFastCircle(Color.BLUE); else graphics.drawFastCircle(Color.CYAN); break;
            case Status.ZOMBIE   : if (running) graphics.drawFastCircle(Color.RED); else graphics.drawFastCircle(Color.GREEN); break;
        }
    }


    private int look(int x, int y, int range) {
        Vector neighbors = space.getPersonSpace().getMooreNeighbors(x, y, range, range, false);
        SimUtilities.shuffle(neighbors);

        Iterator iterator = neighbors.iterator();
        while (iterator.hasNext()) {
            Person person = (Person) iterator.next();

            // im alive, you are a zombie, run away!!
            if (status != Status.ZOMBIE) {
                if (person.getStatus() == Status.ZOMBIE) {
                    panic();

                    if (person.getX() < x) {
                        if (person.getY() > y) return 1;
                        if (person.getY() == y) return 2;
                        if (person.getY() < y) return 3;
                    }
                    else if (person.getX() == x) {
                        if (person.getY() < y) return 0;
                        if (person.getY() > y) return 4;
                    }
                    else if (person.getX() > x) {
                        if (person.getY() < y) return 5;
                        if (person.getY() == y) return 6;
                        if (person.getY() > y) return 7;
                    }
                }
/*
                else if (person.isRunning()) {
                    panic();
                    return person.getDirection();
                }
*/
            }

            // im a zombie, you are FRESH BRAINS!!
            else {
                if (person.getStatus() != Status.ZOMBIE) {
                    if (person.getX() > x) {
                        if (person.getY() < y) return 1;
                        if (person.getY() == y) return 2;
                        if (person.getY() > y) return 3;
                    }
                    else if (person.getX() < x) {
                        if (person.getY() < y) return 5;
                        if (person.getY() == y) return 6;
                        if (person.getY() > y) return 7;
                    }
                    else if (person.getX() == x) {
                        if (person.getY() < y) return 4;
                        if (person.getY() > y) return 0;
                    }
                }
            }
        }

        // wander
        return Random.uniform.nextIntFromTo(0, 7);
    }

    private void move(int direction) {
        int x = getX();
        int y = getY();

        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
        }

        int spaceSizeX = space.getPersonSpace().getSizeX();
        int spaceSizeY = space.getPersonSpace().getSizeY();
        if ((x >= 0) && (x < spaceSizeX) && (y >= 0) && (y < spaceSizeY) && (!space.occupied(x, y))) {
            space.getPersonSpace().putObjectAt(getX(), getY(), null);
            this.setPosition(x, y);
            space.getPersonSpace().putObjectAt(x, y, this);
        }

        countdown--;
    }

    private void movePanic(int direction) {
        boolean moved = false;
        int maxAttempts = 20;
        int attempts = 0;

        while (!moved && attempts++ < maxAttempts) {
            int x = getX();
            int y = getY();

            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
            }

            int spaceSizeX = space.getPersonSpace().getSizeX();
            int spaceSizeY = space.getPersonSpace().getSizeY();

            if ((x >= 0) && (x < spaceSizeX) && (y >= 0) && (y < spaceSizeY) && (!space.occupied(x, y))) {
                space.getPersonSpace().putObjectAt(getX(), getY(), null);
                this.setPosition(x, y);
                space.getPersonSpace().putObjectAt(x, y, this);
                moved = true;
            }

            // pick a new random direction to try
            direction = Random.uniform.nextIntFromTo(0, 7);
        }

        countdown--;
    }

    private void infectAll(int x, int y) {
        Vector neighbors = space.getPersonSpace().getMooreNeighbors(x, y, false);
        SimUtilities.shuffle(neighbors);

        Iterator iterator = neighbors.iterator();
        while (iterator.hasNext()) {
            Person person = (Person) iterator.next();
            if (person.getStatus() == Status.NORMAL) {
                person.infect();
            }
            if (person.getStatus() == Status.MUTANT) {
                status = Status.DEAD;
            }
        }
    }
}


[edit] ZombieSpace

package com.stannum.repast.zombies;

import uchicago.src.sim.space.*;
import uchicago.src.sim.util.*;

public class ZombieSpace {
    private Object2DGrid citySpace;
    private Object2DGrid personSpace;

    private int size;

    public ZombieSpace(int size) {
        this.size = size;
        this.citySpace = new Object2DGrid(size, size);
        this.personSpace = new Object2DGrid(size, size);
    }

    public void addStructures(int count) {
        Random.createUniform();
        for (int i=0; i<count; i++) {
            int x = Random.uniform.nextIntFromTo(0, size-1);
            int y = Random.uniform.nextIntFromTo(0, size-1);
            int length = Random.uniform.nextIntFromTo(Math.round(.05f * size), Math.round(.20f * size));
            int width = Random.uniform.nextIntFromTo(Math.round(.05f * size), Math.round(.20f * size));

            for (int j=Math.max(x, 2); j<Math.min(x+length, size-3); j++) {
                for (int k=Math.max(y, 2); k<Math.min(y + width, size-3); k++) {
                    citySpace.putObjectAt(j, k, new Block());
                }
            }
        }
    }

    public void addPerson(Person person) {
        int maxAttempts = 10 * size * size;
        int attempts = 0;

        while(attempts < maxAttempts) {
            int x = Random.uniform.nextIntFromTo(0, size-1);
            int y = Random.uniform.nextIntFromTo(0, size-1);
            if (!occupied(x,y)) {
                personSpace.putObjectAt(x, y, person);
                person.setPosition(x, y);
                person.setSpace(this);
                return;
            }
            attempts++;
        }
    }

    public boolean occupied(int x, int y) {
        return (citySpace.getObjectAt(x, y) != null || personSpace.getObjectAt(x, y) != null);
    }

    public Object2DGrid getCitySpace() {
        return citySpace;
    }

    public Object2DGrid getPersonSpace() {
        return personSpace;
    }
}

[edit] Status

package com.stannum.repast.zombies;

public class Status {
    public static final int NORMAL = 1;
    public static final int MUTANT = 2;
    public static final int ZOMBIE = 3;
    public static final int DEAD = 99;
}


[edit] ZombieModel

package com.stannum.repast.zombies;

import java.util.*;

import uchicago.src.sim.engine.*;
import uchicago.src.sim.gui.*;
import uchicago.src.sim.util.*;
import uchicago.src.sim.util.Random;
import uchicago.src.sim.analysis.*;

public class ZombieModel extends SimModelImpl {

    private static final int SIZE = 100;

    private ArrayList agentList;
    private Schedule schedule;
    private ZombieSpace space;
    private DisplaySurface surface;
    private OpenSequenceGraph plot;

    private int people = 1000;
    private int structures = 20;
    private int zombies = 1;
    private double mutationRate = 0.05;
    private double reproductionRate = 0.01;


    public String getName() {
        return "ZOMG!! Zombies!";
    }

    public void setup() {
        if (surface != null) {
            surface.dispose();
        }
        if (plot != null) {
            plot.dispose();
        }

        space = null;

        agentList = new ArrayList();
        schedule = new Schedule(1);

        surface = new DisplaySurface(this, "Zombie Model Window 1");
        registerDisplaySurface("Zombie Model Window 1", surface);

        plot = new OpenSequenceGraph("plot", this);
        plot.setYRange(0, Math.max(people, zombies));
        plot.setAxisTitles("time", "count");
        plot.addSequence("people", new Sequence() {
            public double getSValue() {
                int count = 0;
                Iterator iterator = agentList.iterator();
                while (iterator.hasNext()) {
                    if (((Person) iterator.next()).getStatus() == Status.NORMAL) {
                        count++;
                    }
                }
                return count;
            }
        });

        plot.addSequence("mutants", new Sequence() {
            public double getSValue() {
                int count = 0;
                Iterator iterator = agentList.iterator();
                while (iterator.hasNext()) {
                    if (((Person) iterator.next()).getStatus() == Status.MUTANT) {
                        count++;
                    }
                }
                return count;
            }
        });

        plot.addSequence("zombies", new Sequence() {
            public double getSValue() {
                int count = 0;
                Iterator iterator = agentList.iterator();
                while (iterator.hasNext()) {
                    if (((Person) iterator.next()).getStatus() == Status.ZOMBIE) {
                        count++;
                    }
                }
                return count;
            }
        });

    }

    public void begin() {
        buildModel();
        buildSchedule();
        buildDisplay();

        surface.display();
    }

    public Schedule getSchedule() {
        return schedule;
    }

    public String[] getInitParam() {
        return new String[] {"structures", "people", "zombies"};
    }


    public void buildModel() {
        space = new ZombieSpace(SIZE);
        space.addStructures(structures);

        for (int i=0; i<people; i++) {
            addPerson();
        }
        for (int i=0; i<zombies; i++) {
            addZombie();
        }
    }

    public void buildDisplay() {
        Object2DDisplay displayCity = new Object2DDisplay(space.getCitySpace());
        surface.addDisplayable(displayCity, "City");

        Object2DDisplay displayAgents = new Object2DDisplay(space.getPersonSpace());
        displayAgents.setObjectList(agentList);
        surface.addDisplayable(displayAgents, "People");

        plot.display();
    }

    public void buildSchedule() {
        class PersonAction extends BasicAction {
            public void execute() {
                SimUtilities.shuffle(agentList);
                for(int i =0; i < agentList.size(); i++){
                    Person person = (Person) agentList.get(i);
                    if (person.getStatus() != Status.ZOMBIE) {
                        person.step();
                    }
                }
            }
        }

        class ZombieAction extends BasicAction {
            public void execute() {
                SimUtilities.shuffle(agentList);
                for(int i =0; i < agentList.size(); i++){
                    Person person = (Person) agentList.get(i);
                    if (person.getStatus() == Status.ZOMBIE) {
                        person.step();
                    }
                }
            }
        }

        class DisplayAction extends BasicAction {
            public void execute() {
                reap();
                reproduce();

                surface.updateDisplay();
                plot.updateGraph();
            }
        }


        schedule.scheduleActionAtInterval(1, new DisplayAction());
        schedule.scheduleActionAtInterval(2, new PersonAction());
        schedule.scheduleActionAtInterval(4, new ZombieAction());
    }


    public int getStructures() { return structures; }
    public void setStructures(int structures) { this.structures = structures; }

    public int getPeople() { return people; }
    public void setPeople(int people) { this.people = people; }

    public int getZombies() { return zombies; }
    public void setZombies(int zombies) { this.zombies = zombies; }


    public static void main(String[] args) {
        SimInit init = new SimInit();
        init.loadModel(new ZombieModel(), null, false);
    }


    private void addPerson() {
        Person person = null;
        double rnd = Random.uniform.nextDoubleFromTo(0, 1);
        if (rnd <= mutationRate) {
            person = new Person(Status.MUTANT);
        }
        else {
            person = new Person(Status.NORMAL);
        }

        agentList.add(person);
        space.addPerson(person);
    }

    private void addZombie() {
        Person person = new Person(Status.ZOMBIE);
        agentList.add(person);
        space.addPerson(person);
    }

    private void reap() {
        Iterator iterator = agentList.iterator();
        while (iterator.hasNext()) {
            Person person = (Person) iterator.next();
            if (person.getStatus() == Status.DEAD) {
                space.getPersonSpace().putObjectAt(person.getX(), person.getY(), null);
                iterator.remove();
            }
        }
    }

    private void reproduce() {
        int people = 0;
        Iterator iterator = agentList.iterator();
        while (iterator.hasNext()) {
            Person person = (Person) iterator.next();
            if (person.getStatus() == Status.NORMAL || person.getStatus() == Status.MUTANT) {
                people++;
            }
        }
        int offspring = (int) Math.round(reproductionRate * people);

        for (int i=0; i<offspring; i++) {
            addPerson();
        }
    }
}