SCHOOL OF ENGINEERING AND APPLIED
SCIENCES
HARVARD UNIVERSITY
SSR-Swarm Simulation Environment
|
| Contents |
Part 1: Setup & Running Applications
Part 2: Programming ModelI. Philosophy
II. General Model
III. Hello World
IV. Starting to Write Your Own Application
V. Writing a Simulation World
VI. General Tips
| I. Philosophy |
The most important thing to understand about the SSR-Swarm environment is the design principal that agent code should be separated as much as possible from world code. In this context, agent code refers to the logic that you build into your agents, whereas world code refers to the "rules" of how the world works and what agents can and can't do.Here is an example of one set of rules we could build into a world:
1. Agents may only communicate with other agents within 5 meters.Given this set of rules, there are many different ways that we could program agents to operate in this world. Here is one example of how we could implement an agent's logic:
2. Agents can move in any direction at a rate of 1 meter per second.1. If any agents are nearby, move away from them.
2. Otherwise, move in a random direction.There are many good reasons for this distinction: its easy to implement many different agents while reusing the same world; it forces agent developers to act within the rules of the world, preventing accidental (or intention) code bugs that give agents "superpowers" that they are not supposed to have; it provides for a convenient way of breaking application down into more easily understood modules.
| II. General Model |
This is the general model that every SSR-Swarm application follows:
Figure 1. The SSR-Swarm Architecture (gif format)First, there is a single "world" object (blue) which implements the rules that all agents must follow. Agents (red) each implement internal logic, but must each support a common interface (orange) so that the world can call certain methods on them. Agents interact with the world via a well-defined interface (green) that the world exposes to agents. Note that agents have no direct access to the world object; likewise, the world itself has no direct access to agents' private implementations.
This is all pretty abstract, let's take a look at a specific applicaion:
| III. Hello World |
The Hello World application is a very simple applications that demonstrates some of the basics of developing SSR-Swarm applications. At a high level, the application works at follows. First, some number of agents are added to the world. Then, when the simulation is started, the world continually loops over all of the agents, prompting each of them to act in the world. In this application, "acting in the world" consists simply of writing strings of text, although agents (depending on their internal logic) may choose different things to say. Here is an applet version of this application which uses 3 agents; 1 of them is an instance of the "Gentleman" class, which says "I'm very pleased to meet you" whenever prompted, and the remaining 2 agents are instances of the "Egotist" class, which choose one of a selection of egotistical things to say. The world is implemented so as to remember and display the most recent 5 lines of text from each agent.Now let's look at the actual classes and interfaces that make up the package edu.harvard.ssr.swarm.apps.hello. If we take figure 1 and replace each component with the specific classes and interfaces used in the Hello World application, we get the following:
Figure 2. Hello World Architecture (gif format)Here is a description of each class:
HelloWorldSim - The main application, which which implements all world rules specific to this application. Agents cannot call methods on this object directly. This class inherits from SwarmSim which is the base class for all applications. Internally, this class maintains a history of chat messages for each agent. Note that the addHelloAgent method requires that all agents added to this application implement the HelloAgent interface.
HelloActions - An interface that HelloWorldSim implements for agents to call methods on. This provides a concise, well-known interface for how agents interact with this world. The two actions available to agents are clear, which clears all of this agent's previous messages, and talk which adds a new line of text for this agent. As specified in the Javadoc comments, the server may limit the total number of lines of text stored for any one agent, so adding a new line might kick out an older line of text. Also note that talk specifies that the argument cannot be greater than 40 characters long, or an IllegalActionException will be thrown. This is the standard exception that is thrown whenever an agent tries to do something that the rules of the world don't allow. If you ever see one thrown, that means some agent implementation is trying to "cheat."
HelloAgent - An interface that all agent implementations must implement. This provides a well-known interface with which the HelloWorldSim can interact with each agent, regardless of its internal logic. The HelloWorldSim object calls on each agent to act in the world via the update method on the HelloAgent interface. Also note that update takes a HelloActions object as a parameter so that agents can call on it in order to take actions in the world.
Egotist - One specific agent implementation, in which the agent tends to say egotistical things when prompted to act.
Gentleman - Another specific agent implementation, in which the agent says polite things when prompted to act.
HelloWorldApp - A convenience class used in running instances of this application. Ignore this class for now. (note: does not appear in flow chart above)
This is a typical set of files for one application. The number of different agent implementations that you use might vary (in this case there are 2), but otherwise your files will probably match up with a 1:1 mapping to the files listed here.
At this point, you should mostly be able to understand all of the code in the files above, other than HelloWorldSim and HelloWorldApp. Further explanations of these classes will be given later.
| IV. Starting To Write Your Own Application |
Now that we have looked at the different components of the Hello World application, let's think about how you would go about writing your own application. As noted at the end of the section above, other than agent implementations (of which you may have multiple), your application will probably consist of your own version of each of the files listed above.Agent Interface
The agent interface is often the easiest place to start; in almost all applications, it will look almost exactly the same as HelloAgent: one method (often named update), which takes one parameter, which is an instance of the actions interface for that application. Although you may occasionally want to define some additional methods, this will probably be rare; in fact, the Agent interfaces for all four of the applications that come with the SSR-Swarm distribution follow this exact same pattern.Actions Interface
The actions interface is often good to tackle next. You will need to consider all of the actions that agents should be allowed to take in your world, so this can take a bit of thought. Ideally, you will completely specify this interface before writing any other code, although in reality (particularly for larger, more complex simulation worlds), you will probably have to make some updates to this interface over time as you realize things that you forgot initially ("Oops, I forgot to give ants a way to pick up food once they find some...").Once you have your interfaces defined first (always a good design practice), you can work on implementing agents and the simulation world. You can handle these classes in any order since neither depends on the other.
Agent Implementations
This work is pretty application specific and there isn't too much of a pattern to follow. By and large the only public methods that your agents will need to implement are those that you defined in your agent interface. As you flesh out the logic in each of these methods, this process should help you identify any persistant state (i.e. class member variables) that your agent needs. In the Hello World application, both agents are quite simple and require no private state, although more complicated agents often do (for example, see the Curler and Loner agents used in the Wander application).Simulation World
This is where the real meat of the simulation comes in, and is also (usually) the most difficult component to develop; because of this, a seperate section is dedicated to programming the main simulation class (see below).Application Class
The four components listed above are sufficient to compile a complete simulation. However, it is usually helpful to also create an "application" class which will make it easier when it comes time to actually run your simulation. An application class is simply a class that implements the Application interface. In the init method, your class should create an instance of your simulation world and perform any necessary initialization (such as creating and adding agents to the simulation, or setting simulation parameters). In the getSim method, your class should return this previously created instance of your simulation world. Check out any of the existing applications for an example.The advantage to creating this class is that it can be reused without modification for both applets and command line applications (both with or without a Swing GUI). See the included makefile for examples of running command line applications, or view source on any of the applet pages for examples of running applets.
| V. Writing a Simulation World |
The class that implements your simulation world is generally the most challenging component of a simulation to write. To start with, all simulations must inherit from SwarmSim which provides all of the core functionality of a simulation. Although SwarmSim provides a fairly large number of methods, the great majority of them you will never need to worry about. Here are the main concepts:Events
The SwarmSim implements a discrete event simulator. Essentially, a queue of events is maintained internally. To make things happen in the simulation, an event must be inserted into the queue, along with the time that the event should occur. The queue stores events in order of their schedule execution time, such at the event at the front of the queue is always the event scheduled to happen before any others in the queue (i.e. it is "next"). The simulation proceeds by simply looping over this queue, removing the head event and executing it. If the queue ever empties, the simulation will halt although in practice many events, in the process of executing, cause more events to be enqueued, such that the queue will never empty. Although events can be anything you want (even just System.out.println("Foobar")), they typically represent some kind of world event, like an agent moving, some pheromone evaporating, or a bit of error being introduced into an agent's sensors. New events are inserted into the sytem by the method scheduleEvent.Threading Issues
The SSR-Swarm simulator runs across multiple threads which means that synchronization is important to take into account. In this area, we have generally chosen to sacrifice performance for ease of use. Specifically, developers can by and large avoid ever having to write synchronized code if they so choose. This is almost always the right choice to make considering the difficulty of writing correct thread-safe code. If you find that the simulation is running slower than you need, I encourage you to contact me to discuss your project and options for increasing performance.To avoid having to worry about writing thread-safe code, you only need to follow two simple rules in implementing your inherited version of SwarmSim:
1. Don't call setEventSynchronization(false). By default this parameter is set to true, which is what you want in this case. This method is available only for advanced users that are willing to sacrifice the convenience of not having to worry about thread-safety.
2. Only access the class' member variables from within events. For example, suppose that you declare a method setAgentColor(Agent agent, Color color) which changes the color of an agent. Here is the wrong way to implement this:
This is wrong because agentColors (which is a member variable) is accessed directly in this method which violates the rule that member variables should only be accessed from within events. Here is the correct way to implement this:// declare a map to keep track of agents' colors Map<Agent,Color> agentColors = new HashMap<Agent,Color>(); ... public void setAgentColor(Agent agent, Color color) { // THIS IS THE WRONG THING TO DO: // simply update this agent's entry in the agent->color map agentColors.put(agent, color); }So basically we have just taken the code from the previous (wrong) method, wrapped it in a Runnable object, and scheduled that Runnable to run as an event after a delay of 0 (because we presumably want it to happen as soon as possible in the simulation). So to reiterate, whenever you make a public method that needs to access member variables, that access must happen within an event.// declare a map to keep track of agents' colors Map<Agent,Color> agentColors = new HashMap<Agent,Color>(); ... public void setAgentColor(Agent agent, Color color) { // THIS IS THE RIGHT THING TO DO: // execute this on the event loop since we need to access shared data scheduleEvent(new Runnable() { public void run() { // when this event executes, update this agent's entry in the agent->color map agentColors.put(agent, color); }}, 0); // schedule event to run as soon as possible (delay = 0) }Tracking Agents
Since the SSR-Swarm environment is intended to be flexible enough to work for a variety of different scenarios, the class SwarmSim provides very little in the way of application framework since this framework by necessity would vary from application to application. For example, there are no built-in notions of movement, communication, sensing, or objects (other than obstacles). These concepts must all be defined by the specific simulation implementation. However, the one exception to this rule is that each SwarmSim object creates one instance of the AgentTracker class.This class implements a lookup registry for agents and their positions in the world. Firstly, the SwarmSim class uses this object to know what agents it should paint when necessary (see below for more information). Secondly, for applications that assign agents to positions in the world (which is generally the case, HelloWorld being an exception), this class provides an efficient region-based lookup mechanism. Essentially, the AgentTracker class helps answer questions of the form "What agents are currently within a range of X from position P?". If there are a large number of agents in the simulation, the method getAgentsInRange is more efficient at retrieving this information than the naive method of iterating over all agents and checking their distance from P. Just remember that whenever an agent's position changes, you need to call update on the AgentTracker with the agent's new coordinate. In the end, the AgentTracker class is provided purely as a convenience to you and if you don't want to use it, you can safely ignore it with absolutely no ill-effects.
Painting
If you run your application with a GUI, the GUI code will occasionally call paint on your simulation, which requests that your simulation draw a representation of the current state of the world to the provided Graphics object. The SwarmSim class provides the following default implementation of the paint method:
paint() { 1. paint the entire paintable area the background color 2. paint the unused border areas gray 3. paint each obstacle 4. paint each agent }Any of these steps can be overridden to provide custom behavior in your application. Also, note that the default implementation of step 4 will only paint agents that have been registered with the AgentTracker object (described above). As long as you register each of your agents with the AgentTracker, you can be guaranteed that any calls to paint will result in a call to paintAgent for each of your agents. Conversely, if you do not register your agents, none of them will be painted by default.
| VI. General Tips |