Spatial Altruism
altruism/altruism_model.py is a direct evolutionary altruism model rather than an ecological predator-prey system.
It models a patch-based population in which:
- each grid cell is empty, selfish, or altruistic,
- altruists provide a local benefit but pay a private cost,
- selfish patches receive the local benefit without paying that cost,
- the next occupant of each patch is chosen by a local weighted lottery.
Static Run Snapshot
The figure below is generated from a fixed Python run of the current NumPy implementation. It shows the initial patch state, the patch state after 200 updates, and the population counts across time.

Patch States
Each patch has one of three states:
- altruist: pink
- selfish: green
- empty: black
The model is spatial because each patch interacts only with its own plus-shaped neighborhood:
- the focal patch itself,
- the patch above,
- the patch below,
- the patch to the left,
- the patch to the right.
So each local calculation uses exactly 5 patches.
Local Benefit Rule
The altruism benefit at one patch is:
altruism_benefit = benefit_from_altruism × (benefit_out_self + sum_of_neighbor_benefit_out) / 5
Variable meanings:
altruism_benefit: total social benefit currently available at the focal patchbenefit_from_altruism: strength of the positive externality produced by altruistsbenefit_out_self:1if the focal patch is altruist, otherwise0sum_of_neighbor_benefit_out: number of altruist contributors in the four-neighbor set5: the focal patch plus its four neighbors
This means altruists create a benefit that is local and shared.
Fitness Rule
Fitness depends on patch type:
- altruist patch:
fitness = (1 - cost_of_altruism) + altruism_benefit - selfish patch:
fitness = 1 + altruism_benefit - empty patch:
fitness = harshness
Variable meanings:
fitness: the reproductive weight a patch contributes to nearby lotteriescost_of_altruism: private cost paid only by altruist patchesharshness: baseline weight assigned to empty patches
This is the core social dilemma:
- altruists help the neighborhood,
- selfish patches enjoy that help too,
- but only altruists pay the direct cost.
Neighborhood Lottery
After each patch computes its fitness, the focal patch collects three local totals:
alt_fitness: summed fitness contributed by altruist patches in the focal plus-neighborhoodself_fitness: summed fitness contributed by selfish patches in the focal plus-neighborhoodharsh_fitness: summed fitness contributed by empty patches in the focal plus-neighborhood
The normalized lottery weights are:
alt_weight = alt_fitness / fitness_sumself_weight = self_fitness / fitness_sumharsh_weight = (harsh_fitness + disease) / fitness_sum
with:
fitness_sum = alt_fitness + self_fitness + harsh_fitness + disease
Variable meanings:
alt_weight: probability mass for the next patch becoming altruistself_weight: probability mass for the next patch becoming selfishharsh_weight: probability mass for the next patch becoming emptydisease: extra pressure toward emptiness, even when no empty neighbor strongly contributes
The next generation at each patch is then sampled from those weights:
- altruist if the draw lands in
alt_weight - selfish if the draw lands in
self_weight - empty otherwise
Why This Belongs Under Evolved Cooperation
This model is on equal footing with cooperative hunting because both models study cooperation through inherited variation and selection across generations.
The difference is the mechanism:
Spatial Altruismstudies a direct altruism-versus-selfishness social dilemma in a patch lotteryPredator-Prey-Grass Cooperative Huntingstudies selection on an inherited hunting-investment trait inside an ecological food web
So they are complementary examples of evolved cooperation at different levels of abstraction:
- one minimal and spatial,
- one ecological and agent-based