Agent-Based Simulations
The main purpose of this project is to give you an opportunity to use your linked list within the context of an agent-based simulation.
In this week's simulation, we will have Agents that are on a 2D landscape. They will have positions in a continuous (floating point) 2D space, rather than the grid from the Game of Life. Like in the Game of Life, the agents are active and will implement an updateState method.
For this simulation, we won't have the concept of a scale factor, or you can think of the scale factor as being 1 so that coordinates map to pixels. If your Landscape's size is 500 by 500, then the Agents' positions should be between 0 and 500.
The Agent-based simulations were inspired by Growing Artificial Societies by Epstein and Axtell, two of the first researchers to make use computational modeling to examine social science questions about self-organizing societies.
Tasks
Testing
As you proceed through this project, maintain testing files to exercise the functionality of your code that you have written. Use previous testing files provided to you as templates for the structure.
Wrapping up LinkedLists
To enable another program to loop through the list, implement the Iterable interface, which allows a programmer to use a foreach loop structure on our LinkedLists. First, change the class definition for LinkedList to specify that it implements the Iterable interface.
public class LinkedList<T> implements Iterable<T>
Then create an Iterator subclass that handles traversing the list. Create a new private class inside the LinkedList class with the following definition.
private class LLIterator implements Iterator<T>
Give the class one field, which is of type Node, and which will hold the next node to be provided during a traversal. Then implement the following methods in the class.
public LLIterator(Node head)
the constructor for the LLIterator given the head of a list.public boolean hasNext()
returns true if there are still values to traverse (if the current node reference is not null).public T next()
returns the next item in the list, which is the item contained in the current node. The method also needs to move the traversal along to the next node in the list.public void remove()
does nothing. Implementing this function is optional for an Iterator.
Finally, add the function iterator public Iterator iterator()
to the LinkedList class.
It should return a new LLIterator with head passed to the constructor, shown as the following code:
// Return a new LLIterator pointing to the head of the list public Iterator<T> iterator() { return new LLIterator( this.head ); }
The iterator method makes the class actually implement Iterable.
Add one final method that converts the LinkedList to an ArrayList with the items in the same order. It
should have the signature: public ArrayList<T> toArrayList()
Download the LLTest file and test the standard LinkedList functions. Compile and run the test code. Debug your code until it works.
Agent Class
Create an abstract class called Agent. The simulations will require making two forms of agents with different updateState() methods. This is an ideal situation for using inheritance, which means starting with a base class: the Agent class.
The Agent class needs fields to store its x and y position. Use type double for the agent's position.
The Agent class should implement the following methods:
public Agent(double x0, double y0)
a constructor that sets the position.public double getX()
returns the x position.public double getY()
returns the y position.public void setX( double newX )
sets the x position.public void setY( double newY )
sets the y position.public String toString()
returns a String containing the x and y positions, e.g. "(3.024, 4.245)".public abstract void updateState( Landscape scape )
does nothing, for now.public abstract void draw(Graphics g)
does nothing, for now.
SocialAgent
Create a (non abstract) SocialAgent class. The first agent we will implement will ultimately move towards other agents, creating groups of agents. SocialAgent will extend the Agent class. More information about inheritance.
Give the child class a boolean field, moved, which will indicate whether the agent moved during its last updateState. It should also have a field that specifies the radius R within which it considers another agent a neighbor.
The new class will need to override the constructor, and draw methods:
public SocialAgent(double x0, double y0, int radius)
The constructor, which calls the super class constructor and sets the radius field.public SocialAgent(double x0, double y0, int radius) { super( x0, y0 ); // remainder of constructor code }
public void setRadius(int radius)
sets the cell's radius of sensitivity to the value of radius.public int getRadius()
returns the cell's radius of sensitivity.public void draw(Graphics g)
draws a circle of radius 5 (i.e. it fits in a 10x10 box) at the Agent's location. If the agent moved during the last updateState, it is drawn with a lighter shade of blue, otherwise a darker shade of blue.public void draw(Graphics g){ if(!moved) g.setColor(new Color(0, 0, 255)); else g.setColor(new Color(125, 125, 255)); g.fillOval((int) getX(), (int) getY(), 5, 5); }
public void updateState(Landscape scape)
for now, leave this blank.
AntiSocialAgent
Create an AntiSocialAgent class. As opposed to SocialAgents, AntiSocialAgents try to move away from groups of Agents. AntiSocialAgent will also extend the Agent class.
Give the child class a boolean field, moved, which will indicate whether the agent moved during its last updateState. It should also have a field that specifies the radius R within which it considers another agent a neighbor.
The new class will need to override the constructor, and draw methods:
public AntiSocialAgent(double x0, double y0, int radius)
The constructor, which calls the super class constructor and sets the radius field.public AntiSocialAgent(double x0, double y0, int radius) { super( x0, y0 ); // remainder of constructor code }
public void setRadius(int radius)
sets the cell's radius of sensitivity to the value of radius.public int getRadius()
returns the cell's radius of sensitivity.public void draw(Graphics g)
draws a circle of radius 5 (i.e. it fits in a 10x10 box) at the Agent's location. If the agent moved during the last updateState, it is drawn with a lighter shade of red, otherwise a darker shade of red.public void draw(Graphics g){ if(!moved) g.setColor(new Color(255, 0, 0)); else g.setColor(new Color(255, 125, 125)); g.fillOval((int) getX(), (int) getY(), 5, 5); }
public void updateState(Landscape scape)
for now, leave this blank.
Landscape
Create a class called Landscape. It serves the same purpose as the Landscape in the Game of Life simulation, but is different enough that you probably don't want to copy the old one and edit it. Start with a new file. The Landscape will need fields to store its width and height (as ints) and a LinkedList of Agents. Use your implementation of a linked list. The Landscape needs the following methods:
public Landscape(int w, int h)
a constructor that sets the width and height fields, and initializes the agent list.public int getHeight()
returns the height.public int getWidth()
returns the width.public void addAgent( Agent a )
inserts an agent at the beginning of its list of agents.public String toString()
returns a String representing the Landscape. It can be as simple as indicating the number of Agents on the Landscape.public LinkedList<Agent> getNeighbors(double x0, double y0, double radius)
returns a list of the Agents within radius distance of the location x0, y0.public void draw(Graphics g)
Calls the draw method of all the agents on the Landscape.
Visualization
Make it possible to visualize your Landscape. Download the LandscapeDisplay.java file. It is almost identical to last week.
Test your visualization by running LandscapeDisplay as your main program. The main program adds 200 agents to the Landscape and then displays them.
updateState( )
Add a method to your SocialAgent class, public void updateState(Landscape scape)
.
If there are less than 4 agents within its own radius from itself, it changes both its x and y coordinates
by a random value between -10 and 10. Make sure that it stays within the boundary of the window!
To your AntiSocialAgent class, add an analagous method that would move the Agent if there is more than 1 neighbor within its radius.
As you code this function, if the (Anti/)SocialAgent moves, set the moved field to true, otherwise set it to false.
updateAgents( )
Add a method to your Landscape class, public void updateAgents()
. The method should update the
state of each agent, in whatever order you'd like.
SocialAgentSimulation
Create an AgentSimulation class. It should be modeled after the LifeSimulation class. It would be best to control the Landscape size and the number of agents from the command line, but it is acceptable to hard-code them. Generate and randomly place N agents (N = 200 is reasonable) on the Landscape. Then loop over the time steps, calling updateAgents, repaint, and Thread.sleep as in LifeSimulation. Use a good size radius, such as 15-25, to start your simulation. Do your agents clump together?
- Include in your report a picture of the end of the simulation, noting the radius of sensitivity used.
- Include in your report a second picture of the end of a simulation that uses a radius that is twice as big as the first case. Note any differences.
Required simulation image set
Include a screen shot of the end of each simluation in your report.
Extensions
The following are some suggested extensions. You should feel free to pursue your own custom extensions to your project that you find interesting. Please clearly identify your extensions in your report. In your code, make sure the baseline simulations run properly. Making a separate sub-folder for major extensions is recommended.
- Try creating other agent types that implement different rule sets. Be intentional about your agent design.
- Instead of moving randomly, have your agent move towards other agents, or possibly towards some and away from others.
- Experiment with the effects of modifying some of the update rules. These should be compare and contrast. Try to avoid comparing simulations where more than one thing has been changed.
Report
Your intended audience for your report are your peers not in the class, but who have taken a CS course. Your report should explain the core CS concept used, present the results of your program, and discuss the meaning of the results: what did you discover and does it make sense?
Your project report should contain the following elements. Please include a header for each section.
- Abstract
A brief summary of the project, in your own words. This should be no more than a few sentences. Give the reader context and identify the key purpose of the assignment. Each assignment will have both a core CS purpose--usually a new data structure--and an application such as a simulation or analysis.
Writing an effective abstract is an important skill. Consider the following questions while writing it.
- Does it describe the specific project application?
- Does it describe the results of your simulation or analysis?
- Is it concise?
- Are all of the terms well-defined?
- Does it read logically and in the proper order?
- Results
Present your results. Include text output, images, tables, graphs, or qualitative descriptions, as appropriate. Include a discussion as to whether the results make sense and what they mean. For this week, you should include animated gifs or screen capture videos for each of the Agent subclasses you implemented.
- Extensions
Describe any extensions you undertook, including text output, graphs, tables, or images demonstrating those extensions. If you added any modules, functions, or other design components, note their structure and the algorithms you used.
- Reflection
A semi-brief overview of the data structure implemented/used in this project. Why did we use LinkedLists instead of, say, Arrays/Arraylists?
- References/Acknowledgements
A list of people you worked with, including TAs and instructors. Include in that list anyone whose code you may have seen, such as those of friends who have taken the course in a previous semester.
Handin
Here's a checklist of everything you should do when you are finished with a project and are ready to turn it in.
+ Create a report and export to pdf
- Create a report in your favorite document application (Microsoft Word, Google Doc, Libre Writer). Make sure to have all the sections described above.
- Print to PDF (Windows)
- Open a file in a Windows application.
- Choose "File > Print".
- Choose "Adobe PDF" as the printer in the Print dialog box.
- Click Print. Type a name for your file, and click Save.
- Print to PDF (Mac OS)
- Open a file in a Mac OS application.
- Click the "PDF" button and choose "Save As Adobe PDF".
- Choose the "Adobe PDF Settings" and click "Continue".
- Type a name for your file, and click "Save".
- Export to PDF Google Drive
- Choose "File" > "Download" > "PDF Document (.pdf)"
+ Organize your project folder
- In Finder, move your lab folder inside your project folder. For example, if you have
Lab04
andproject_04
folders both on your Desktop, drag theLab04
folder insideproject_04
.
+ Submit your project on Google Drive
- On Google Drive, locate the folder named with your Colby username that was shared with you earlier.
- Upload the relevant files into the matching project folder. you can ‘drag and drop’ the files into the project folder on Drive. (".class" files are not relevant)
- Note that once the files are copied, Google drive records submission time and any changes made. No further changes are allowed unless you want to make another submission.
- Submission checklist:
1- Your report should be in PDF.
2- Add your lab folder should be in your project Folder.
3- Your project and lab files are in the relevant directory in your working directory. Your working directory is the Google Drive folder with your name that I shared with you earlier.
4- The rubric is in your project directory and your name is added to the top row.