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 diff --git a/examples/traffic_flow/README.md b/examples/traffic_flow/README.md new file mode 100644 index 000000000..1d4697102 --- /dev/null +++ b/examples/traffic_flow/README.md @@ -0,0 +1,86 @@ +# 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 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` `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. + - 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..268c3a88a --- /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 diff --git a/examples/traffic_flow/run.py b/examples/traffic_flow/run.py new file mode 100644 index 000000000..1e5ed8f95 --- /dev/null +++ b/examples/traffic_flow/run.py @@ -0,0 +1,22 @@ +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() diff --git a/examples/traffic_flow/traffic_flow/agent.py b/examples/traffic_flow/traffic_flow/agent.py new file mode 100644 index 000000000..7ab6b6afc --- /dev/null +++ b/examples/traffic_flow/traffic_flow/agent.py @@ -0,0 +1,20 @@ +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.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 new file mode 100644 index 000000000..63fe0aa79 --- /dev/null +++ b/examples/traffic_flow/traffic_flow/model.py @@ -0,0 +1,25 @@ +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): + car = CarAgent(self) + + 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")