From b85986723a31897a797ff19fb93403e792d1decb Mon Sep 17 00:00:00 2001 From: Nandha-kumar-S Date: Sat, 28 Feb 2026 14:36:25 +0530 Subject: [PATCH 1/5] Add traffic_flow example --- examples/traffic_flow/README.md | 84 +++++++++++++++++++++ examples/traffic_flow/app.py | 59 +++++++++++++++ examples/traffic_flow/run.py | 20 +++++ examples/traffic_flow/traffic_flow/agent.py | 15 ++++ examples/traffic_flow/traffic_flow/model.py | 23 ++++++ 5 files changed, 201 insertions(+) create mode 100644 examples/traffic_flow/README.md create mode 100644 examples/traffic_flow/app.py create mode 100644 examples/traffic_flow/run.py create mode 100644 examples/traffic_flow/traffic_flow/agent.py create mode 100644 examples/traffic_flow/traffic_flow/model.py diff --git a/examples/traffic_flow/README.md b/examples/traffic_flow/README.md new file mode 100644 index 000000000..520b0cdf1 --- /dev/null +++ b/examples/traffic_flow/README.md @@ -0,0 +1,84 @@ +# Traffic Flow (Mesa ABM) + +A minimal traffic flow agent-based model built with the Mesa framework (tested with Mesa `3.3.1`). + +The model uses a discrete grid world with cars that move only from left to right. When a car reaches the right edge, it wraps back to the left using a torus grid. + +## What this model does + +- The world is a `width x height` `MultiGrid` with `torus=True`. +- Cars are placed randomly at initialization (one car per cell). +- Each simulation step (a “tick”), every car attempts to move one cell to the right: + - If the cell is empty, it moves. + - If the cell is occupied, it waits. + +## Core rules / logic + +- **Movement check**: a car checks the cell at `(x+1, y)`. +- **Wrap-around**: since the grid is a torus, moving past the right edge returns to the left edge. +- **No overtaking**: cars do not change lanes or move diagonally (future feature). + +## Project structure + +- `traffic_flow/agent.py` + - `CarAgent`: defines movement behavior (`move`) and agent step (`step`). +- `traffic_flow/model.py` + - `TraficFlow`: creates the grid, places cars, and advances the model. +- `run.py` + - CLI runner that prints the grid as ASCII each tick. +- `app.py` + - Solara UI entrypoint using `mesa.visualization.SolaraViz`. + +## Requirements + +- Python 3.8+ +- Mesa 3.x (this was built with Mesa `3.3.1`) +- Solara (only required for UI) + +Install (example): + +```bash +pip install "mesa[rec]" solara +``` + +## How to run (CLI) + +From the `examples/traffic_flow` directory: + +```bash +python run.py +``` + +### Output format + +- `C` = a car occupies the cell +- `.` = empty cell + +Each printed block corresponds to one simulation step (a “tick”). + +## How to run (Solara UI) + +From the `examples/traffic_flow` directory: + +```bash +solara run app.py +``` + +Open the URL shown in your terminal. + +### Using the sliders + +The parameter widgets typically apply to the next model instance. After changing sliders, use the UI control to reset/restart the model so it rebuilds with the new parameters. + +## Model parameters + +- `width` (int): grid width +- `height` (int): grid height +- `n_cars` (int): number of cars placed initially +- `seed` (int/str): random seed passed to the Mesa model + +### Practical limits + +- `n_cars` should be less than or equal to `width * height`. + - If `n_cars` is close to `width * height`, initialization can slow down because the model searches repeatedly for empty cells. + diff --git a/examples/traffic_flow/app.py b/examples/traffic_flow/app.py new file mode 100644 index 000000000..87ebb2f2d --- /dev/null +++ b/examples/traffic_flow/app.py @@ -0,0 +1,59 @@ +from mesa.visualization import SolaraViz, make_space_component +from traffic_flow.model import TraficFlow + + +def car_portrayal(agent): + if agent is None or agent.pos is None: + return + return { + "color": "#1f77b4", + "size": 50, + } + + +space_component = make_space_component( + car_portrayal, + draw_grid=True, +) + +model_params = { + "width": { + "type": "SliderInt", + "value": 20, + "label": "Width", + "min": 5, + "max": 60, + "step": 1, + }, + "height": { + "type": "SliderInt", + "value": 5, + "label": "Height", + "min": 2, + "max": 20, + "step": 1, + }, + "n_cars": { + "type": "SliderInt", + "value": 20, + "label": "Number of cars", + "min": 1, + "max": 200, + "step": 1, + }, + "seed": { + "type": "InputText", + "value": 1, + "label": "Seed", + }, +} + +model = TraficFlow(width=20, height=5, n_cars=20, seed=1) + +page = SolaraViz( + model, + components=[space_component], + model_params=model_params, + name="Traffic Flow", +) +page # noqa \ No newline at end of file diff --git a/examples/traffic_flow/run.py b/examples/traffic_flow/run.py new file mode 100644 index 000000000..6370538c9 --- /dev/null +++ b/examples/traffic_flow/run.py @@ -0,0 +1,20 @@ +from traffic_flow.model import TraficFlow + +def render_grid(model): + lines = [] + for y in reversed(range(model.grid.height)): + row = [] + for x in range(model.grid.width): + cell_agents = model.grid.get_cell_list_contents((x, y)) + row.append("C" if cell_agents else ".") + lines.append("".join(row)) + return "\n".join(lines) + +if __name__ == "__main__": + model = TraficFlow(width=20, height=5, n_cars=20, seed=1) + + for t in range(10): + print(f"tick {t}") + print(render_grid(model)) + print() + model.step() \ No newline at end of file diff --git a/examples/traffic_flow/traffic_flow/agent.py b/examples/traffic_flow/traffic_flow/agent.py new file mode 100644 index 000000000..ec1affcf2 --- /dev/null +++ b/examples/traffic_flow/traffic_flow/agent.py @@ -0,0 +1,15 @@ +from mesa import Agent + +class CarAgent(Agent): + def __init__(self, model): + super().__init__(model) + + def move(self): + x,y = self.pos + new_pos = ((x + 1) % self.model.grid.width, y) + if self.model.grid.is_cell_empty(new_pos): + self.model.grid.move_agent(self, new_pos) + + def step(self): + self.move() + diff --git a/examples/traffic_flow/traffic_flow/model.py b/examples/traffic_flow/traffic_flow/model.py new file mode 100644 index 000000000..5ede9ed2d --- /dev/null +++ b/examples/traffic_flow/traffic_flow/model.py @@ -0,0 +1,23 @@ +from mesa import Model +from mesa.space import MultiGrid + +from .agent import CarAgent + +class TraficFlow(Model): + def __init__(self, width=20, height=5, n_cars=10, seed=None): + super().__init__(seed=seed) + + self.grid = MultiGrid(width, height, torus=True) + + for _ in range(n_cars): + car = CarAgent(self) + + x = self.random.randrange(self.grid.width) + y = self.random.randrange(self.grid.height) + while not self.grid.is_cell_empty((x, y)): + x = self.random.randrange(self.grid.width) + y = self.random.randrange(self.grid.height) + self.grid.place_agent(car, (x, y)) + + def step(self): + self.agents.shuffle_do("step") \ No newline at end of file From 0eaa400891486aeba1d10030566bffc684d95632 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 28 Feb 2026 09:16:13 +0000 Subject: [PATCH 2/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/traffic_flow/app.py | 2 +- examples/traffic_flow/run.py | 4 +++- examples/traffic_flow/traffic_flow/agent.py | 6 +++--- examples/traffic_flow/traffic_flow/model.py | 9 +++++---- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/examples/traffic_flow/app.py b/examples/traffic_flow/app.py index 87ebb2f2d..268c3a88a 100644 --- a/examples/traffic_flow/app.py +++ b/examples/traffic_flow/app.py @@ -56,4 +56,4 @@ def car_portrayal(agent): model_params=model_params, name="Traffic Flow", ) -page # noqa \ No newline at end of file +page # noqa diff --git a/examples/traffic_flow/run.py b/examples/traffic_flow/run.py index 6370538c9..1e5ed8f95 100644 --- a/examples/traffic_flow/run.py +++ b/examples/traffic_flow/run.py @@ -1,5 +1,6 @@ from traffic_flow.model import TraficFlow + def render_grid(model): lines = [] for y in reversed(range(model.grid.height)): @@ -10,6 +11,7 @@ def render_grid(model): lines.append("".join(row)) return "\n".join(lines) + if __name__ == "__main__": model = TraficFlow(width=20, height=5, n_cars=20, seed=1) @@ -17,4 +19,4 @@ def render_grid(model): print(f"tick {t}") print(render_grid(model)) print() - model.step() \ No newline at end of file + model.step() diff --git a/examples/traffic_flow/traffic_flow/agent.py b/examples/traffic_flow/traffic_flow/agent.py index ec1affcf2..a9802e1b2 100644 --- a/examples/traffic_flow/traffic_flow/agent.py +++ b/examples/traffic_flow/traffic_flow/agent.py @@ -1,15 +1,15 @@ from mesa import Agent + class CarAgent(Agent): def __init__(self, model): super().__init__(model) - + def move(self): - x,y = self.pos + x, y = self.pos new_pos = ((x + 1) % self.model.grid.width, y) if self.model.grid.is_cell_empty(new_pos): self.model.grid.move_agent(self, new_pos) def step(self): self.move() - diff --git a/examples/traffic_flow/traffic_flow/model.py b/examples/traffic_flow/traffic_flow/model.py index 5ede9ed2d..ec322c5a6 100644 --- a/examples/traffic_flow/traffic_flow/model.py +++ b/examples/traffic_flow/traffic_flow/model.py @@ -1,12 +1,13 @@ from mesa import Model from mesa.space import MultiGrid - + from .agent import CarAgent - + + class TraficFlow(Model): def __init__(self, width=20, height=5, n_cars=10, seed=None): super().__init__(seed=seed) - + self.grid = MultiGrid(width, height, torus=True) for _ in range(n_cars): @@ -20,4 +21,4 @@ def __init__(self, width=20, height=5, n_cars=10, seed=None): self.grid.place_agent(car, (x, y)) def step(self): - self.agents.shuffle_do("step") \ No newline at end of file + self.agents.shuffle_do("step") From ad19d4d3762fce6b697154a1be20eb34bbb0d88e Mon Sep 17 00:00:00 2001 From: Nandha-kumar-S Date: Mon, 2 Mar 2026 16:14:19 +0530 Subject: [PATCH 3/5] fix-readme-run-instructions --- examples/color_patches/Readme.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/color_patches/Readme.md b/examples/color_patches/Readme.md index 22f6639d8..fc62c48de 100644 --- a/examples/color_patches/Readme.md +++ b/examples/color_patches/Readme.md @@ -18,13 +18,13 @@ An agent's state represents its "opinion" and is shown by the color of the cell ## How to Run -To run the model interactively, run ``mesa runserver` in this directory. e.g. +To run the model interactively, run ``solara run`` in this directory. e.g. -``` - $ mesa runserver +```bash + $ solara run app.py ``` -Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press Reset, then Run. +Then open your browser to [http://localhost:8765/](http://localhost:8765/) and press Reset, then Run. ## Files From d6760029ed46df0e60c54a267477d8e58348234e Mon Sep 17 00:00:00 2001 From: Nandha-kumar-S Date: Fri, 6 Mar 2026 13:03:38 +0530 Subject: [PATCH 4/5] Migrate from 'MultiGrid' to 'OrthogonalMooreGrid' --- examples/traffic_flow/README.md | 4 +++- examples/traffic_flow/traffic_flow/agent.py | 11 ++++++++--- examples/traffic_flow/traffic_flow/model.py | 17 +++++++++-------- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/examples/traffic_flow/README.md b/examples/traffic_flow/README.md index 520b0cdf1..1d4697102 100644 --- a/examples/traffic_flow/README.md +++ b/examples/traffic_flow/README.md @@ -2,11 +2,13 @@ A minimal traffic flow agent-based model built with the Mesa framework (tested with Mesa `3.3.1`). +The model uses Mesa 3.x's modern `OrthogonalMooreGrid` for discrete space representation, providing the latest Mesa API features. + The model uses a discrete grid world with cars that move only from left to right. When a car reaches the right edge, it wraps back to the left using a torus grid. ## What this model does -- The world is a `width x height` `MultiGrid` with `torus=True`. +- The world is a `width x height` `OrthogonalMooreGrid`. - Cars are placed randomly at initialization (one car per cell). - Each simulation step (a “tick”), every car attempts to move one cell to the right: - If the cell is empty, it moves. diff --git a/examples/traffic_flow/traffic_flow/agent.py b/examples/traffic_flow/traffic_flow/agent.py index ec1affcf2..cc9d0d3b2 100644 --- a/examples/traffic_flow/traffic_flow/agent.py +++ b/examples/traffic_flow/traffic_flow/agent.py @@ -6,9 +6,14 @@ def __init__(self, model): def move(self): x,y = self.pos - new_pos = ((x + 1) % self.model.grid.width, y) - if self.model.grid.is_cell_empty(new_pos): - self.model.grid.move_agent(self, new_pos) + new_pos = ((x + 1) % self.model.grid.dimensions[0], y) + if len(self.model.grid._cells[new_pos].agents) == 0: + # Remove from current cell + self.model.grid._cells[self.pos].remove_agent(self) + # Add to new cell + self.model.grid._cells[new_pos].add_agent(self) + # Update agent position + self.pos = new_pos def step(self): self.move() diff --git a/examples/traffic_flow/traffic_flow/model.py b/examples/traffic_flow/traffic_flow/model.py index 5ede9ed2d..12bfca07f 100644 --- a/examples/traffic_flow/traffic_flow/model.py +++ b/examples/traffic_flow/traffic_flow/model.py @@ -1,5 +1,5 @@ from mesa import Model -from mesa.space import MultiGrid +from mesa.discrete_space import OrthogonalMooreGrid from .agent import CarAgent @@ -7,17 +7,18 @@ class TraficFlow(Model): def __init__(self, width=20, height=5, n_cars=10, seed=None): super().__init__(seed=seed) - self.grid = MultiGrid(width, height, torus=True) + self.grid = OrthogonalMooreGrid([width, height], torus=True, random=self.random) for _ in range(n_cars): car = CarAgent(self) - x = self.random.randrange(self.grid.width) - y = self.random.randrange(self.grid.height) - while not self.grid.is_cell_empty((x, y)): - x = self.random.randrange(self.grid.width) - y = self.random.randrange(self.grid.height) - self.grid.place_agent(car, (x, y)) + x = self.random.randrange(self.grid.dimensions[0]) + y = self.random.randrange(self.grid.dimensions[1]) + while len(self.grid._cells[(x, y)].agents) > 0: + x = self.random.randrange(self.grid.dimensions[0]) + y = self.random.randrange(self.grid.dimensions[1]) + self.grid._cells[(x, y)].add_agent(car) + car.pos = (x, y) def step(self): self.agents.shuffle_do("step") \ No newline at end of file From 3740dd2eda795ece8897083462d6f11010c8ba5b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 6 Mar 2026 07:40:06 +0000 Subject: [PATCH 5/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/traffic_flow/traffic_flow/agent.py | 2 +- examples/traffic_flow/traffic_flow/model.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/traffic_flow/traffic_flow/agent.py b/examples/traffic_flow/traffic_flow/agent.py index 0d9f4952f..7ab6b6afc 100644 --- a/examples/traffic_flow/traffic_flow/agent.py +++ b/examples/traffic_flow/traffic_flow/agent.py @@ -6,7 +6,7 @@ def __init__(self, model): super().__init__(model) def move(self): - x,y = self.pos + x, y = self.pos new_pos = ((x + 1) % self.model.grid.dimensions[0], y) if len(self.model.grid._cells[new_pos].agents) == 0: # Remove from current cell diff --git a/examples/traffic_flow/traffic_flow/model.py b/examples/traffic_flow/traffic_flow/model.py index 66fbc06f7..63fe0aa79 100644 --- a/examples/traffic_flow/traffic_flow/model.py +++ b/examples/traffic_flow/traffic_flow/model.py @@ -1,13 +1,13 @@ from mesa import Model from mesa.discrete_space import OrthogonalMooreGrid - + from .agent import CarAgent class TraficFlow(Model): def __init__(self, width=20, height=5, n_cars=10, seed=None): super().__init__(seed=seed) - + self.grid = OrthogonalMooreGrid([width, height], torus=True, random=self.random) for _ in range(n_cars):