A 2D agent-based growth simulation written in C++ with SFML. The system grows a tree-like structure on a grid, where nodes store a scalar resource (count), walkers search for expansion opportunities, and backpropagation agents return resources up the tree after successful growth.
The simulation is interactive: you can pause, step, paint obstacles, toggle growth rules, and watch how different settings change the balance between branching, survival, collapse, and long-term structure.
The simulation begins with a single seed node placed at the center of the grid.
Each update has four main phases:
-
Backpropagation agents deposit resources
- After a successful spawn, a backpropagation agent is launched from the parent of the newly created node.
- That agent walks upward through the parent chain and deposits resource according to the selected backpropagation mode.
-
Walk agents attempt to grow the structure
- A walker starts at some living node.
- If there is at least one empty neighboring cell in the 8-neighborhood, it creates a new node there.
- If there is no empty neighbor, it attempts to move downward through the node’s living children.
- Moving or spawning costs energy from the walker's original funder node.
-
A new walker is spawned
- One walker is spawned per update by default.
- The starting node is chosen with probability proportional to node
count. - A hard cap limits how many walkers may exist at once.
-
All living nodes age and decay
- Every living node gets older.
- Its resource is reduced according to the selected aging mode.
- If its resource drops to or below the death threshold, the node dies.
Each node stores:
- a grid position
- a parent pointer
- a list of children
- a scalar resource value called
count - age
- depth in the tree
- alive/dead state
A new node starts with count = 1.0.
Growth only happens into empty cells in the 8-neighborhood around a walker’s current node.
If a walker cannot spawn locally, it tries to continue downward into one of the current node’s living children.
The resource value count is the main driver of the dynamics:
- nodes with higher
countare more likely to be selected as walker starting points - nodes with higher
countare favored during weighted child selection - nodes lose resource through movement/spawn costs and through global decay
- nodes gain resource from backpropagation
A node dies when its count becomes less than or equal to deathThreshold (currently 0.0).
When a node dies:
- it is removed from the alive list
- its cell is either cleared or turned into a wall, depending on death mode
- its children lose that node as parent and become disconnected from it
Toggles weighted child traversal on/off.
- ON: when a walker must move downward through children, children with higher
countare chosen more often. - OFF: downward child choice is uniform among living children.
This tends to:
- ON: reinforce strong branches and create winner-take-most subtrees
- OFF: spread exploration more evenly across branches
Note: in the current code, the initial spawning of a new walker is always weighted by node
count. TheWtoggle only affects downward child traversal, not the first node chosen for a fresh walker.
Cycles through four ways to return energy after a successful spawn.
Each visited node on the way back to the root receives the full backpropPool amount.
Effect:
- injects a lot of total energy into the system
- strongly supports survival and large growth
- can make long paths disproportionately profitable
The total backprop pool is divided evenly across the full return path.
Effect:
- keeps total returned energy fixed per successful spawn
- reduces the advantage of very deep branches
- often gives more balanced growth
Nodes closer to the leaf get more of the return; the reward decays linearly toward the root.
Effect:
- favors outer growth fronts
- supports active branch tips
- can make exploratory edges more persistent than the interior
Nodes closer to the root get more of the return; the reward grows linearly toward the root.
Effect:
- strengthens the trunk/interior
- can stabilize the core while making tips more fragile
- tends to encourage a strong backbone instead of many persistent frontier branches
Changes backpropPool.
- Higher values mean each successful spawn returns more energy overall.
- Lower values make it harder for the system to offset walk costs and aging.
Typical effect:
- higher pool -> denser growth, more survival, larger populations
- lower pool -> sparser growth, more die-off, possible collapse
Cycles through three global decay rules.
Every living node loses the same base amount each update:
baseDecay = 1 / lifespanTimesteps
Effect:
- all nodes age at the same baseline rate
- simplest and easiest mode to reason about
Decay starts at the base rate and increases with the node’s age.
Effect:
- older nodes become progressively harder to sustain
- encourages turnover
- can prune old interiors unless backprop is strong enough to maintain them
Decay depends on how many living children a node has.
- nodes with living children decay more slowly
- leaf-like nodes decay at the base rate
Effect:
- branch points and transport hubs become easier to maintain
- unsupported tips remain fragile
- tends to reward tree-like transport structure
Changes lifespanTimesteps, which sets the base decay scale.
- Larger lifespan -> lower base decay -> nodes survive longer
- Smaller lifespan -> higher base decay -> nodes die faster
Typical effect:
- long lifespan -> bigger, more persistent structures
- short lifespan -> harsher environment, stronger selection pressure
Changes walkDownCost.
This cost is charged to the walker’s original funder whenever the walker:
- moves downward through the tree, or
- successfully spawns a new node
Typical effect:
- higher cost -> exploration is expensive; weaker nodes die faster
- lower cost -> easier branching and deeper traversal
- negative cost -> movement effectively becomes rewarding, which can create runaway behavior
Toggles what happens to a dead node’s cell.
- Clear: the dead cell becomes empty space again
- Calcify: the dead cell becomes a wall
Typical effect:
- Clear -> the system can regrow through old territory
- Calcify -> dead structure becomes permanent obstacle, which can lock in shape and reduce future accessibility
Draws walls into empty cells using a brush.
Effect:
- blocks future growth in painted regions
- useful for forcing channels, barriers, mazes, or constrained environments
Resets the simulation to a single center seed and clears graph history.
In the current implementation, reset also removes any hand-painted walls.
1: smooth color gradient based on log-scaled node count2: alternate 16-bit style log visualization3: grayscale intensity based on count
- Yellow: active walkers
- Cyan: active backpropagation agents
- Gray / dark: dead or calcified cells depending on mode
The graph in the top-right tracks:
- green line: number of living nodes
- orange/yellow line: total energy in the system
Press D to inspect a local neighborhood.
Use I, J, K, L to pan the debug window.
Space— pause / resumeS— single-step while pausedR— reset simulation
1,2,3— color modesD— toggle debug gridI,J,K,L— move debug grid center
W— toggle weighted child selectionB— cycle backprop modeA— cycle aging mode[/]— decrease / increase backprop poolLeft/Right— decrease / increase lifespanDown/Up— decrease / increase walk costC— toggle clear vs calcify on death
- Left mouse button — paint walls
This project uses CMake and fetches SFML automatically.
- CMake 3.14+
- A C++ compiler with C++17 support or better
- Internet access on first configure so CMake can fetch SFML
mkdir build
cd build
cmake ..
cmake --build .On Windows, the current CMakeLists.txt also copies the required SFML DLLs into the build output directory after building.
main.cpp— UI, rendering, controls, graphs, and interactionSimulation.hpp— simulation state and update logicCMakeLists.txt— build configuration and SFML fetching
A few details that matter when interpreting results:
- The simulation currently spawns one new walker per update by default.
- Active walkers are capped at approximately
cbrt(width * height). - Reset clears both the simulation and any manually painted walls.
- Font rendering uses
arial.ttfif available; if not, the simulation still runs but text overlays may not appear.
- save/load parameter presets
- separate initial walker selection from weighted child traversal as distinct toggles
- expose death threshold as a runtime parameter
- make walker spawn count adaptive to system size or energy
- keep user-painted walls across reset as an option
- export screenshots / statistics for experiments