/** * --Copyright notice-- * * Copyright (c) School of Geography, University of Leeds. * http://www.geog.leeds.ac.uk/ * This software is licensed under 'The Artistic License' which can be found at * the Open Source Initiative website at... * http://www.opensource.org/licenses/artistic-license.php * Please note that the optional Clause 8 does not apply to this code. * * The Standard Version source code, and associated documentation can be found at... * [online] http://mass.leeds.ac.uk/ * * * --End of Copyright notice-- * */ import java.util.*; import java.awt.*; import java.awt.event.*; import java.io.*; import java.io.Serializable; /** * Main class for an agent-based model.

* The model has an Environment, containing raster data.

* It also has a series of Agent objects.

* Each iteration of the model, the agent's run method is called.

* The model also implements a model-wide stopping criterion.

* @version 1.0 * @author Andy Evans */ public class Model extends Frame implements ActionListener, Serializable { // We keep all the model parameters here in one place. // Agent and Environment parameters are kept in those classes, except initial default Environment size. /** Number of timesteps in model. */ private int numberOfIterations = 10000; /** Number of agents in model. */ private int numberOfAgents = 10; /** Reader/writer. */ private IO io = new IO(); /** For drawing. */ private Canvas c = new Canvas(); /** For enabling. */ MenuItem runMenuItem = new MenuItem("Run"); /** For enabling. */ MenuItem saveMenuItem = new MenuItem("Save Results..."); /** For eating environment data. */ private double eatingRate = 10.0; /** For eating environment data. */ private double fullUp = -1.0; /** * List of all agents. * Note that the use of the Agent interface allows for heterogeneous agents. */ private ArrayList agents = new ArrayList (); /** Environment to store raster data. */ private Environment world = new Environment(); /** Extra agent that the user can control. **/ private Protector protector = null; /** * Model constructor. * The arguments, if used, should be: * java Model numberOfAgents:int numberOfIterations:int eatingRate:double fullUp:double fileIn:String fileOut:String * @param args String sequence. */ public Model (String args[]) { if (args.length == 0) { buildGui(); } else { numberOfAgents = Integer.parseInt(args[0]); numberOfIterations = Integer.parseInt(args[1]); eatingRate = Double.parseDouble(args[2]); fullUp = Double.parseDouble(args[3]); String fileIn = args[4]; String fileOut = args[5]; File fIn = new File(fileIn); File fOut = new File(fileOut); world.setData(io.readData(fIn)); setSize(world.getWidth() + getInsets().left + getInsets().right, world.getHeight() + getInsets().top + getInsets().bottom); buildAgents(); runAgents(); io.writeData(world.getData(), fOut); } } /** * Builds the GUI. */ private void buildGui() { add(c); setSize(300,300); addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ ((Frame)e.getSource()).dispose(); // System.exit(0); } }); MenuBar menuBar = new MenuBar(); Menu fileMenu = new Menu("File"); menuBar.add(fileMenu); MenuItem openMenuItem = new MenuItem("Open..."); fileMenu.add(openMenuItem); openMenuItem.addActionListener(this); fileMenu.add(saveMenuItem); saveMenuItem.addActionListener(this); saveMenuItem.setEnabled(false); Menu modelMenu = new Menu("Model"); menuBar.add(modelMenu); modelMenu.add(runMenuItem); runMenuItem.addActionListener(this); runMenuItem.setEnabled(false); setMenuBar(menuBar); setLocation( (int)(Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2) - (getWidth() / 2), (int)(Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2) - (getHeight() / 2) ); setVisible(true); } /** * Paints agents and data to the screen. * @param g Graphics context (actually draws on a canvas, so unused) */ public void paint(Graphics g) { Image image = world.getDataAsImage(); Graphics cg = c.getGraphics(); if (image != null) { cg.drawImage(image, 0, 0, this); } for (Agent agent : agents) { cg.setColor(Color.GREEN); cg.fillOval(agent.getX() - 1,agent.getY() - 1,2,2); cg.drawPolygon(agent.getNeighbourhood()); cg.setColor(Color.BLACK); } if (protector != null) { cg.setColor(Color.YELLOW); cg.fillOval(protector.getX() - 1,protector.getY() - 1,2,2); cg.drawPolygon(protector.getNeighbourhood()); cg.setColor(Color.BLACK); } } /** * Listens to menus. */ public void actionPerformed(ActionEvent e) { MenuItem clickedMenuItem = (MenuItem)e.getSource(); if (clickedMenuItem.getLabel().equals("Open...")) { FileDialog fd = new FileDialog(this, "Open File", FileDialog.LOAD); fd.setVisible(true); File f = null; if((fd.getDirectory() != null)||( fd.getFile() != null)) { f = new File(fd.getDirectory() + fd.getFile()); world.setData(io.readData(f)); setSize(world.getWidth() + getInsets().left + getInsets().right, world.getHeight() + getInsets().top + getInsets().bottom); buildAgents(); } runMenuItem.setEnabled(true); } if (clickedMenuItem.getLabel().equals("Save Results...")) { FileDialog fd = new FileDialog(this, "Save File", FileDialog.SAVE); fd.setVisible(true); File f = null; if((fd.getDirectory() != null)||( fd.getFile() != null)) { f = new File(fd.getDirectory() + fd.getFile()); io.writeData(world.getData(), f); } } if (clickedMenuItem.getLabel().equals("Run")) { Thread tt = new Thread(new Runnable() { public void run() { runAgents(); }}); tt.start(); saveMenuItem.setEnabled(true); } repaint(); } /** * Makes the world with initial values. */ private void buildWorld() { for (int i = 0; i < world.getHeight(); i++) { for (int j = 0; j < world.getWidth(); j++) { world.setDataValue(j, i, 255.0); } } } /** * Makes the agents. */ private void buildAgents() { for (int i = 0; i < numberOfAgents; i++) { agents.add(new Nibbler(world,agents,eatingRate,fullUp)); } // Make agent controlled by user, and add appropriate key listener. protector = new Protector(world,agents); agents.add(protector); addKeyListener(new KeyListener() { public void keyPressed(KeyEvent e) { int keyCode = e.getKeyCode(); String keyString = KeyEvent.getKeyText(keyCode); switch(keyString) { case ("Up") : protector.setY(protector.getY() - 1); break; case ("Down") : protector.setY(protector.getY() + 1); break; case ("Left") : protector.setX(protector.getX() - 1); break; case ("Right") : protector.setX(protector.getX() + 1); break; } } public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { } }); } /** * Runs the agents. * Iterates through numberOfIterations. Randomises the agent order. */ private void runAgents() { long startTime = System.currentTimeMillis(); for (int i = 0; i < numberOfIterations; i++) { Collections.shuffle(agents); for (int j = 0; j < agents.size(); j++) { agents.get(j).run(); // System.out.println(i + " " + agents.get(j).getX() + " " + agents.get(j).getY() + " " + world.getDataValue(agents.get(j).getX(), agents.get(j).getY())); } update(getGraphics()); if (stoppingCriteriaMet() == true) break; } System.out.println("Time taken = " + (((double)((System.currentTimeMillis() - startTime))) / 1000.0) + " seconds"); } /** * Checks stopping criteria. * Returns true if stopping criteria met, false otherwise, including where the * criteria can't be checked. * Here the stopping criterion is that there is nothing left in the environment. * @return whether the stopping criteria have been met */ private boolean stoppingCriteriaMet() { for (int i = 0; i < world.getHeight(); i++) { for (int j = 0; j < world.getWidth(); j++) { if (world.getDataValue(j, i) > 0.0) return false; } } return true; } /** * Just calls the constructor. * The arguments, if used, should be: * java Model numberOfAgents:int numberOfIterations:int eatingRate:double fullUp:double fileIn:String fileOut:String * @param args String sequence. */ public static void main (String args[]) { new Model(args); } }