Sheepsmeadow is a simulation program designed and distributed with the MASON Framework. It fascilliates options to create a number of Sheeps and Wolves on a Meadow and observe their interaction over time.
Purpose of this simulation is to give an introduction to "Multi-Agent-Systems" (MAS) for undergrad Students making their first steps in MAS. They can tweak the model parameters and observe the changes of the simulation state in each time steps via a GUI. Also some simple "live statistics" will be provided.
- you need to install
make
on your machine- Windows: Download make. Then start the ".exe". This will install
make
on your machine. After installation, addmake
to yourPATH
-Variables. - Linux: Install via your favorite package manager
- MacOS: Install via your favorite package manager
- check the installation
make --version
- Windows: Download make. Then start the ".exe". This will install
- you need at least Java JDK 21 or newer. Visit the Java Website to download the JDK.
- check the installation
java --version
javac --version
- you need to "clone" this Repository
git clone https://github.com/MichaelBrueggemann/sheepsmeadow.git
- Change to this projects root directory
sheepsmeadow/
- Enter
make run
in the console- Windows: Please use
cmd
(Win + R), then enter "cmd" - Linux, MacOS: Please use
bash
- this will open the GUI to start a simulation
- Windows: Please use
- Windows:
- Deploy 'Sheepsmeadow' for Windows
- Double-click the ".exe" in
deployments/windows
. This will install the.exe
(you will be asked to give a path to install the actual program) - Double-click the .exe in the path you provided in the installer
- Linux-Debian:
- Deploy 'Sheepsmeadow' for Linux-Debian
- Install the
.deb
file - Add
Sheepsmeadow
to$PATH
export PATH=/opt/sheepsmeadow/bin:$PATH
- Run:
Sheepsmeadow
- MacOS:
- Deploy 'Sheepsmeadow' for MacOS
- Add
Sheepsmeadow
to$PATH
export PATH=/opt/sheepsmeadow/bin:$PATH
- Run:
Sheepsmeadow
!! THE TESTING SCRIPT IN THE makefile
WILL ONLY WORK ON MacOS/Linux !!
Tests for this project are defined in ./tests
. Each testfile is automaticly compiled and run on every "push" of this repositoy as part of the Testing Pipeline (see GitHub Workflow).
To run a test locally, execute:
make test
To deploy Sheepsmeadow on different systems, please follow these steps:
- Windows: You need to install the following software:
WiX Toolset v3.14
. You can download it here: WiX. Then start the ".exe". This will installWiX
on your machine. After installation, addWiX
to yourPATH
-Variables.- check the installation
candle --version
light --version
7zip
. You can download it here: 7zip. Then start the ".exe". This will install7zip
on your machine. After installation, add7zip
to yourPATH
-Variables.- check the installation
7z
make
. You can download it here: make. Then start the ".exe". This will installmake
on your machine. After installation, addmake
to yourPATH
-Variables.- check the installation
make --version
- Linux-Debian:
make
- MacOS:
make
Please note, that the different deployments are designed for x86_64
systems. This Guide might not work for other systems.
- Run
make deploy-windows
make deploy-windows
- Run
make deploy-linux-deb
make deploy-linux-deb
- Run
make deploy-macOS
make deploy-macOS
In this Section I will note some of my experiences with this project. Those notes aren't necessary to use this simulation tool, so feel free to skip the reading:
-
MASON extensively used the MVC paradigm (Model-View-Controller):
- a Model is defined e.g. as a
SimState
instance. - a View is a specific Visualization (2D, 3D, etc.).
- a Controller is a GUI, TUI, CLI to interact with the Model.
In this regard, all project files are also organised in the MVC pattern.
- this was a great opportunity for me to refresh my college knowledge of the MVC pattern.
- a Model is defined e.g. as a
- I implemented a automated testing pipeline for the following reasons:
- keeping myself accountable. Any push will be checked by the pipeline, so I have to design my code in compliance with my tests or my build will fail (and I get annoying emails because of that).
- Learn to set up testing enviroments. Setting up this pipeline was a new experience for me, as these tests arent run on my local machine, but instead are run in a VM on GitHub. Therefore debugging was a bit challenging (I encountered the famous "but it works on my machine" a lot :) ). Using the tool
act
was a great help, to debug the VM locally.
- In the current implementation, each agent will get a new
Int2D
-Object every time a location has to be changed. This is incredibly inefficient, as this pollutes memory. I decided to leave it in, as the effort needed to change this doesn't outweigh the benefit, as this project doesn't aim to provide the most performant simulation, but instead provide a simple example to learn Agent-Based-Modelling. This programm should be used as explanatory material in an first year undergrad course, so I think this is a fair consideration, as this is only a hobby project of me.
-
changed the implementation of a Neighbourhood, to enable an easier control flow to evaluate if an
Action
can be performed on anAgent
. -
Actions (rules) an agent can perform should be encapsulated in an object. This has the benefit, that the use of the
Action
interface can be enforced. This allows me to use thecheckCondition()
andexecute()
function in the agents source code. This later enables other Users to add newAction
s, will still securing that the model logic won't break. -
Each
Action
has a condition (defined by thecheckCondition()
function) that defines when theAction
can be applied. -
All Actions of an
Agent
are collected in aruleSet
, a PriorityQuene<Action> which sorts allAction
s of anAgent
by theirpriority
. This defines the "importance" of "Action"s for the "Agent".
Funny Note: on 08.03.2024 when setting up this project, I decided to represent each cell of the models grid as stacks of entities (Stack<Entity>) as I thought that this would be a clever way to have multiple Entities in one grid cell whilst also having an easy way to decide, if a cell is already occupied (stack size is greater than 1).
Today seems to be the day this bites me in the ass, as I figured out that MASONs LabeledPortrayal2D
is defined on Object directly passed from another object (my grid). But this grid doesnt pass the actual Entity-objects as LabeledPortrayal2D
would expect, but passes Stack<Entity>
objects instead. Running the model then greets me with this kind error message:
Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: class Model.Entities.Objects.Grass cannot be cast to class java.util.Stack (Model.Entities.Objects.Grass is in unnamed module of loader 'app'; java.util.Stack is in module java.base of loader 'bootstrap')
at View.MeadowDisplay$1.draw(MeadowDisplay.java:42)
at sim.portrayal.simple.LabelledPortrayal2D.draw(LabelledPortrayal2D.java:182)
at View.MeadowDisplay$2.draw(MeadowDisplay.java:63)
at sim.portrayal.grid.ObjectGridPortrayal2D.hitOrDraw(ObjectGridPortrayal2D.java:220)
at sim.portrayal.FieldPortrayal2D.draw(FieldPortrayal2D.java:84)
at sim.display.Display2D$InnerDisplay2D.paintUnbuffered(Display2D.java:671)
at sim.display.Display2D$InnerDisplay2D.paint(Display2D.java:594)
at sim.display.Display2D$InnerDisplay2D.paintComponent(Display2D.java:524)
at sim.display.Display2D$InnerDisplay2D.paintComponent(Display2D.java:511)
at java.desktop/javax.swing.JComponent.paint(JComponent.java:1128)
at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:961)
at java.desktop/javax.swing.JComponent.paint(JComponent.java:1137)
at java.desktop/javax.swing.JViewport.paint(JViewport.java:736)
at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:961)
at java.desktop/javax.swing.JComponent.paint(JComponent.java:1137)
at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:961)
at java.desktop/javax.swing.JComponent.paint(JComponent.java:1137)
at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:961)
at java.desktop/javax.swing.JComponent.paint(JComponent.java:1137)
at java.desktop/javax.swing.JComponent.paintToOffscreen(JComponent.java:5318)
at java.desktop/javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:246)
at java.desktop/javax.swing.RepaintManager.paint(RepaintManager.java:1336)
at java.desktop/javax.swing.JComponent._paintImmediately(JComponent.java:5266)
at java.desktop/javax.swing.JComponent.paintImmediately(JComponent.java:5076)
at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:878)
at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:861)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
at java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:861)
at java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:834)
at java.desktop/javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:784)
at java.desktop/javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1897)
at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
... I think it's time to implement Multi-Object grids as intended by MASON.
- I changed the grid representation in the following way:
- The grid now only contains one object per cell.
- At model setup, the grid contains only
Grass
objects. - After calling
populateMeadow
the grid will containAgent
objects in each cell an agent was added to.- adding a new agent to a cell now stores the
Grass
object of the cell in theAgent
object. When theAgent
later updates it's location, theGrass
object will be placed on the cell, theAgent
has left.
- adding a new agent to a cell now stores the
- the same will than happen in each Step of the model.
- In the lecture example, each agents step is evaluated whilst "freezing" every other agent in their position. This means that a new position of an agent (which was previously "stepped") is not known to the other agents. In this simulation on the other hand, all agent have a finite order, so an agent that would be out-of-reach for another agent (because it isn't placed in an adjacent grid cell) could move into a grid cell adjacent to another agent. This agent then checks it's "new" neighbourhood.
- This has to be kept in mind, when students try performing simulations with "Sheepsmeadow".
- I added a makefile-script to automatically compile, run, build and deploy the application. This makes the development process a lot easier, as I can use the modular
make
commands to create the different artifacts- I also added placeholders inside of the makefile to make editing this file easier for the future
- The idea behind creating "deployments" for this application is, that I can deploy executables for different plattform, so that users can just download it and run it, without needing to compile everything from source. This makes this project much more accessible for others.
- to achieve deploying this app for "windows" I will use a virtual machine on my linux machine, so that I have the needed
jpackage
dependencies to create a.exe
file. But this is still work in progress.
- Now provided a way to compile and deploy 'Sheepsmeadow' also as an
.exe
-file. My Intention was, that also students with limited programming knowledge will use this simulation program, so it is crucial to provide an easy experience. This ensures that also those students can enjoy the beauty of this program, without having to build it from source. - This also removes accessibilty barriers from this project, which makes this even more appealing.
- Compiling and Deploying this project on Linux-Debian was already possible
- Today I provided images to use in this README and inside the application. I created those images using pixelart. Here I really learned to appreciate AI art generation, as I created raster-based images which aren't fun to upscale. Fortunately, pixelart had an option to automatically upscale those images. This really boosted the design process and made my life significantly easier.
- Now it's also possible to deploy a ".dmg"
- I improved the
makefile
so that it automatically detects, on which OS it was run. This enables easier compilation of the project across plattforms.
- I started add options to the
makefile
to download a system-specific version of the JRE instance and add this to the right executable format for the OS. - To test this, I have set up a Virtual Machine for MacOS.
- After pulling an "all-nighter" and doing some additional research, I figured out how to use
jpackage
to also create.exe
files. This has the advantage, thatjpackage
automatically packs an JRE with the executable (for every OS). This makes the deployment significantly slimmer. - This makes the changes from 05.11.2024 useless