Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move core example models back (v2) #2358

Merged
merged 118 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
118 commits
Select commit Hold shift + click to select a range
9d4781e
Move examples from mesa repo to mesa-examples repo.
jackiekazil Nov 16, 2022
d3aee92
Move examples from mesa repo to mesa-examples repo.
jackiekazil Nov 16, 2022
b88486d
Add sugarcape_g1mt that is consistent with complexity tutorial (#18)
tpike3 Feb 3, 2023
e43e1f7
Fix Eat Sugarscape_g1mt (#20)
tpike3 Mar 31, 2023
579521c
requirements: Pin Mesa version to 1.x
rht Dec 5, 2022
15d279c
requirements: Pin Mesa version to 1.x
rht Dec 5, 2022
bfadd93
Fixed consistent capitalization of files.
ItsQuinnMoore Apr 24, 2023
dcd6132
Fixed consistent capitalization of files.
ItsQuinnMoore Apr 24, 2023
a95df26
Update outdated readme to be more accurate + clean up requirements.tx…
houssam-kensho Apr 24, 2023
8621d59
Use pre-commit (#27)
catherinedevlin Apr 25, 2023
7627f03
Use pre-commit (#27)
catherinedevlin Apr 25, 2023
36a407e
Update examples to version 1.2 (using SingleGrid instead of Grid) (#30)
jeremander Apr 25, 2023
8b2295d
Update examples to version 1.2 (using SingleGrid instead of Grid) (#30)
jeremander Apr 25, 2023
1b03752
Add a 'headless' option to run.py to allow wolf_sheep to run headless
Apr 25, 2023
4fb3eec
Fix bug boltzman model agent giving money to itself (#28)
houssam7737 Apr 25, 2023
13331fe
check to make sure sys.argv actually has two or more items
Apr 25, 2023
4b19116
modified wolf_sheep edits to just include the one liine explicit true…
Apr 25, 2023
b903296
modified wolf_sheep edits to just include the one liine explicit true…
Apr 25, 2023
ff30c87
Apply Black to boid_flockers
rht May 17, 2023
baa448c
Fix bug in tutorial to calculate MRS with potential trade (#33)
tpike3 May 5, 2023
803e412
Implement Streamlit UI for Boltzmann wealth model (#36)
ankitk50 Jun 11, 2023
1960a2b
Update Readme.md for Conway's Game of Life (#37)
ankitk50 Jun 11, 2023
7345199
Package Schelling and civil_violence examples
rht Jun 17, 2023
bd91c31
Package Schelling and civil_violence examples
rht Jun 17, 2023
c999c7a
Package wolf_sheep example
rht Jun 17, 2023
0eefba9
boltzmann wealth model: Apply isort to app.py
rht Jun 18, 2023
afd8e17
Apply isort
rht Jun 25, 2023
6d037b5
Apply ruff --fix
rht Jun 25, 2023
de4e17b
Apply isort
rht Jun 25, 2023
656abaf
Fix remaining Ruff errors manually
rht Jun 25, 2023
fd7edb1
Apply ruff --fix
rht Jun 25, 2023
315101a
boltzmann wealth model: Replace Streamlit viz with Solara
rht Jun 29, 2023
8f947d8
Simplify boltzmann_wealth_model frontend setup
rht Jun 29, 2023
99d42f9
fix: Move agent_portrayal to example-specific file
rht Jun 29, 2023
bd0d542
feat: Support color in Jupyter viz
rht Jun 29, 2023
65f2018
schelling: Switch to Jupyter viz
rht Jun 29, 2023
cdf47cf
Fix Ruff errors
rht Jun 29, 2023
0bbe16b
schelling: Add solara as requirement
rht Jun 29, 2023
48807f3
Add mesa_models to requirements.txt
rht Jun 29, 2023
5cf28c8
Make separate copies for experimental
rht Jun 29, 2023
90e2caa
Add back original boltzmann and schelling
rht Jun 29, 2023
2a68b11
Bug fixes and additions to Epstein Civil Violence (#42)
JoeHelbing Jun 30, 2023
1ea028a
trading Sugarscape: Add solara visualization
rht Jul 6, 2023
63c3a69
solara trading sugarscape: Use coord_iter
rht Jul 7, 2023
7adbb0b
Bump to Mesa 2.0 (#40)
rht Jul 16, 2023
013afa9
Bump to Mesa 2.0 (#40)
rht Jul 16, 2023
7709bd9
Move jupyter_viz to core Mesa repo
rht Jul 20, 2023
e692eed
fix: Add count_cops method to the model (#60)
jurreaserna Sep 15, 2023
394e566
solara: Add virus_on_network network example
rht Aug 17, 2023
34aa1c9
Sugarscape G1MT: Update to Mesa 2.1.2 JupyterViz API
rht Sep 26, 2023
aa6377d
Sugarscape {G1,{M,T}}: Add tests
rht Nov 26, 2023
98e7ede
refactor: Simplify Sugarscape resource agents
rht Nov 27, 2023
2cb5a06
Fix epstein_civil_voilence reqs txt.
jackiekazil Dec 27, 2023
86fcb98
Fix conways_game reqs txt.
jackiekazil Dec 27, 2023
27a74a1
Always use relative imports and fix examples
Corvince Nov 1, 2023
54d44e0
Apply ruff --fix .
rht Jan 11, 2024
1672af7
Apply lint fixes
rht Jan 11, 2024
b6f4307
Apply lint fixes
rht Jan 11, 2024
2fe3bef
fix: Use new API to loop over agents_by_type
rht Jan 11, 2024
9b8630d
Add super().__init__() in model.__init__()
rht Jan 11, 2024
5e2cd7d
Apply ruff format
rht Jan 11, 2024
fe89077
Add super().__init__() in model.__init__()
rht Jan 11, 2024
76d3c0a
wolf_sheep: Handle the case when type_class not in agents_by_type
rht Jan 11, 2024
d08a26c
Boid flockers: Add Solara viz file
rht Jan 9, 2024
985e889
Revert "fix: Use new API to loop over agents_by_type"
rht Jan 24, 2024
d80f17e
fix: Ensure schedule.steps is always incremented before data collection
rht Jan 24, 2024
999dcce
sugarscape_g1mt: Remove dependence on model.schedule.steps
rht Jan 24, 2024
b51c09e
fix: Use resource agent for Sugarscape G1MT server.py
rht Jan 24, 2024
c8ba9e9
Improve Virus on Network documentation (#100)
coderbeta1 Feb 22, 2024
af856c6
Improve boid flocker model and documentation (#101)
coderbeta1 Feb 24, 2024
f855671
Improve model to benchmark (#104)
coderbeta1 Feb 25, 2024
b045e66
Improve schelling model documentation (#103)
coderbeta1 Feb 26, 2024
5f4b49c
fix sugarscape_g1mt running options, requirements and changes to READ…
FoFFolo Mar 2, 2024
3610bcc
Fix Sugarscape G1MT app.py to use Resource instead
rht Mar 19, 2024
2ced3b0
[pre-commit.ci] pre-commit autoupdate (#116)
pre-commit-ci[bot] May 7, 2024
c120d97
Fix pre-commit
EwoutH Jul 4, 2024
bd63101
Make batch_run pytestable by adding main() functions (#143)
EwoutH Jul 22, 2024
56a0c04
Address duplicate position warning (#122)
rht Aug 10, 2024
6cc60c3
Address duplicate position warning (#122)
rht Aug 10, 2024
e946873
Use SolaraViz instead of JupyterViz in examples
EwoutH Aug 11, 2024
fcc415b
Use SolaraViz instead of JupyterViz in examples
EwoutH Aug 11, 2024
eade76d
sugerscape_g1mt: Refactor using AgentSet functionality
EwoutH Aug 10, 2024
267499e
wolf_sheep: Replace custom scheduler with AgentSet functionality
EwoutH Aug 10, 2024
c8cb14e
sugerscape_g1mt: Use model.get_agents_of_type instead of .select()
EwoutH Aug 11, 2024
977e7ae
Replace model.schedule.agents with model.agents AgentSet
EwoutH Aug 17, 2024
e17e1c5
Replace model.schedule.agents with model.agents AgentSet
EwoutH Aug 17, 2024
0b3bbb9
Replace RandomActivation scheduler with AgentSet
EwoutH Aug 17, 2024
7e90179
Replace RandomActivation scheduler with AgentSet
EwoutH Aug 17, 2024
9211360
Replace SimultaneousActivation scheduler by AgentSet
EwoutH Aug 17, 2024
145b628
Remove adding agents to schedulers
EwoutH Aug 17, 2024
1be2dec
Remove adding agents to schedulers
EwoutH Aug 17, 2024
12aa3ed
Restore Sugerscape and Wolf-sheep schedulers
EwoutH Aug 17, 2024
25fadfb
Custom schedule replacements
EwoutH Aug 17, 2024
a0f6ab6
Final fixes
EwoutH Aug 17, 2024
4eb166d
Final fixes
EwoutH Aug 17, 2024
e112c0b
Revert PR #161: Replace schedulers with AgentSet functionality (#170)
EwoutH Aug 22, 2024
9f27caf
Revert PR #161: Replace schedulers with AgentSet functionality (#170)
EwoutH Aug 22, 2024
227b6b2
Ensure grasspatches info is only collected if gras is true (#181)
quaquel Aug 28, 2024
64c9709
Update SugarscapeG1mt for automatic time advancement
EwoutH Aug 29, 2024
b1d5a93
Reinstate PR #161: Replace schedulers with AgentSet functionality
EwoutH Aug 30, 2024
90d668c
Reinstate PR #161: Replace schedulers with AgentSet functionality
EwoutH Aug 30, 2024
83c56c5
Replace `get_agents_of_type` method with `agents_by_type` property (#…
EwoutH Sep 3, 2024
c1e051b
Remove unique_id and model.next_id (#194)
quaquel Sep 5, 2024
15aa757
Remove unique_id and model.next_id (#194)
quaquel Sep 5, 2024
26624c0
update examples to use new SolaraViz API (#193)
Corvince Sep 17, 2024
52f4c3d
update examples to use new SolaraViz API (#193)
Corvince Sep 17, 2024
4e76eff
Use performance optimized shuffle_do() method (#201)
EwoutH Sep 21, 2024
0f4b939
Use performance optimized shuffle_do() method (#201)
EwoutH Sep 21, 2024
eae3f0b
Replace the remaining schedulers with AgentSet functionality (#202)
EwoutH Sep 21, 2024
e23b804
Replace the remaining schedulers with AgentSet functionality (#202)
EwoutH Sep 21, 2024
8000505
merge experimental schelling into schelling example
wang-boyu Oct 11, 2024
0a77f58
merge experimental boltzmann wealth into boltzmann wealth example
wang-boyu Oct 12, 2024
0c5b753
refactor: Simplify Schelling code (#222)
rht Oct 14, 2024
e897839
Moving examples to use the new discrete spaces (#198)
quaquel Oct 15, 2024
3773e4d
Add basic example models
EwoutH Oct 15, 2024
2270116
Add advanced example models
EwoutH Oct 15, 2024
9d0d6c0
Add Readme for examples
EwoutH Oct 15, 2024
9c8a581
Exclude examples from ruff, fix remaining stuff
EwoutH Oct 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Mesa core examples
This folder contains a collection of example models built using Mesa. These core models are maintained by the Mesa team and are intended to demonstrate the capabilities of Mesa.

More user examples and showcases can be found in the [mesa-examples](https://github.com/projectmesa/mesa-examples) repository.

## Basic Examples
The basic examples are relatively simple and only use stable Mesa features. They are good starting points for learning how to use Mesa.

### [Boltzmann Wealth Model](basic/boltzmann_wealth_model)
Completed code to go along with the [tutorial](https://mesa.readthedocs.io/latest/tutorials/intro_tutorial.html) on making a simple model of how a highly-skewed wealth distribution can emerge from simple rules.

### [Boids Flockers Model](basic/boid_flockers)
[Boids](https://en.wikipedia.org/wiki/Boids)-style flocking model, demonstrating the use of agents moving through a continuous space following direction vectors.

### [Conway's Game of Life](basic/conways_game_of_life)
Implementation of [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life), a cellular automata where simple rules can give rise to complex patterns.

### [Schelling Segregation Model](basic/schelling)
Mesa implementation of the classic [Schelling segregation](http://nifty.stanford.edu/2014/mccown-schelling-model-segregation/) model.

### [Virus on a Network Model](basic/virus_on_network)
This model is based on the NetLogo [Virus on a Network](https://ccl.northwestern.edu/netlogo/models/VirusonaNetwork) model.

## Advanced Examples
The advanced examples are more complex and may use experimental Mesa features. They are good starting points for learning how to build more complex models.

### [Epstein Civil Violence Model](advanced/epstein_civil_violence)
Joshua Epstein's [model](http://www.uvm.edu/~pdodds/files/papers/others/2002/epstein2002a.pdf) of how a decentralized uprising can be suppressed or reach a critical mass of support.

### [Demographic Prisoner's Dilemma on a Grid](advanced/pd_grid)
Grid-based demographic prisoner's dilemma model, demonstrating how simple rules can lead to the emergence of widespread cooperation -- and how a model activation regime can change its outcome.

### [Sugarscape Model with Traders](advanced/sugarscape_g1mt)
This is Epstein & Axtell's Sugarscape model with Traders, a detailed description is in Chapter four of *Growing Artificial Societies: Social Science from the Bottom Up (1996)*. The model shows how emergent price equilibrium can happen via decentralized dynamics.

### [Wolf-Sheep Predation Model](advanced/wolf_sheep)
Implementation of an ecological model of predation and reproduction, based on the NetLogo [Wolf Sheep Predation](http://ccl.northwestern.edu/netlogo/models/WolfSheepPredation) model.
116 changes: 116 additions & 0 deletions examples/advanced/epstein_civil_violence/Epstein Civil Violence.ipynb

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions examples/advanced/epstein_civil_violence/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Epstein Civil Violence Model

## Summary

This model is based on Joshua Epstein's simulation of how civil unrest grows and is suppressed. Citizen agents wander the grid randomly, and are endowed with individual risk aversion and hardship levels; there is also a universal regime legitimacy value. There are also Cop agents, who work on behalf of the regime. Cops arrest Citizens who are actively rebelling; Citizens decide whether to rebel based on their hardship and the regime legitimacy, and their perceived probability of arrest.

The model generates mass uprising as self-reinforcing processes: if enough agents are rebelling, the probability of any individual agent being arrested is reduced, making more agents more likely to join the uprising. However, the more rebelling Citizens the Cops arrest, the less likely additional agents become to join.

## How to Run

To run the model interactively, run ``EpsteinCivilViolenceServer.py`` in this directory. e.g.

```
$ python EpsteinCivilViolenceServer.py
```

Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press Reset, then Run.

## Files

* ``EpsteinCivilViolence.py``: Core model and agent code.
* ``EpsteinCivilViolenceServer.py``: Sets up the interactive visualization.
* ``Epstein Civil Violence.ipynb``: Jupyter notebook conducting some preliminary analysis of the model.

## Further Reading

This model is based adapted from:

[Epstein, J. “Modeling civil violence: An agent-based computational approach”, Proceedings of the National Academy of Sciences, Vol. 99, Suppl. 3, May 14, 2002](http://www.pnas.org/content/99/suppl.3/7243.short)

A similar model is also included with NetLogo:

Wilensky, U. (2004). NetLogo Rebellion model. http://ccl.northwestern.edu/netlogo/models/Rebellion. Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL.
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import math

import mesa


class EpsteinAgent(mesa.experimental.cell_space.CellAgent):
def update_neighbors(self):
"""
Look around and see who my neighbors are
"""
self.neighborhood = self.cell.get_neighborhood(radius=self.vision)

self.neighbors = self.neighborhood.agents
self.empty_neighbors = [c for c in self.neighborhood if c.is_empty]


class Citizen(EpsteinAgent):
"""
A member of the general population, may or may not be in active rebellion.
Summary of rule: If grievance - risk > threshold, rebel.

Attributes:
hardship: Agent's 'perceived hardship (i.e., physical or economic
privation).' Exogenous, drawn from U(0,1).
regime_legitimacy: Agent's perception of regime legitimacy, equal
across agents. Exogenous.
risk_aversion: Exogenous, drawn from U(0,1).
threshold: if (grievance - (risk_aversion * arrest_probability)) >
threshold, go/remain Active
vision: number of cells in each direction (N, S, E and W) that agent
can inspect
condition: Can be "Quiescent" or "Active;" deterministic function of
greivance, perceived risk, and
grievance: deterministic function of hardship and regime_legitimacy;
how aggrieved is agent at the regime?
arrest_probability: agent's assessment of arrest probability, given
rebellion
"""

def __init__(
self,
model,
hardship,
regime_legitimacy,
risk_aversion,
threshold,
vision,
):
"""
Create a new Citizen.
Args:
model: the model to which the agent belongs
hardship: Agent's 'perceived hardship (i.e., physical or economic
privation).' Exogenous, drawn from U(0,1).
regime_legitimacy: Agent's perception of regime legitimacy, equal
across agents. Exogenous.
risk_aversion: Exogenous, drawn from U(0,1).
threshold: if (grievance - (risk_aversion * arrest_probability)) >
threshold, go/remain Active
vision: number of cells in each direction (N, S, E and W) that
agent can inspect. Exogenous.
model: model instance
"""
super().__init__(model)
self.hardship = hardship
self.regime_legitimacy = regime_legitimacy
self.risk_aversion = risk_aversion
self.threshold = threshold
self.condition = "Quiescent"
self.vision = vision
self.jail_sentence = 0
self.grievance = self.hardship * (1 - self.regime_legitimacy)
self.arrest_probability = None

def step(self):
"""
Decide whether to activate, then move if applicable.
"""
if self.jail_sentence:
self.jail_sentence -= 1
return # no other changes or movements if agent is in jail.
self.update_neighbors()
self.update_estimated_arrest_probability()
net_risk = self.risk_aversion * self.arrest_probability
if self.grievance - net_risk > self.threshold:
self.condition = "Active"
else:
self.condition = "Quiescent"

if self.model.movement and self.empty_neighbors:
new_cell = self.random.choice(self.empty_neighbors)
self.move_to(new_cell)

def update_estimated_arrest_probability(self):
"""
Based on the ratio of cops to actives in my neighborhood, estimate the
p(Arrest | I go active).
"""
cops_in_vision = len([c for c in self.neighbors if isinstance(c, Cop)])
actives_in_vision = 1.0 # citizen counts herself
for c in self.neighbors:
if (
isinstance(c, Citizen)
and c.condition == "Active"
and c.jail_sentence == 0
):
actives_in_vision += 1
self.arrest_probability = 1 - math.exp(
-1 * self.model.arrest_prob_constant * (cops_in_vision / actives_in_vision)
)


class Cop(EpsteinAgent):
"""
A cop for life. No defection.
Summary of rule: Inspect local vision and arrest a random active agent.

Attributes:
unique_id: unique int
x, y: Grid coordinates
vision: number of cells in each direction (N, S, E and W) that cop is
able to inspect
"""

def __init__(self, model, vision):
"""
Create a new Cop.
Args:
x, y: Grid coordinates
vision: number of cells in each direction (N, S, E and W) that
agent can inspect. Exogenous.
model: model instance
"""
super().__init__(model)
self.vision = vision

def step(self):
"""
Inspect local vision and arrest a random active agent. Move if
applicable.
"""
self.update_neighbors()
active_neighbors = []
for agent in self.neighbors:
if (
isinstance(agent, Citizen)
and agent.condition == "Active"
and agent.jail_sentence == 0
):
active_neighbors.append(agent)
if active_neighbors:
arrestee = self.random.choice(active_neighbors)
sentence = self.random.randint(0, self.model.max_jail_term)
arrestee.jail_sentence = sentence
arrestee.condition = "Quiescent"
if self.model.movement and self.empty_neighbors:
new_pos = self.random.choice(self.empty_neighbors)
self.move_to(new_pos)
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import mesa

from .agent import Citizen, Cop


class EpsteinCivilViolence(mesa.Model):
"""
Model 1 from "Modeling civil violence: An agent-based computational
approach," by Joshua Epstein.
http://www.pnas.org/content/99/suppl_3/7243.full
Attributes:
height: grid height
width: grid width
citizen_density: approximate % of cells occupied by citizens.
cop_density: approximate % of cells occupied by cops.
citizen_vision: number of cells in each direction (N, S, E and W) that
citizen can inspect
cop_vision: number of cells in each direction (N, S, E and W) that cop
can inspect
legitimacy: (L) citizens' perception of regime legitimacy, equal
across all citizens
max_jail_term: (J_max)
active_threshold: if (grievance - (risk_aversion * arrest_probability))
> threshold, citizen rebels
arrest_prob_constant: set to ensure agents make plausible arrest
probability estimates
movement: binary, whether agents try to move at step end
max_iters: model may not have a natural stopping point, so we set a
max.
"""

def __init__(
self,
width=40,
height=40,
citizen_density=0.7,
cop_density=0.074,
citizen_vision=7,
cop_vision=7,
legitimacy=0.8,
max_jail_term=1000,
active_threshold=0.1,
arrest_prob_constant=2.3,
movement=True,
max_iters=1000,
):
super().__init__()
self.width = width
self.height = height
self.citizen_density = citizen_density
self.cop_density = cop_density
self.citizen_vision = citizen_vision
self.cop_vision = cop_vision
self.legitimacy = legitimacy
self.max_jail_term = max_jail_term
self.active_threshold = active_threshold
self.arrest_prob_constant = arrest_prob_constant
self.movement = movement
self.max_iters = max_iters
self.iteration = 0

self.grid = mesa.experimental.cell_space.OrthogonalMooreGrid(
(width, height), capacity=1, torus=True
)

model_reporters = {
"Quiescent": lambda m: self.count_type_citizens(m, "Quiescent"),
"Active": lambda m: self.count_type_citizens(m, "Active"),
"Jailed": self.count_jailed,
"Cops": self.count_cops,
}
agent_reporters = {
"x": lambda a: a.cell.coordinate[0],
"y": lambda a: a.cell.coordinate[1],
"breed": lambda a: type(a).__name__,
"jail_sentence": lambda a: getattr(a, "jail_sentence", None),
"condition": lambda a: getattr(a, "condition", None),
"arrest_probability": lambda a: getattr(a, "arrest_probability", None),
}
self.datacollector = mesa.DataCollector(
model_reporters=model_reporters, agent_reporters=agent_reporters
)
if self.cop_density + self.citizen_density > 1:
raise ValueError("Cop density + citizen density must be less than 1")

for cell in self.grid.all_cells:
if self.random.random() < self.cop_density:
cop = Cop(self, vision=self.cop_vision)
cop.move_to(cell)

elif self.random.random() < (self.cop_density + self.citizen_density):
citizen = Citizen(
self,
hardship=self.random.random(),
regime_legitimacy=self.legitimacy,
risk_aversion=self.random.random(),
threshold=self.active_threshold,
vision=self.citizen_vision,
)
citizen.move_to(cell)

self.running = True
self.datacollector.collect(self)

def step(self):
"""
Advance the model by one step and collect data.
"""
self.agents.shuffle_do("step")
# collect data
self.datacollector.collect(self)
self.iteration += 1
if self.iteration > self.max_iters:
self.running = False

@staticmethod
def count_type_citizens(model, condition, exclude_jailed=True):
"""
Helper method to count agents by Quiescent/Active.
"""
citizens = model.agents_by_type[Citizen]

if exclude_jailed:
return len(
[
c
for c in citizens
if (c.condition == condition) and (c.jail_sentence == 0)
]
)
else:
return len([c for c in citizens if c.condition == condition])

@staticmethod
def count_jailed(model):
"""
Helper method to count jailed agents.
"""
return len([a for a in model.agents_by_type[Citizen] if a.jail_sentence > 0])

@staticmethod
def count_cops(model):
"""
Helper method to count jailed agents.
"""
return len(model.agents_by_type[Cop])
Loading
Loading