sprout-garden-core-test
Test: Sprout Garden Core
Following [[pattern-looking-glass-development]] - the tests ARE the documentation.
Subject
The core simulation engine (src/core/simulation.js) implementing Hot Potato execution:
- Position key conversion (A1, B2, etc.)
- Seed/Fruit direction matching
- Sprout movement and cloning
- Tick-based execution
config
path: null
base_url: file:///Users/graemefawcett/working/ben.fawcett.family/activities/sprout-gardenSpec
Test: Position Key Conversion
// posKey(x, y) converts grid coords to Excel-style keys
// (0,0) → "A1", (1,0) → "A2", (0,1) → "B1"
describe('posKey', () => {
it('converts (0,0) to A1', () => {
expect(posKey(0, 0)).toBe('A1');
});
it('converts (1,0) to A2', () => {
expect(posKey(1, 0)).toBe('A2');
});
it('converts (0,1) to B1', () => {
expect(posKey(0, 1)).toBe('B1');
});
it('handles large grids', () => {
expect(posKey(9, 7)).toBe('H10');
});
});Test: Key to Coords Conversion
// keyToCoords(key) reverses posKey
// "A1" → {x: 0, y: 0}, "B2" → {x: 1, y: 1}
describe('keyToCoords', () => {
it('converts A1 to (0,0)', () => {
expect(keyToCoords('A1')).toEqual({x: 0, y: 0});
});
it('converts B2 to (1,1)', () => {
expect(keyToCoords('B2')).toEqual({x: 1, y: 1});
});
it('handles H10', () => {
expect(keyToCoords('H10')).toEqual({x: 9, y: 7});
});
});Test: Can Receive Direction
// canReceive(plotType, fromDirection) checks if plot accepts from direction
// fromDirection is where sprout came FROM, check opposite
describe('canReceive', () => {
it('vine-right accepts from left', () => {
// vine-right has seeds: ['left', 'up', 'down']
expect(canReceive('vine-right', 'right')).toBe(true); // came from right, enters from left
});
it('vine-right rejects from right', () => {
expect(canReceive('vine-right', 'left')).toBe(false); // came from left, would enter from right
});
it('harvest accepts from all directions', () => {
expect(canReceive('harvest', 'left')).toBe(true);
expect(canReceive('harvest', 'right')).toBe(true);
expect(canReceive('harvest', 'up')).toBe(true);
expect(canReceive('harvest', 'down')).toBe(true);
});
});Test: Get Fruit Directions
// getFruitDirections(plotType, incomingDirection) returns output directions
describe('getFruitDirections', () => {
it('pass-through continues same direction', () => {
// form-grower has fruits: ['pass-through']
expect(getFruitDirections('form-grower', 'right')).toEqual(['right']);
expect(getFruitDirections('form-grower', 'down')).toEqual(['down']);
});
it('perpendicular splits 90 degrees', () => {
// splitter has fruits: ['perpendicular']
expect(getFruitDirections('splitter', 'right')).toEqual(['up', 'down']);
expect(getFruitDirections('splitter', 'up')).toEqual(['left', 'right']);
});
it('vine outputs single direction', () => {
expect(getFruitDirections('vine-right', 'right')).toEqual(['right']);
});
});Test: Sprout Cloning for Splits
// When a splitter outputs to multiple directions, sprout is cloned
describe('Sprout Cloning', () => {
it('creates separate sprouts for each output', () => {
const sim = new Simulation(gardenWithSplitter);
sim.run();
// Started with 1 sprout, should have 2 after split
expect(sim.sprouts.length).toBe(2);
});
it('cloned sprouts have copied lineage', () => {
const sim = new Simulation(gardenWithSplitter);
sim.run();
const [original, clone] = sim.sprouts;
expect(clone.lineage.length).toBeGreaterThan(0);
});
it('cloned sprouts have unique IDs', () => {
const sim = new Simulation(gardenWithSplitter);
sim.run();
const [original, clone] = sim.sprouts;
expect(original.id).not.toBe(clone.id);
});
});Test: Hot Potato Execution
// All sprouts move together at end of tick
describe('Hot Potato Execution', () => {
it('all movements happen simultaneously', () => {
const sim = new Simulation(gardenWithTwoSprings);
// After tick 1, both sprouts should have moved
sim.emitFromSprings();
sim.executeTick();
const positions = sim.sprouts.map(s => s.position);
// Neither should be at spring position anymore
expect(positions).not.toContain('A1');
expect(positions).not.toContain('E1');
});
it('sprout marked lost only if NO valid direction found', () => {
const sim = new Simulation(gardenWithDeadEnd);
sim.run();
const lostSprout = sim.sprouts.find(s => s.status === 'lost');
expect(lostSprout.lineage.at(-1).action).toBe('stuck');
});
});Results
{
"summary": "0/2 tests passing",
"tests": [
{
"test": "simulation.js exists",
"status": "fail",
"details": "file not found"
},
{
"test": "constants.js exists",
"status": "fail",
"details": "file not found"
}
]
}Provenance
Fences
test-position-keys
- Status: Spec only (JS tests pending vitest setup)
- By: Claude (2025-11-29)
- Note: Defines expected behavior for posKey()
test-key-to-coords
- Status: Spec only
- By: Claude (2025-11-29)
- Note: Defines expected behavior for keyToCoords()
test-can-receive
- Status: Spec only
- By: Claude (2025-11-29)
- Note: Defines seed direction matching
test-fruit-directions
- Status: Spec only
- By: Claude (2025-11-29)
- Note: Defines output direction logic including pass-through and perpendicular
test-sprout-cloning
- Status: Spec only
- By: Claude (2025-11-29)
- Note: Defines splitter behavior
test-hot-potato
- Status: Spec only
- By: Claude (2025-11-29)
- Note: Defines simultaneous movement semantics
sprout-garden-core-results
- Status: Verified (Python static analysis)
- By: Claude (2025-11-29)
- Note: Checks source files exist and contain expected functions
Slots
North
slots:
- sprout-garden-coreSouth
slots: []East
slots: []West
slots:
- pattern-looking-glass-development
↑ northsprout-garden-core