A collection of agent-based models exploring cooperation, altruism, and eco-evolutionary dynamics.
The current evolved-cooperation examples in this repo are:
ecological_models/spatial_altruism/: a minimal spatial altruism modelecological_models/cooperative_hunting/: a spatial predator-prey-grass cooperative-hunting modelecological_models/spatial_prisoners_dilemma/: a spatial Prisoner's Dilemma ecology with local play, movement, reproduction, and inherited same-vs-other strategy encodingsecological_models/retained_benefit/: a lattice model that tests how much cooperative benefit must be routed back toward cooperators or their copies before cooperation can spread
Additional experimental module in this repo:
behaviorally_anchored_model/: an integrated human-cooperation model with reputation, norms, band territory, kin/spouse/household structure, local movement, child rearing, social learning, and density ecology in one demographic simulationmoran_models/interaction_kernel/: a general interaction-kernel engine with explicit positive/negative routing and pluggable selection dynamics that can be used to instantiate mechanisms such as kin selectionmoran_models/nowak_mechanisms/direct_reciprocity/scaffolds/: direct-reciprocity scaffold experiments, including discrete spatial clustering and continuous spatial partner-memory routing
EvolvedCooperation contains the canonical Python implementations for
website-facing cooperation models where cooperation changes through evolutionary
dynamics alone. Lifetime learning is intentionally out of scope here and is
handled in the companion repositories LearnedCooperation and
EvolvedAndLearnedCooperation.
The cooperation model repositories are separated by mechanism:
EvolvedCooperation: evolutionary dynamics only; lifetime learning is out of scope.LearnedCooperation: lifetime learning only; evolutionary change is out of scope.EvolvedAndLearnedCooperation: coupled evolutionary and lifetime-learning dynamics.
The public website https://humanbehaviorpatterns.org/ is built from the
sibling human-cooperation-site repo, so the matching website pages should
stay faithful to the Python implementations here.
For detailed cross-repo mapping and repository maintenance notes, see
AGENTS.md.
This repo uses a project-local Conda environment stored at .conda/ so it travels with the workspace and VS Code can auto-select it.
- Interpreter path:
/EvolvedCooperation/.conda/bin/python - VS Code setting: see
.vscode/settings.json(we setpython.defaultInterpreterPath, point VS Code at the local Conda executable, and use a repo-specific terminal profile instead of fixed-script launch entries) - Matplotlib cache/config path for VS Code runs:
.vscode/.envsetsMPLCONFIGDIR=.matplotlib - Ruff editor linting: install Ruff into the project environment with
./.conda/bin/python -m pip install ruff - Pylance note:
.vscode/settings.jsondisablesreportMissingModuleSourceso compiled Matplotlib modules do not produce false-positive import warnings in editor diagnostics
The workspace is configured so VS Code uses the repo-local .conda deterministically:
Terminal => New Terminalopensbash (.conda), which sources the normal shell startup and then activates/EvolvedCooperation/.conda.Run => Run Without Debugginguses.vscode/launch.jsonplus.vscode/run_active_python.pyto inspect the active editor file.- If the active file lives inside a Python package in the repo, the helper runs it with module semantics (
runpy.run_module(...)), which matchespython -m ...from the repo root and satisfies module-only guards. - If the active file is not inside a package, the helper falls back to normal script execution (
runpy.run_path(...)). - The launch config still forces
${workspaceFolder}/.conda/bin/python, so runs do not depend on whichever interpreter VS Code happened to remember previously.
The most actively documented ecology model in the repo lives in
ecological_models/cooperative_hunting/.
- Main runtime:
ecological_models/cooperative_hunting/cooperative_hunting.py - Active parameters:
ecological_models/cooperative_hunting/config/cooperative_hunting_config.py - Detailed model notes and theory mapping:
ecological_models/cooperative_hunting/README.md
Current mechanics in that model:
- predators carry a heritable continuous hunt investment trait
hunt_investment_trait in [0,1] - hunt contribution is
predator_energy * hunt_investment_trait - predator cooperation cost is paid directly as
predator_cooperation_cost_per_unit * hunt_investment_trait - the config file now uses descriptive canonical parameter names, while legacy short aliases remain accepted for backward compatibility
- optional plasticity has been removed from the active code path, so the stored trait is the value used for hunting and cost
Browser replay preview:
Click the full-window animation preview to open the GitHub Pages replay viewer.
Project convention for this model:
- run from repo root with
./.conda/bin/python
Minimal run example:
./.conda/bin/python -m ecological_models.cooperative_hunting.cooperative_huntingTaken together, the four current website-facing evolved-cooperation modules do not support a strong claim that cooperation simply appears by default. They support a more specific claim: cooperation persists only when the update rules and ecology give cooperators some protection against immediate exploitation.
A useful near-universal formulation is: cooperation evolves when the benefits created by cooperation flow back to cooperators, or to copies of the cooperative rule, strongly enough to outweigh the private cost. In shorthand: there is no cooperation without feedback.
The ecological_models/retained_benefit/ module is the repo's most direct attempt to test
that claim in a deliberately abstract form.
Shared pattern across the current models:
- There must be heritable variation in a cooperative trait or strategy.
- Interactions must be local enough that cooperative benefits are not spread completely at random.
- Some feedback mechanism must return enough of the cooperative benefit back toward cooperators.
- Reproduction and turnover must allow successful local structures to spread.
- The private cost of cooperation must stay low enough relative to the protected benefit.
The four modules implement that protection in different ways:
ecological_models/spatial_altruism/: local clustering plus void competition and disturbance can support altruist-selfish coexistenceecological_models/spatial_prisoners_dilemma/: conditional reciprocity can outperform pure defection, but it still yields coexistence rather than universal cooperationecological_models/cooperative_hunting/: costly cooperation can pay when coordinated hunting creates real synergy, but the current active baseline is a supported-start threshold-synergy case rather than a pure de novo emergence testecological_models/retained_benefit/: cooperation rises only when enough of the benefit it creates is routed back toward cooperators or their copies rather than leaking broadly to free-riders
Mapping the current models onto Nowak's five canonical mechanisms:
| Model | Primary Nowak mechanism | Secondary or adjacent | Not implemented |
|---|---|---|---|
ecological_models/spatial_altruism/ |
Network reciprocity | Group-selection-adjacent spatial patch effects | Direct reciprocity, indirect reciprocity, explicit kin selection |
ecological_models/spatial_prisoners_dilemma/ |
Direct reciprocity and network reciprocity | Local assortment through lattice position | Indirect reciprocity, explicit kin selection, explicit group reproduction |
ecological_models/cooperative_hunting/ |
Not a clean single Nowak mechanism | Network reciprocity, byproduct mutualism, partner-like ecological feedback | Reputation-based indirect reciprocity, explicit kin selection |
ecological_models/retained_benefit/ |
Generalized assortment and feedback | Kin-selection-like lineage routing, network reciprocity through local neighborhoods | Memory-based direct reciprocity, reputation-based indirect reciprocity |
moran_models/interaction_kernel/ |
General engine, not a single mechanism | Kin-weighted routing, network-local routing, mixed help-harm | Mechanism-specific memory and reputation unless added as wrappers |
The five explicit Moran wrappers for Nowak's mechanisms live under moran_models/nowak_mechanisms/.
The additional direct_reciprocity/scaffolds/ packages are direct-reciprocity
scaffold examples rather than pure well-mixed baselines.
For emergence from rarity, the mechanisms differ in fundamentality because they create positive assortment in different ways. The useful ordering is:
- Kin selection — genetic relatedness can favor helping from the start when enough benefit returns to copies of the cooperative rule.
- Network reciprocity — spatial or graph clustering protects cooperative neighborhoods.
- Group selection — group boundaries and between-group competition can favor cooperative groups despite within-group free riding.
- Indirect reciprocity — reputation can scale cooperation among non-kin, but it requires observation, assessment, and memory.
- Direct reciprocity — repeated interaction is strongest as amplification and maintenance; pure direct reciprocity has the hardest startup problem.
This is not a claim that kin selection is always the only or largest mechanism in real systems. It is a claim about de novo emergence: kin, network, and group mechanisms supply assortment earlier, while indirect and direct reciprocity need more social-information or partner-history structure before they become reliable.
The non-direct Nowak wrappers were tested in isolation with:
./.conda/bin/python -m moran_models.interaction_kernel.utils.compare_emergence_baselinesStepwise impact:
- The new utility compares indirect reciprocity, network reciprocity, group selection, and kin selection from the same low-start condition.
- The run used initial mean cooperation trait about
0.119,B_plus_scale = [0.8, 1.0, 1.2],C_scale = [0.10, 0.20, 0.30], eight seeds per cell, and 250 steps. - Emergence success means final mean trait at least
0.50and a gain of at least0.05from the low start. - Indirect reciprocity succeeded in all 9 tested cells under the current reputation-weighted implementation.
- Kin selection succeeded fully in 2 cells and partially in 3 more; network reciprocity and group selection each succeeded fully in 1 cell and partially in 2 more.
- Network and group selection were strongly cost-thresholded, while kin selection retained more intermediate growth at moderate cost.
Artifacts:
moran_models/interaction_kernel/data/compare_emergence_baselines_20260508_134242_summary.csvmoran_models/interaction_kernel/data/compare_emergence_baselines_20260508_134242_replicates.csvmoran_models/interaction_kernel/data/compare_emergence_baselines_20260508_134242_summary.txt
So the strongest repo-level conclusion at this stage is modest:
- the minimal conditions are not one magic parameter, but a bundle of assortment, feedback, inheritance, and a favorable cost-benefit ratio
- without such protection, selfish behavior usually wins
- with it, cooperation can persist, spread, or coexist, depending on the mechanism
- these models are mechanism-level demonstrations, not a universal law of the evolution of cooperation
- Description: Integrated population model that gives agents reputation sensitivity, norm enforcement, band identity, kin recognition, spouse bonds, households, child rearing, spatial awareness, direct-reciprocity bond fidelity, and social learning at the same time. The model is now oriented toward a more realistic hunter-gatherer starting point rather than a strict abstract Nowak-mechanism decomposition.
- Website counterpart: the historical framing page is https://humanbehaviorpatterns.org/history-of-human-cooperation-and-competition. That page defines the behaviorally anchored target capacities; this package is the canonical Python implementation for the small-scale family/foraging-band layer. The model intentionally does not implement later institutional layers such as agriculture, states, markets, law, money, writing, or bureaucracy. Territorial competition is therefore modeled as soft hunter-gatherer avoidance and scarcity-gated displacement, not as fixed property defense.
- Files:
behaviorally_anchored_model/behaviorally_anchored_model.py: core runtime, interaction routing, demographic update, and summary outputbehaviorally_anchored_model/config/behaviorally_anchored_config.py: active runtime parameters and proof scenariosbehaviorally_anchored_model/utils/live_viewer.py: Pygame scatter viewer for spatial position, trait color, band color, reciprocity bonds, and summary metricsbehaviorally_anchored_model/utils/proof_of_mechanism.py: scenario proof utility for the integrated model
- Usage:
- Edit parameters in
behaviorally_anchored_model/config/behaviorally_anchored_config.py - Run:
./.conda/bin/python -m behaviorally_anchored_model.behaviorally_anchored_model
- Run live viewer:
./.conda/bin/python -m behaviorally_anchored_model.utils.live_viewer
- Edit parameters in
- Current status:
- cooperation is represented by a continuous inherited
helping_trait - social learning is represented separately as a bounded within-lifetime
learned_helping_adjustment; actions use effective helpingclamp01(helping_trait + learned_helping_adjustment), while offspring inherit only the genetichelping_trait - the viewer separates mean helping trait from the frequency above the
invasion threshold, so
Trait >= 0.10 = 100%can coexist with a mean trait near0.25 - the viewer now also reports realized help: the fraction of actual helping
opportunities in the latest step that produced help
group_idnow stores concrete band membership: bands have territory centers, weak member attraction, migration, fission/fusion, inter-band marriage, soft territorial avoidance, territorial exclusion, and lossy resource raids- life history now separates dependent juveniles, non-reproductive
subadults, reproductive adults, and elders; default biological adulthood
starts at age
18 - agents now move during life through a stage-specific local random walk, so spatial neighborhoods change through both movement and birth/death turnover
- child rearing is now explicit: adults and elders can invest costly care in nearby juveniles, with parent and kin relatedness biasing who receives care
- reproductive co-parents can form persistent spouse bonds; spouse bonds can
bias repeat mating, seed local reciprocity bonds, and strengthen care for
shared nearby children
- households now act as co-resident family/camp units with a residence point; members are pulled toward that residence, juveniles can receive a household survival bonus from living parents and adult caregivers, and maturing juveniles can disperse into new households
- local grass is now explicit: adults and elders harvest from nearby grass cells instead of receiving guaranteed adult food energy, and parents can pass surplus harvested food to their own nearby juveniles
- reciprocity bonds are spatially local: new bonds form only within the configured formation radius, and existing bonds dissolve if movement carries bondmates beyond the configured dissolution radius
- population regulation now uses soft density pressure rather than a hard random trim to exactly the target population
- cooperation is represented by a continuous inherited
Stepwise impact:
- The former
top_down_model/package was renamed tobehaviorally_anchored_model/to match the public website's behaviorally anchored terminology. - The core runtime file was renamed from
top_down_model.pytobehaviorally_anchored_model.py, and the active config file was renamed fromtop_down_config.pytobehaviorally_anchored_config.py. - Runtime commands now use:
./.conda/bin/python -m behaviorally_anchored_model.behaviorally_anchored_model. - The model now references the website counterpart page directly: https://humanbehaviorpatterns.org/history-of-human-cooperation-and-competition.
- The website page now links back to the canonical implementation path in this repository, so page and model can be checked against each other.
- Social learning is now explicit: subadults, adults, and elders can copy nearby adult/elder demonstrators weighted by reputation and energetic success.
- Social learning updates
learned_helping_adjustment, a bounded within-lifetime offset. It changes behavior without rewriting inheritedhelping_trait, keeping cultural/individual learning distinct from genetic inheritance. - Effective helping now drives cooperation probability, helping cost, child-care investment, reproduction cost pressure, and band raid force.
- New config keys include
social_learning_probability,social_learning_radius,social_learning_rate,social_learning_reputation_weight,social_learning_success_weight, andsocial_learning_max_adjustment. - The proof utility now includes
no_social_learningandsocial_learning_onlyscenarios, and reports effective helping plus social learning events. - The live viewer now reports effective helping and social-learning activity alongside inherited mean helping trait.
Stepwise impact:
behaviorally_anchored_model/config/behaviorally_anchored_config.pynow defines soft territorial competition controls:territorial_overlap_start_fraction,territorial_avoidance_strength,territorial_scarcity_threshold,territorial_conflict_probability,territorial_conflict_scarcity_weight,territorial_conflict_energy_penalty, andterritorial_displacement_distance.- Bands now measure overlap between their territory circles and local grass availability inside those territories.
- When bands overlap beyond the configured threshold, they usually drift apart through avoidance rather than treating territory as hard property.
- Direct territorial contests require both overlap and local grass scarcity, so conflict remains rare when resources elsewhere make avoidance feasible.
- Territorial contests impose an energy penalty and displacement on the losing band rather than immediate mortality or permanent exclusion.
- The model now records territorial overlap pairs, mean territorial overlap, avoidance events, territorial conflicts, territorial displacements, and cumulative conflict/displacement totals.
- The live viewer now displays territorial overlap, avoidance, conflict, and displacement metrics so band competition is visible during a run.
Stepwise impact:
- Random per-step band migration is no longer unconditional. The configured
group_migration_probabilitynow acts as the maximum migration probability, and actual migration requires ecological pressure. - Migration pressure rises when an individual is outside its current band
territory or when local grass around the individual is scarce relative to
territorial_scarcity_threshold. - Migration target choice now weights candidate bands by local grass availability around the target band, so movement is more likely to pull people toward better foraging territory rather than merely toward nearby bands.
interband_marriage_preferenceandinterband_marriage_distancewere reduced so inter-band marriage remains possible but no longer overwhelms residential band structure.band_member_attractionwas reduced so band territories guide long-range belonging without pulling all members into a central point.- Households now have short-range camp spacing through
household_spacing_radiusandhousehold_spacing_strength. Household residences repel each other when too close, which addresses individual camp clustering without deleting small bands or adding hard territorial killing.
Stepwise impact:
- Territorial contests can now cause lethal raid casualties, but only after the existing overlap-and-scarcity conflict gate has already produced a territorial contest.
- New config keys define this mechanism:
territorial_raid_mortality_probability,territorial_raid_mortality_scarcity_weight,territorial_raid_mortality_size_imbalance_weight,territorial_raid_mortality_adult_male_weight,territorial_raid_mortality_other_weight, andterritorial_raid_mortality_max_loser_fraction. - Raid mortality is applied to the losing band only. Subadult/adult males have the highest exposure by default; other non-juvenile members can still be at lower risk.
- Mortality risk increases with scarcity and winner-loser size imbalance, and a per-raid cap prevents one contest from automatically erasing an entire band.
- Juveniles are not direct raid targets under the default rule; they can still be affected indirectly when parents or caregivers die.
- The model now records
territorial_raid_deathsandcumulative_territorial_raid_deaths. - The live viewer now reports cumulative raid deaths in the side panel, and the command-line summary includes raid deaths in the territorial statistic.
Stepwise impact:
behaviorally_anchored_model/behaviorally_anchored_model.pyno longer treats the spatial world as a torus. Individual, household, band, offspring, and fission positions are clamped inside the bounded landscape.- Spatial distances for movement, mate choice, reciprocity bonds, social learning, child care, food transfer, territorial overlap, and reproduction now use direct bounded Euclidean distance instead of wrapped shortest-path distance.
- Grass harvesting no longer wraps across the edge of the grid. Foragers near a boundary harvest only cells that actually exist inside the landscape.
- Competing bands now actively exclude foreign members found inside their territory. Intruding individuals are displaced away from the visited band center and non-juvenile intruders pay a small energy penalty.
- Territorial overlap can now produce occasional direct contests even before severe scarcity; local grass scarcity still raises the chance and cost of those contests.
- Household residence points also get pushed out of foreign band territories, making camps follow band boundaries instead of sitting passively inside a competitor's range.
- The model records
territorial_exclusionsandcumulative_territorial_exclusions, and the command-line territorial statistic is now overlap/avoidance/exclusion/conflict/death. behaviorally_anchored_model/utils/live_viewer.pynow draws band territories and bond lines only once in bounded screen space. Circles and lines no longer reappear across the opposite edge.
Stepwise impact:
_apply_group_conflict()was replaced by_apply_resource_raids()inbehaviorally_anchored_model/behaviorally_anchored_model.py.- The old shortcut parameters
conflict_winner_bonusandconflict_loser_penaltywere removed. The model no longer creates winner energy separately from loser loss. - New config keys define the ecological raid rule:
raid_min_combatants,raid_base_probability,raid_scarcity_weight,raid_force_advantage_weight,raid_defender_energy_reserve,raid_steal_fraction,raid_transfer_efficiency,raid_attacker_cost_per_combatant,raid_attacker_injury_probability,raid_defender_injury_probability, andraid_injury_energy_cost. - Raids are local: they can occur only between overlapping band territories and only when both bands have enough non-juvenile combatants.
- Defender energy loss is bounded by surplus above
raid_defender_energy_reserve; attackers receive onlyraid_transfer_efficiencyof stolen energy, so part of the stolen resource is wasted. - Attackers pay direct energetic costs and can be injured, so raiding is not a free winner bonus. Defenders can also be injured.
- The model now records resource-raid events, stolen energy, attacker gain, attacker cost, and injuries in both latest-step and cumulative metrics.
- The live viewer now reports cumulative raid transfer and raid risk in the side panel.
Stepwise impact:
behaviorally_anchored_model/config/behaviorally_anchored_config.pynow makes the local grass ecology less abundant without changing the foraging gain parameters.grass_regrowth_per_stepwas reduced from0.18to0.07, so depleted patches recover more slowly after clustered foraging.grass_max_per_cellwas reduced from2.0to1.5, lowering the carrying capacity of each grass patch.grass_initial_fractionremains1.0, so the model still starts with a full landscape; scarcity now emerges from depletion and slower recovery rather than from an immediately poor initial condition.- Adult, elder, and subadult foraging gains were intentionally left unchanged, so changes in migration, child food, raids, and juvenile survival can be attributed to the resource ecology rather than a simultaneous foraging rewrite.
Stepwise impact:
behaviorally_anchored_model/config/behaviorally_anchored_config.pynow defines a local grass ecology:grass_grid_size,grass_max_per_cell,grass_initial_fraction,grass_regrowth_per_step, andgrass_harvest_radius.behaviorally_anchored_model/behaviorally_anchored_model.pynow initializes a bounded grass grid and regrows grass each step before foraging.- Subadult, adult, and elder
*_foraging_energy_gainparameters now act as maximum grass harvest per step. They are no longer unconditional energy added from nowhere. - Harvesting uses a local radius around the forager, so clustered households can deplete nearby grass even when grass elsewhere in the world remains available.
- Parents then keep an energy reserve
(
parent_food_transfer_energy_reserve) and distribute surplus food up toparent_food_transfer_capacityto their own juvenile children withinparent_food_transfer_radius. - Parent food allocation is weighted by proximity and by shared household
membership through
parent_food_transfer_household_weight_bonus. - The kin/household ablation now disables parent surplus feeding by setting
parent_food_transfer_capacityto0, while grass itself remains part of the background ecology. - The model now records
mean_grass_fraction,grass_harvest,parent_food_transfer,fed_juvenile_count, andfed_juvenile_fraction. behaviorally_anchored_model/utils/live_viewer.pynow supportsgfor the grass background and reports grass availability, harvested grass, and food passed to juveniles in the side panel.
Stepwise impact:
behaviorally_anchored_model/config/behaviorally_anchored_config.pynow definesjuvenile_food_survival_benefit,juvenile_food_survival_saturation, andjuvenile_no_food_survival_penalty.- Juvenile survival in
behaviorally_anchored_model/behaviorally_anchored_model.pynow includes a saturating food term from actual parent food received in the current step. Fed juveniles receive a survival boost; unfed juveniles receive a small penalty. - The model now records
mean_juvenile_food_survival_effect, so the net survival contribution of parent feeding is visible in history and summaries. - Parent provisioning was retuned to be visible under the longer childhood:
parent_food_transfer_capacity = 0.90,parent_food_transfer_energy_reserve = 6.0, andparent_food_transfer_radius = 24.0. - Fertility and energetic thresholds were retuned after adding human-length
childhood:
female_reproduction_probability = 0.90,reproduction_energy_threshold = 3.5,reproduction_energy_cost = 0.6, andchild_energy = 4.0. - Adult and elder grass harvest ceilings were increased to
0.40and0.24respectively so adults can support replacement under local resource depletion. base_juvenile_survival_probabilityis now0.88; food and care still modulate survival, but the baseline no longer collapses just because children remain dependent longer.behaviorally_anchored_model/behaviorally_anchored_model.pynow records stage counts:juvenile_count,subadult_count,adult_count, andelder_count.behaviorally_anchored_model/utils/live_viewer.pynow reports stage composition asStages J/S/A/Eand shows the latest food survival effect asFood surv.
Stepwise impact:
behaviorally_anchored_model/utils/live_viewer.pynow has a separate fed-juvenile overlay controlled byf.- The existing
ccontrol still toggles gold child-care rings only. - Juveniles that received parent food in the latest step now receive a cyan ring, scaled by food amount.
- Care and food can now be inspected independently, so alloparental care and parent provisioning are no longer visually conflated.
- The viewer legend now distinguishes
Gold ring = received care this stepfromCyan ring = received parent food.
Stepwise impact:
behaviorally_anchored_model/utils/live_viewer.pynow places the readable legend in the lower-left of the side panel.- The viewer window and side panel are wider, so charts can sit to the right of the legend instead of forcing compact labels.
- The existing mean-helping-trait chart now sits at the top of the right-side chart stack.
behaviorally_anchored_model/behaviorally_anchored_model.pynow records per-band population counts asband_<id>_count, with dynamic IDs when fission creates new bands.behaviorally_anchored_model/utils/live_viewer.pynow draws a second chart for band sizes beneath the mean-helping-trait chart, using the band colors as line colors.- The legend keeps readable labels such as
Band 0,Gold ring = received care this step,Cyan ring = received parent food, andSquare = household residence.
Stepwise impact:
behaviorally_anchored_model/behaviorally_anchored_model.pynow adds explicitBandobjects with territory centers and territory radii. The existinggroup_idindividual field is retained as the storage slot for band membership.n_groupsnow seeds the initial number of bands instead of only defining abstract color groups.- Founder families are initialized around band territories, so the starting
population has spatially concrete residential bands from step
0. - Movement now includes a weak attraction toward the individual's band
territory through
band_member_attraction, in addition to the existing household residence attraction. - Band territory centers update toward their current subadult/adult/elder
members through
band_territory_update_weight, so bands can move as their members move. group_migration_probabilityis now interpreted as per-step individual migration between bands for agents at leastband_migration_min_age, rather than random newborn group reassignment.- Large bands can fission using
band_fission_interval,band_fission_size_threshold,band_fission_min_size, andband_fission_dispersal_std; small bands can fuse with nearby bands usingband_fusion_interval,band_fusion_size_threshold, andband_fusion_distance. - Mate choice now includes inter-band marriage: different-band fathers receive
an additional mate-choice weight when the two bands or the two individuals
are close enough under
interband_marriage_distance. - When an inter-band spouse joins the mother's household, the father migrates into the mother's band, making marriage a migration pathway as well as a reproduction pathway.
- Spouse bonds are now stable while both spouses are alive, so births no longer constantly overwrite living spouse bonds. The inter-band marriage counter increments only when a new cross-band spouse bond forms.
- The model now records
band_count,mean_band_size,band_migrations,band_fissions,band_fusions,interband_marriages, and dynamicband_<id>_counthistories, plus cumulative totals for migration, fission, fusion, and inter-band marriage. behaviorally_anchored_model/utils/live_viewer.pynow labels these identities as bands, draws optional band territory outlines witht, reports band event counters, and plots dynamic band-size histories.
Stepwise impact:
behaviorally_anchored_model/config/behaviorally_anchored_config.pynow replaces the compressedjuvenile_maturity_age = 5life history withjuvenile_dependency_age = 12andadult_maturity_age = 18.behaviorally_anchored_model/behaviorally_anchored_model.pynow has a fourth life stage,subadult, for ages between dependent childhood and biological adulthood.- Default stage ranges are now: juvenile
0-11, subadult12-17, adult18-54, and elder55-84. - Female reproduction now starts at age
18, and male reproduction now starts at age20, so age5-6agents can no longer reproduce. - Founder adults now start between ages
18and36, and initial dependent children now start between ages0and11. - Subadults can move, remain attached to households, survive with their own stage probability, and harvest grass at a lower rate than adults.
- Subadults do not reproduce, do not provide full child-care investment, and do not count as adult/elder anchors for household residence updates.
- Adult household dispersal now happens when a juvenile/subadult first becomes
an adult at
adult_maturity_age. behaviorally_anchored_model/utils/live_viewer.pynow renders subadults as an intermediate dot size between dependent juveniles and adults.- Under the fertility retuning above,
no_kin_selectionremains demographically viable; it disables kin, spouse, household, child-care, and parent-food channels without making population collapse the expected result.
Stepwise impact:
behaviorally_anchored_model/behaviorally_anchored_model.pynow has explicitHouseholdobjects and every individual carries ahousehold_id.- Founder pairs start in the same household, and each household has a residence point that updates toward its adult/elder members.
- Movement now combines individual random walk with stage-specific attraction toward the household residence, making co-residence a spatial mechanism.
- Reproductive children inherit the mother's household. When a spouse bond is
formed or refreshed, the father can join the mother's household through
spouse_household_join_probability. - Juvenile survival now includes a household bonus from living parents and adult/elder household caregivers, in addition to direct child-care investment.
- Maturing juveniles can disperse and found a new household through
maturity_new_household_probabilityandmaturity_household_dispersal_std. - Household membership also biases child-care allocation through
child_rearing_household_member_weight_bonus. - The live viewer now supports
hfor household residence centers and reports household count, average household size, and latest household survival bonus.
Stepwise impact:
behaviorally_anchored_model/config/behaviorally_anchored_config.pynow makes child rearing more visible by loweringbase_juvenile_survival_probabilityfrom0.92to0.88and increasing the maximum care survival benefit from0.08to0.12.- Individuals now store
spouse_id, a persistent co-parent pair bond separate from the direct-reciprocityreciprocity_bond_id. - Initial founder pairs are placed near each other, form spouse bonds, and can seed local reciprocity bonds if both are unbonded and within the configured spouse-reciprocity radius.
- Reproduction now forms or refreshes a spouse bond between the mother and
father. Existing spouses are weighted upward in mate choice through
spouse_mate_preference. - Spouse reciprocity bonds now get a higher persistence floor while the spouses remain local, so co-parent reciprocity can be maintained but still dissolves if distance or bond history breaks it.
- Child-care allocation now gives extra weight when the helper's spouse is also the juvenile's parent, and another bonus when both co-parents are near the juvenile.
- The model now records spouse-pair counts, spouse reciprocity counts, cared juvenile counts, and spouse/coparent contributions to child care.
behaviorally_anchored_model/utils/live_viewer.pynow supportssfor spouse-bond lines andcfor the cared-juvenile overlay. The overlay draws a gold ring around juveniles that received care in the latest step.
Stepwise impact:
behaviorally_anchored_model/config/behaviorally_anchored_config.pynow defines child-rearing parameters under the kin-selection capacity, including care radius, helper care capacity, care cost, caregiver energy reserve, juvenile survival benefit, saturation, and parent/kin allocation weights.behaviorally_anchored_model/behaviorally_anchored_model.pynow lets adults and elders provide costly care to nearby juveniles before the survival pass.- Care recipient choice is local first, then biased by pedigree: parents get the strongest allocation bonus, other recognized kin get a smaller bonus, and non-kin juveniles can still receive low-baseline alloparental care.
- Juvenile survival now uses the configured base juvenile survival probability plus a saturating care bonus, then applies the existing soft density survival pressure.
- The model now records
juvenile_survival_rate,total_child_rearing_care,mean_child_rearing_care,mean_child_rearing_relatedness,kin_child_rearing_fraction, andparent_child_rearing_fraction. - The live viewer now displays latest child-care investment and juvenile survival, so child rearing is visible as a demographic mechanism rather than being conflated with reciprocity bonds or mating.
Stepwise impact:
- The direct-reciprocity pair-bond concept is now named
reciprocity_bondinstead ofpartnerin the behaviorally anchored Python model and viewer. behaviorally_anchored_model/config/behaviorally_anchored_config.pynow usesreciprocity_bond_persistence_probability,reciprocity_bond_formation_radius, andreciprocity_bond_dissolution_radius.behaviorally_anchored_model/behaviorally_anchored_model.pynow storesreciprocity_bond_idandreciprocity_bond_memoryon each individual.- The history metric formerly called
mean_partner_memoryis nowmean_reciprocity_bond_memory. - The live viewer labels these lines and controls as reciprocity bonds, making clear that they are direct-reciprocity social bonds rather than mating, group, kin, or reputation links.
Stepwise impact:
behaviorally_anchored_model/config/behaviorally_anchored_config.pynow definesreciprocity_bond_formation_radiusandreciprocity_bond_dissolution_radius.behaviorally_anchored_model/behaviorally_anchored_model.pynow uses bounded spatial distance when managing direct-reciprocity bonds.- Newly unbonded adults can only form reciprocity bonds with adults within
reciprocity_bond_formation_radius. - Existing bonds dissolve immediately when bondmates drift beyond
reciprocity_bond_dissolution_radius, in addition to the existing bond-memory dissolution rule. - The live viewer now draws reciprocity bonds as bounded-space segments, so bond lines reflect direct spatial separation in the displayed landscape.
Stepwise impact:
behaviorally_anchored_model/config/behaviorally_anchored_config.pynow definesjuvenile_movement_step_std,adult_movement_step_std, andelder_movement_step_std.behaviorally_anchored_model/behaviorally_anchored_model.pynow moves every living individual once per step after age and energy updates, before spatial interaction routing and mate choice are evaluated.- Movement is a local Gaussian random walk on the bounded landscape, so agents are clamped at the space edges instead of wrapping around.
- Juveniles move fastest, subadults remain fairly mobile, adults move moderately, and elders move slowest under the default config.
- The live viewer now shows real model movement because
xandypositions change during lifetime, not only when new offspring are born.
Stepwise impact:
behaviorally_anchored_model/utils/live_viewer.pynow labels the threshold statistic asTrait >= 0.10instead ofHelpers >10%, and the chart title now saysMean helping trait over time.behaviorally_anchored_model/behaviorally_anchored_model.pynow recordsrealized_helping_rate,helping_events, andhelping_opportunitiesfrom actual interaction events, making trait propensity and observed helping behavior distinct.- The viewer side panel now displays realized help rate and the raw help-event/opportunity count for the latest step.
behaviorally_anchored_model/config/behaviorally_anchored_config.pyreplaced the hardmax_populationcap withdensity_target_population,density_reproduction_pressure, anddensity_survival_pressure.- The core model no longer randomly removes every individual above 400 after reproduction. Crowding above the density target now reduces reproduction and survival probabilities, so population can fluctuate around the target rather than pinning exactly to it.
- Description: Patch-based grid simulation of altruism vs selfishness, ported from NetLogo to Python/NumPy.
- Browser replay preview:
- Features:
- Each cell can be empty (black), selfish (green), or altruist (pink)
- Simulates benefit/cost of altruism, fitness, and generational updates
- Fully vectorized NumPy implementation for fast simulation
- Pygame UI for interactive exploration
- Matplotlib plots for population dynamics
- Grid search for parameter sweeps
- Sampled browser replay and README GIF preview
- Files:
ecological_models/spatial_altruism/altruism_model.py: Core simulation logicecological_models/spatial_altruism/altruism_pygame_ui.py: Pygame-based interactive UIecological_models/spatial_altruism/config/altruism_config.py: Active runtime configurationecological_models/spatial_altruism/config/altruism_website_demo_config.py: Frozen website replay configurationecological_models/spatial_altruism/images/: Plotting scripts and generated image or Plotly outputsecological_models/spatial_altruism/utils/export_github_pages_demo.py: Website replay and preview GIF exporterecological_models/spatial_altruism/utils/altruism_grid_search.py: Parallel grid search for extended coexistence sweepsecological_models/spatial_altruism/data/grid_search_results_extended.csv: Results from the parallel grid search
- Usage:
- Run core model:
# edit ecological_models/spatial_altruism/config/altruism_config.py first if needed ./.conda/bin/python -m ecological_models.spatial_altruism.altruism_model - Run Pygame UI:
./.conda/bin/python -m ecological_models.spatial_altruism.altruism_pygame_ui
- Run grid search:
./.conda/bin/python -m ecological_models.spatial_altruism.utils.altruism_grid_search
- Regenerate website replay bundle:
./.conda/bin/python -m ecological_models.spatial_altruism.utils.export_github_pages_demo
- Run core model:
- Requirements:
- Python 3.8+
- numpy
- pygame (for UI)
- matplotlib (for plotting)
- torch (for surface fitting)
- Description: Spatial predator-prey ecology where predators evolve a continuous cooperation trait that affects group hunting success, payoff sharing, and private cooperation cost.
- Files:
ecological_models/cooperative_hunting/cooperative_hunting.py: core simulation and runtime entry pointecological_models/cooperative_hunting/config/cooperative_hunting_config.py: active runtime parametersecological_models/cooperative_hunting/utils/matplot_plotting.py: Matplotlib plotting helpers for baseline runsecological_models/cooperative_hunting/utils/sweep_dual_parameter.py: parameter sweep toolingecological_models/cooperative_hunting/utils/tune_mutual_survival.py: coexistence tuning utilitiesecological_models/cooperative_hunting/README.md: detailed interpretation and experiment guide
- Usage:
- Edit parameters in
ecological_models/cooperative_hunting/config/cooperative_hunting_config.py - Run:
./.conda/bin/python -m ecological_models.cooperative_hunting.cooperative_hunting
- Edit parameters in
- Current status:
- uses raw inherited
hunt_investment_traitdirectly for hunt effort and cooperation cost - supports equal-split or contribution-weighted prey sharing
- includes headless analysis, pygame live rendering, and sweep/tuning helpers
- uses raw inherited
- Description: Spatial Prisoner's Dilemma ecology inspired by the
FLAMEGPU implementation from
zeyus-research/FLAMEGPU2-Prisoners-Dilemma-ABM. Agents interact locally, move when isolated, reproduce into neighboring empty cells, and inherit mutable strategies. - Relation to the other evolved-cooperation models:
- relative to
ecological_models/spatial_altruism/, this model adds explicit agents, energy budgets, pairwise Prisoner's Dilemma play, movement, and conditional reciprocity;ecological_models/spatial_altruism/is the simpler lattice model of altruist versus selfish site competition - relative to
ecological_models/cooperative_hunting/, this model is more game-theoretic and less ecological: it has no prey, grass, hunt coalitions, or continuous cooperation trait - taken together, the three models form a progression from local altruist-benefit selection (
ecological_models/spatial_altruism/), to local reciprocity and inherited response rules (ecological_models/spatial_prisoners_dilemma/), to ecological synergy in predator group hunting (ecological_models/cooperative_hunting/)
- relative to
- Files:
ecological_models/spatial_prisoners_dilemma/spatial_prisoners_dilemma.py: core runtime, logging, and summary outputecological_models/spatial_prisoners_dilemma/config/spatial_prisoners_dilemma_config.py: active runtime parametersecological_models/spatial_prisoners_dilemma/config/spatial_prisoners_dilemma_website_demo_config.py: frozen website replay configurationecological_models/spatial_prisoners_dilemma/utils/matplot_plotting.py: Matplotlib plotting helpersecological_models/spatial_prisoners_dilemma/utils/export_github_pages_demo.py: website replay exporterecological_models/spatial_prisoners_dilemma/README.md: detailed mechanism and adaptation notes
- Usage:
- Edit parameters in
ecological_models/spatial_prisoners_dilemma/config/spatial_prisoners_dilemma_config.py - Run:
./.conda/bin/python -m ecological_models.spatial_prisoners_dilemma.spatial_prisoners_dilemma
- Regenerate website replay bundle:
./.conda/bin/python -m ecological_models.spatial_prisoners_dilemma.utils.export_github_pages_demo
- Edit parameters in
- Current status:
- preserves the intended spatial play, movement, reproduction, mutation, and culling logic from the external model family
- uses smaller CPU-friendly defaults instead of the original CUDA-scale population sizes
- now exports both JSON run logs for analysis and a sampled website replay bundle from a frozen public config
- now maps to the
human-cooperation-sitepage at/evolved-cooperation/spatial-prisoners-dilemma/
- Description: Abstract lattice model that tests a general cooperation condition: cooperation spreads when enough of the value it creates is routed back toward cooperators, or copies of the cooperative rule, to offset its private cost.
- Relation to the other evolved-cooperation models:
- compared with
ecological_models/spatial_altruism/, it replaces binary altruist-versus-selfish site types with a continuous cooperation trait and an explicit benefit-routing split - compared with
ecological_models/spatial_prisoners_dilemma/, it removes repeated-game memory and discrete strategy families so the feedback structure is easier to isolate - compared with
ecological_models/cooperative_hunting/, it removes predator-prey ecology and hunt-coalition mechanics so cooperative synergy is reduced to an abstract routing problem - it is therefore the most abstract website-facing module in the repo and the most direct test here of the claim that cooperation requires feedback
- compared with
- Files:
ecological_models/retained_benefit/retained_benefit_model.py: core runtime, local benefit-routing rule, and summary outputecological_models/retained_benefit/retained_benefit_pygame_ui.py: live lattice viewer with cooperation and lineage modesecological_models/retained_benefit/config/retained_benefit_config.py: active runtime parametersecological_models/retained_benefit/config/retained_benefit_website_demo_config.py: frozen website replay configurationecological_models/retained_benefit/utils/matplot_plotting.py: Matplotlib plotting helpersecological_models/retained_benefit/utils/export_github_pages_demo.py: website replay exporterecological_models/retained_benefit/README.md: detailed rationale and model explanation
- Usage:
- Edit parameters in
ecological_models/retained_benefit/config/retained_benefit_config.py - Run:
./.conda/bin/python -m ecological_models.retained_benefit.retained_benefit_model
- Run live viewer:
./.conda/bin/python -m ecological_models.retained_benefit.retained_benefit_pygame_ui
- Regenerate website replay bundle:
./.conda/bin/python -m ecological_models.retained_benefit.utils.export_github_pages_demo
- Edit parameters in
- Current status:
- implements continuous cooperation traits plus inherited lineage labels on a spatial lattice
- treats
retained_benefit_fractionas the primary abstraction parameter - includes a Pygame viewer that can switch between cooperation intensity and lineage structure
- exports a sampled website replay bundle from a frozen public config
- writes JSON logs for headless analysis and can show a small Matplotlib summary figure
- Description: General non-website-backed interaction-kernel engine that separates trait-dependent production, positive and negative effect routing, fitness score formation, local selection, and inheritance.
- Relation to the other evolved-cooperation models:
- relative to
ecological_models/retained_benefit/, it is broader than retained benefit routing because it supports separate positive and negative kernels - relative to
ecological_models/spatial_altruism/, it keeps the spatial local-selection structure but uses continuous traits and configurable routing kernels - relative to
ecological_models/spatial_prisoners_dilemma/, it has no explicit pairwise game, memory, or strategy family labels unless those are added as mechanism-specific extensions - relative to
ecological_models/cooperative_hunting/, it removes ecological entities and leaves a reusable produced-effect, routed-effect, and selection core
- relative to
- Files:
moran_models/interaction_kernel/interaction_kernel_model.py: core interaction-kernel runtime and summary outputmoran_models/interaction_kernel/kernels.py: positive and negative routing-kernel buildersmoran_models/interaction_kernel/selection.py: local replacement and inheritance logicmoran_models/interaction_kernel/metrics.py: per-step summary metricsmoran_models/interaction_kernel/config/interaction_kernel_config.py: active runtime parametersmoran_models/interaction_kernel/README.md: detailed kernel description and rename note
- Usage:
- Edit parameters in
moran_models/interaction_kernel/config/interaction_kernel_config.py - Run:
./.conda/bin/python -m moran_models.interaction_kernel.interaction_kernel_model
- Edit parameters in
- Current status:
- keeps the config file as the single source of truth for normal runs
- uses theory-aligned
h,B_plus,B_minus,K_plus,K_minus,R_plus,R_minus,C, andWnotation - currently supports uniform and kin-weighted positive kernels plus none or uniform negative kernels
- writes JSON logs for headless analysis
- is present only in the Python repo for now and does not yet have a matching website page
The table below separates the website-facing ecological models from the Moran-process models used for mechanism-level comparisons.
| Feature | Ecological models (ecological_models/) |
Moran models (moran_models/) |
|---|---|---|
| Core question | What happens when cooperation is embedded in a concrete ecology? | Which cooperation mechanism works under a controlled Moran update rule? |
| Model scale | Scenario-specific systems with their own entities and state variables. | Shared abstract engine plus thin mechanism wrappers. |
| Main entities | Cells, agents, predators, prey, grass, lineages, empty sites, or local game players. | Lattice sites carrying a cooperation trait h, lineage or mechanism state, and fitness score W. |
| Cooperation representation | Binary strategy, conditional strategy, or continuous ecological investment depending on the model. | Usually one continuous help trait h; direct and indirect reciprocity add memory or reputation state. |
| Interaction mechanism | Local games, benefit routing, hunting synergy, movement, predation, disturbance, or ecological turnover. | Produced effects are routed through mechanism-specific kernels or hooks such as kin bias, memory, reputation, network locality, or group copying. |
| Selection and replacement | Model-specific birth, death, movement, predation, reproduction, disturbance, and population caps. | Moran-style local replacement: sites sample parents from neighborhoods using fitness-weighted selection, then inherit with mutation. |
| Role of space | Space is part of the ecology itself: it controls encounters, resources, empty sites, and local expansion. | Space mainly constrains who sends benefits to whom and who competes to replace whom. |
| Feedback pathway | Benefits return through ecological structure such as clusters, prey capture, retained benefit, or repeated local play. | Benefits return through explicit Nowak mechanisms: kin selection, direct reciprocity, indirect reciprocity, network reciprocity, or group selection. |
| Parameter style | Ecological parameters such as cost, benefit, prey energy, synergy, retained fraction, movement, and mortality. | Theory-aligned parameters such as B_plus, C, kernel mode, relatedness weights, memory decay, reputation bias, and group interval. |
| Best use | Demonstrating rich eco-evolutionary dynamics and generating replayable public examples. | Comparing mechanisms under matched assumptions and testing theory-level abstractions. |
| Outputs | JSON logs, plots, Pygame viewers, sampled browser replay bundles, and GitHub Pages demo routes. | JSON logs, summary statistics, comparison utilities, and Pygame live viewers. |
| Website status | Canonical Python implementations for the public replay-backed evolved-cooperation pages. | Python-side mechanism demos; some have matching public explanation pages, but no repo-level replay export bundle yet. |
| Example modules | spatial_altruism, cooperative_hunting, spatial_prisoners_dilemma, retained_benefit. |
interaction_kernel, kin_selection, direct_reciprocity, direct_reciprocity/scaffolds/spatial_clustering, direct_reciprocity/scaffolds/continuous_spatial_memory, indirect_reciprocity, network_reciprocity, group_selection. |
The practical distinction is that ecological models ask whether cooperation can survive inside a modeled environment, while Moran models ask how a named cooperation mechanism behaves when the update rule is held mostly constant.
Install dependencies:
pip install numpy pygame matplotlib torchFor Pygame visualization, you may need:
conda install -y -c conda-forge gcc=14.2.0- Original NetLogo models from Uri Wilensky and the EACH unit (Evolution of Altruistic and Cooperative Habits)
- See
ecological_models/spatial_altruism/README.mdfor more details

