/**
* --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();
/**
* 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);
}
}
/**
* 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")) {
runAgents();
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));
}
}
/**
* 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);
}
}