Add needs-based village example: homeostatic agents with competing drives#439
Add needs-based village example: homeostatic agents with competing drives#439darraghmoran2025 wants to merge 2 commits intomesa:mainfrom
Conversation
…ives
N villagers on a Moore grid each manage four competing homeostatic needs
(HUNGER, REST, SOCIAL, SAFETY). Each step the villager acts on whichever
need is most urgent, moving toward the relevant resource or partner and
satisfying it on arrival.
Demonstrates a third decision-making paradigm alongside the reactive and
BDI examples already in the repo: internal drives that push agents rather
than goals that pull them, with continuous priority preemption.
New classes:
NeedSpec — dataclass configuring one homeostatic need
NeedsAgent — abstract CellAgent with decay, urgency ordering,
preemption counting (Pain Point mesa#16)
VillagerAgent — four-need concrete agent; greedy Manhattan movement
FoodSource — regenerating food patch (resource competition)
HomePatch — stationary rest site
ThreatAgent — random-walk predator; spikes SAFETY via perception
VillageModel — OrthogonalMooreGrid, DataCollector, Solara app
for more information, see https://pre-commit.ci
TomRodger
left a comment
There was a problem hiding this comment.
Overall, the model logic is sound and runs without errors. The architecture is genuinely interesting, the NeedSpec dataclass, and NeedsAgent abstraction are clean and reusable, and the Pain Points documentation is well written and directly useful for the Behavioural Framework project.
| # ──────────────────────────────────────────────────────────────────────── # | ||
|
|
||
|
|
||
| class FoodSource(CellAgent): |
There was a problem hiding this comment.
FoodSource is a stationary environment object that never moves after placement. The Mesa convention for stationary agents is FixedAgent, rather than CellAgent (same as GrassPatch in Mesa's built-in Sugarscape example). Using CellAgent works but semantically, it is incorrect and could affect Mesa's internal agent tracking.
There was a problem hiding this comment.
Thanks for the feedback. I've updated both FoodSource and
HomePatch to inherit from FixedAgent instead of CellAgent.
You're right that CellAgent worked functionally, but the
semantics were wrong — both are environment objects placed
once at initialisation and never moved, which is exactly what
FixedAgent is designed for. Using CellAgent implied they were
mobile agents, which could cause issues with Mesa's internal
agent tracking down the line.
The fix mirrors the convention used in Mesa's own Sugarscape
example (GrassPatch inherits from FixedAgent), so it aligns
with the established pattern in the framework.
| return self.food == 0 | ||
|
|
||
|
|
||
| class HomePatch(CellAgent): |
| # Solara app # | ||
| # ──────────────────────────────────────────────────────────────────────── # | ||
|
|
||
| page = SolaraViz( |
There was a problem hiding this comment.
SolaraViz on Mesa 3.5.1 expects a model instance rather than the class. Passing VillageModel directly causes the visualisation to fail with [AttributeError: type object 'VillageModel' has no attribute 'datacollector']. Fix: you instantiate the model first and then pass it in e.g. model = VillageModel(), then pass model to SolaraViz
There was a problem hiding this comment.
Good catch. I've updated app.py to instantiate VillageModel before passing it to SolaraViz:
model = VillageModel()
page = SolaraViz(
model,
components=[SpaceComponent, NeedsChart, ActiveNeedChart, PreemptionChart],
model_params=model_params,
name="Needs-Based Village",
)
Passing the class directly caused the AttributeError because SolaraViz in Mesa 3.5.1 immediately tries to access instance attributes like datacollector on whatever is passed in. The
interactive sliders in model_params still work — SolaraViz handles re-instantiation when parameters change.
| @@ -0,0 +1 @@ | |||
| mesa[viz]>=3.0 | |||
There was a problem hiding this comment.
mesa[viz]>=3.0 is unspecific. When running the Solara visualisation in Mesa 3.5.1 it crashes with an AttributeError in mesa_signals/core.py. The model logic itself runs cleanly. Naming a specific model or stricter constraints would help improve reproducibility.
There was a problem hiding this comment.
Agreed. I've tightened the constraint in requirements.txt to mesa[viz]>=3.5.1,<4.0.
The >=3.0 bound was too loose — any version in the 3.x range would satisfy it, but earlier releases hit the AttributeError in mesa_signals/core.py you flagged. Pinning to >=3.5.1
targets the version the example was developed and tested against, and <4.0 guards against breaking changes in a future major release.
|
|
||
|
|
||
| @dataclass | ||
| class NeedSpec: |
There was a problem hiding this comment.
The NeedSpec dataclass is a clean design choice here. Keeping need configuration separate from the agent logic will make it easy for someone to extend the code, only needing to touch one place when tweaking decay rates, or adding a fifth. It also makes VillagerAgent read much cleaner as a result.
| paradigm alongside reactive agents and BDI deliberation. Needs are | ||
| *pushed* by autonomous decay; they can only be suppressed, never eliminated. | ||
|
|
||
| Pain Points Surfaced |
There was a problem hiding this comment.
Good pain points documentation, labelling the workarounds with numbers and connecting them to specific limitations is very useful evidence for the Behavioural Framework case.
There was a problem hiding this comment.
Hi Tom sorry for the inactivity on my end. College has been exceptionally busy. Hopefully with exams over soon I will have more time. I have responded to all of your queries and pushed the relevant changes. Hope that is acceptable. Thank you!
What this adds
A population of villagers on a Moore grid each manage four competing homeostatic needs — HUNGER, REST, SOCIAL, and SAFETY. Each step, each villager acts on whichever need is most urgent, moving toward the relevant resource or partner and satisfying it on arrival.
This demonstrates a needs-based (drive-reduction) decision-making architecture — a distinct paradigm from both the reactive agents (Schelling, WolfSheep) and BDI deliberation examples already in the repo.
New classes
NeedSpecNeedsAgentCellAgentsubclass: need decay, urgency ordering, preemption countingVillagerAgentFoodSourceHomePatchThreatAgentVillageModelOrthogonalMooreGrid,DataCollector, Solara visualisationArchitecture contrast
Running
Test plan
solara run app.pyopens and grid renders with colour-coded villagersThreats = 0runs without errorfrom needs_village import VillageModel; m = VillageModel(); m.step()runs cleanlyNote: PR #432 also explores needs-based drives, but extends the existing Wolf-Sheep model with two drives (hunger, fear) and no shared base class. This PR takes a different approach: an original scenario, four competing needs including a perception-driven SAFETY need and a mutual SOCIAL need, and a reusable abstract NeedsAgent / NeedSpec architecture that any model could subclass.