sprout-garden-core
Sprout Garden Core
The Simulation Engine - Hot Potato execution model
"If you've got it, pass it. Everyone passes together."
Overview
The core simulation engine implements a push-based execution model where sprouts flow through plots in discrete ticks. Each plot has defined seeds (input directions) and fruits (output directions).
This is the same execution model as Wanderland production workflows - if a 7-year-old can wire up plots to grow a sprout, the abstraction is correct.
Architecture
┌─────────────────────────────────────────────┐
│ SIMULATION ENGINE │
├─────────────────────────────────────────────┤
│ │
│ Garden │
│ ├── size: {width, height} │
│ └── plots: {'A1': {type, config}, ...} │
│ │
│ Sprouts[] (active) │
│ ├── id, position, direction │
│ ├── shape, color (state) │
│ ├── status: transit|arrived|lost|blocked │
│ └── lineage: [{tick, plot, action}, ...] │
│ │
│ Tick Loop │
│ 1. emitFromSprings() - Tick 0 only │
│ 2. executeTick() - Process all sprouts │
│ 3. Check completion │
│ │
└─────────────────────────────────────────────┘Key Concepts
Position Keys
Grid uses Excel-style addressing: A1, B2, C3...
posKey(x, y) → `${String.fromCharCode(65 + y)}${x + 1}`
// (0,0) → "A1", (1,0) → "A2", (0,1) → "B1"Seeds and Fruits
Each plot type defines:
- seeds[]: Directions it can receive from
- fruits[]: Directions it outputs to (or special markers)
Special fruit markers:
'pass-through': Continue in same direction as incoming'perpendicular': Split 90° to incoming direction
Hot Potato Execution
executeTick() {
// Everyone holding something plays
for (const sprout of this.sprouts) {
if (sprout.status !== 'transit') continue;
// 1. Get current plot
// 2. Grow: transform sprout via plot.grow()
// 3. Find next position(s) via getFruitDirections()
// 4. Queue movement (clone for splits)
}
// Execute all movements together
for (const move of movements) {
move.sprout.position = move.to;
move.sprout.direction = move.direction;
}
}Splitter Cloning
When a plot has multiple output directions (perpendicular splitter):
let movingSprout = sprout;
if (!isFirstMove) {
// Clone for additional directions
movingSprout = this.createSprout(sprout.position, {
shape: sprout.shape,
color: sprout.color
}, sprout.direction);
movingSprout.lineage = [...sprout.lineage];
this.sprouts.push(movingSprout);
}Source Files
| File | Purpose |
|---|---|
src/core/simulation.js |
Main simulation engine |
src/core/constants.js |
Directions, helper functions |
src/plots/index.js |
Plot registry (auto-wired) |
Verification
Live test results from [[sprout-garden-core-test]]:
| Test | Status | Details |
|---|---|---|
| simulation.js exists | fail | file not found |
| constants.js exists | fail | file not found |
Slots
North
slots:
- sprout-garden
- sprout-gardenSouth
slots:
- sprout-garden-core-testEast
slots: []West
slots: []
↑ northsprout-garden
↓ southsprout-garden-core-test