Something went wrong. Try again.
My website. cameron.stream
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271import test from 'node:test';import assert from 'node:assert/strict';import { createWorld, advanceWorld, dropObject, offerObject, onShelf, heightAt, depthAt, surfaceAt, bodyAt, waterLevel, SPECIES, SPECIES_ORDER, LIMIT, TIDE_PERIOD, TIDE_LOW, TIDE_HIGH, START_POOL, BURROW, WET, describeWorld, goalText, BLOCK, BLOCK_Y0, X0, Z0, CELL, blockKey, removeBlock, footAt,} from '../public/tide-pool-world.js';
const cellX = i => X0 + (i + .5) * CELL, cellZ = j => Z0 + (j + .5) * CELL;function run(world, seconds) { for (let i = 0; i < seconds * 20; i++) advanceWorld(world, 1 / 20);}const pool = { x: START_POOL.x, z: START_POOL.z };
test('the shelf begins with one pool and a few creatures, and introduces each species one at a time', () => { const world = createWorld(); assert.deepEqual(world.bodies.map(b => b.name), ['I']); assert.ok(world.creatures.length <= 10, `${world.creatures.length} creatures at the start`); assert.deepEqual([...new Set(world.creatures.map(c => c.sp))].sort(), ['scraper', 'tab']); const order = []; for (let s = 0; s < 240; s++) { run(world, 1); for (const e of world.events) if (e.type === 'debut' && !order.includes(e.sp)) order.push(e.sp); } assert.deepEqual(order, ['pylon', 'borer', 'collector', 'mason', 'breaker']);});
test('the tide floods the first pool and leaves it standing', () => { const world = createWorld(); assert.equal(bodyAt(world, pool.x, pool.z), 'I'); // Run to low water: the shelf dries, the pool keeps its water above its own lip. while (waterLevel(world) > TIDE_LOW + .02) run(world, 2); assert.ok(depthAt(world, pool.x, pool.z) > .3, 'the pool holds water at low tide'); let dry = 0, land = 0; for (let k = 0; k < world.h.length; k++) if (world.h[k] > -.6) { land++; if (world.w[k] - world.h[k] < WET) dry++; } assert.ok(dry / land > .6, `only ${Math.round(dry / land * 100)}% of the shelf is dry at low tide`); assert.ok(surfaceAt(world, pool.x, pool.z) > waterLevel(world) + .1, 'the pool stands above the sea'); // Run to high water: the sea washes over the shelf and the pool joins it. while (waterLevel(world) < TIDE_HIGH - .02) run(world, 2); assert.ok(Math.abs(surfaceAt(world, pool.x, pool.z) - waterLevel(world)) < .05);});
test('borers dig burrows and tunnels, pools form on their own, and the ground visibly changes', () => { const world = createWorld(); const before = Float32Array.from(world.h); run(world, 62); const borer = world.creatures.find(c => c.sp === 'borer'); const home = { ...borer.home }; const seen = new Set(); for (let minute = 0; minute < 12; minute++) { run(world, 60); for (const e of world.events) seen.add(e.type); } let changed = 0; for (let k = 0; k < before.length; k++) if (Math.abs(world.h[k] - before[k]) > .15) changed++; assert.ok(changed > 200, `only ${changed} cells changed`); assert.ok(heightAt(world, home.x, home.z) < BURROW + .3 || !world.creatures.includes(borer), 'a burrow was sunk'); assert.ok(seen.has('dump'), 'spoil was banked'); assert.ok(seen.has('form') || world.bodies.length > 1, 'a new pool formed');});
test('masons fill hollows and raise walls, and reshaped ground never stands steeper than the talus limit', () => { const world = createWorld(7); const original = Float32Array.from(world.h); const types = new Set(); for (let minute = 0; minute < 15; minute++) { run(world, 60); for (const e of world.events) types.add(e.type); } assert.ok(types.has('build') || types.has('fill'), 'a mason set material'); assert.ok(types.has('dam'), 'a mason raised a dam across a pool outlet'); let built = 0; for (const b of world.built) if (b > .05) built++; assert.ok(built > 0, 'set stone is on the ground'); let steepest = 0; const cols = 128, rows = world.h.length / cols; for (let j = 1; j < rows - 3; j++) for (let i = 1; i < cols - 2; i++) { const k = j * cols + i; // Natural rock can be sheer; only ground the creatures have dug or piled must settle. for (const n of [k + 1, k + cols]) { if (Math.abs(world.h[k] - original[k]) < .05 || Math.abs(world.h[n] - original[n]) < .05) continue; steepest = Math.max(steepest, Math.abs(world.h[k] - world.h[n])); } } assert.ok(steepest < .45, `steepest step ${steepest.toFixed(2)}`);});
test('a visitor offering draws a free specialist immediately, never one with a carried task', () => { const world = createWorld(); run(world, 205); for (const c of world.creatures) if (c.shell !== null) { world.objects.splice(world.objects.findIndex(o => o.id === c.shell), 1); c.shell = null; } const brass = offerObject(world, 'brass', pool.x, pool.z); const collector = world.creatures.find(c => c.task?.object === brass.id); assert.equal(collector.sp, 'collector'); assert.equal(collector.state, 'notice'); assert.equal(brass.claimed, collector.id); const shell = offerObject(world, 'shell', pool.x + 1, pool.z); assert.equal(world.creatures.find(c => c.task?.object === shell.id).sp, 'scraper'); const pebble = offerObject(world, 'pebble', pool.x - 1, pool.z); assert.equal(world.creatures.find(c => c.task?.object === pebble.id).sp, 'mason'); const carrier = world.creatures.find(c => c.sp === 'breaker'); const held = dropObject(world, 'brass', pool.x, pool.z + 1); held.place = 'carried'; held.claimed = carrier.id; carrier.carrying = held.id; for (let i = 0; i < 6; i++) offerObject(world, 'brass', pool.x + i * .2, pool.z); assert.equal(carrier.carrying, held.id); assert.equal(offerObject(world, 'feed', pool.x, pool.z).kind, 'feed');});
test('a visitor-free shelf grazes, hunts, breeds, and keeps every species going', () => { const world = createWorld(); const seen = new Set(); for (let minute = 0; minute < 20; minute++) { run(world, 60); for (const e of world.events) seen.add(e.type === 'end' ? `end:${e.detail}` : e.type); for (const c of world.creatures) assert.ok(onShelf(world, c.x, c.z), `${c.sp} left the shelf`); } for (const type of ['birth', 'end:eaten']) assert.ok(seen.has(type), type); const maxima = SPECIES_ORDER.map((s, i) => Math.max(...world.census.map(r => r.n[i]))); assert.ok(maxima.every(n => n > 0), 'every species is present at some point'); assert.ok(world.creatures.some(c => c.gen > 1), 'lineages continue'); assert.match(describeWorld(world), /The shelf holds/); for (const c of world.creatures) assert.equal(typeof goalText(world, c), 'string');});
test('deterministic reset and bounded object, ripple, event, census, and population counts', () => { const a = createWorld(12), b = createWorld(12); for (let i = 0; i < 60; i++) { for (const world of [a, b]) { offerObject(world, ['pebble', 'shell', 'brass', 'feed'][i % 4], pool.x + Math.sin(i) * 3, pool.z + Math.cos(i) * 2); run(world, 3); assert.ok(world.objects.length <= LIMIT); assert.ok(world.ripples.length <= 12); assert.ok(world.events.length <= 80); assert.ok(world.census.length <= 150); for (const s of SPECIES_ORDER) assert.ok(world.creatures.filter(c => c.sp === s).length <= SPECIES[s].max); assert.ok(world.objects.every(o => Number.isFinite(o.x + o.z + o.height))); assert.ok(world.creatures.every(c => Number.isFinite(c.x + c.z + c.energy))); } } assert.deepEqual(a.objects, b.objects); assert.deepEqual(a.creatures, b.creatures); assert.deepEqual(a.h, b.h);});
test('drops reject invalid inputs and never evict a claimed object', () => { const world = createWorld(); assert.equal(dropObject(world, 'unknown', pool.x, pool.z), null); assert.equal(offerObject(world, 'unknown', pool.x, pool.z), null); assert.equal(dropObject(world, 'brass', 90, 0), null); assert.equal(dropObject(world, 'brass', NaN, 0), null); const first = world.objects[0]; first.claimed = 0; for (let i = 0; i < 200; i++) dropObject(world, 'pebble', pool.x, pool.z); assert.equal(world.objects.length, LIMIT); assert.ok(world.objects.includes(first));});
test('zero time preserves the world and long frames do not jump it forward', () => { const world = createWorld(); const before = JSON.stringify({ ...world, disturbed: [...world.disturbed] }); advanceWorld(world, 0); assert.equal(JSON.stringify({ ...world, disturbed: [...world.disturbed] }), before); advanceWorld(world, 120); assert.equal(world.time, .05);});
test('carried objects stay with their sole owner, and core machines return brass when they stop', () => { const world = createWorld(); for (let i = 0; i < 6000; i++) { if (i % 150 === 0) offerObject(world, ['brass', 'shell', 'pebble'][i / 150 % 3], pool.x + (i % 7) - 3, pool.z); advanceWorld(world, 1 / 20); const carried = world.creatures.filter(c => c.carrying !== null); assert.equal(new Set(carried.map(c => c.carrying)).size, carried.length); for (const c of carried) { const o = world.objects.find(o => o.id === c.carrying); assert.equal(o.place, 'carried'); assert.equal(o.claimed, c.id); assert.ok(Math.hypot(o.x - c.x, o.z - c.z) < .7); } } const core = world.creatures.find(c => SPECIES[c.sp].core); if (core) { core.energy = 0; const brass = world.objects.filter(o => o.kind === 'brass').length; advanceWorld(world, .05); assert.ok(!world.creatures.includes(core)); assert.ok(world.objects.filter(o => o.kind === 'brass').length >= brass); }});
test('pylons fold shut when the water is disturbed and reopen once it is still', () => { const world = createWorld(); run(world, 26); const pylon = world.creatures.find(c => c.sp === 'pylon'); assert.equal(pylon.open, 1); dropObject(world, 'pebble', pylon.x + .3, pylon.z); run(world, 1); assert.ok(pylon.open < .3, `open ${pylon.open}`); // Leave it alone: no one else nearby to disturb the water. world.creatures = [pylon]; world.ripples = []; run(world, 15); assert.ok(pylon.open > .6 || depthAt(world, pylon.x, pylon.z) < WET, `open ${pylon.open}`);});
test('a runaway shelf stays bounded: no ground below the digging floor and all water finite', () => { const world = createWorld(99); run(world, 600); for (let k = 0; k < world.h.length; k++) { assert.ok(Number.isFinite(world.h[k]) && Number.isFinite(world.w[k])); assert.ok(world.w[k] >= world.h[k] - 1e-6); } assert.ok(Math.min(...world.h.slice(0, 128 * 60)) > -1.3); assert.ok(TIDE_PERIOD > 0);});
test('artificers compose structures part by part, and every part stands on something', () => { const world = createWorld(3); run(world, 1500); assert.ok(world.blocks.size > 20, `only ${world.blocks.size} parts`); const kinds = new Set([...world.blocks.values()].map(b => b.kind)); for (const kind of ['frame', 'deck']) assert.ok(kinds.has(kind), kind); for (const b of world.blocks.values()) assert.ok(b.s <= 3, `${b.kind} at strain ${b.s}`); assert.ok(world.structures.some(t => t.top > TIDE_HIGH + 1), 'a structure rises above high water');});
// Stand a part directly, as an artificer would have.function part(world, i, j, k, kind) { const key = blockKey(i, j, k); const b = { key, i, j, k, kind, s: 0, born: -10, by: -1, charge: 0, turn: 0, resident: null, wear: 0 }; world.blocks.set(key, b); world.blockVersion++; return b;}
test('a scraper climbs into its pod, and falls when the column under it is knocked out; the falling parts land as scrap', () => { const world = createWorld(); run(world, 1); const i = Math.floor((pool.x + 3 - X0) / CELL), j = Math.floor((pool.z - Z0) / CELL); const ground = Math.floor((world.h[j * 128 + i] - BLOCK_Y0) / BLOCK); const footing = part(world, i, j, ground, 'frame'); for (let k = ground + 1; k < ground + 7; k++) part(world, i, j, k, 'frame'); const pod = part(world, i, j, ground + 7, 'pod'); const scraper = world.creatures.find(c => c.sp === 'scraper'); pod.resident = scraper.id; scraper.pod = pod.key; const at = { x: X0 + (i + .5) * CELL, z: Z0 + (j + .5) * CELL, y: BLOCK_Y0 + pod.k * BLOCK + .05, key: pod.key }; Object.assign(scraper, { task: { kind: 'roost', object: null, prey: null, use: null, at, amount: 0 }, path: [{ x: at.x, z: at.z }], state: 'move', timer: 90, reach: .25 }); for (let s = 0; s < 90 && scraper.perch !== pod.key; s++) run(world, 1); assert.equal(scraper.perch, pod.key, 'it reached its pod'); assert.ok(footAt(world, scraper) > heightAt(world, scraper.x, scraper.z) + 2, 'it is up in the structure'); const scrap = world.objects.filter(o => o.kind === 'scrap').length; removeBlock(world, footing, true); assert.equal(world.blocks.size, 0, 'everything the footing held came down'); assert.ok(world.debris.length > 0, 'the pieces are falling'); run(world, 4); assert.equal(scraper.alt, null, 'it landed'); assert.ok(world.events.some(e => e.type === 'fall')); assert.equal(world.debris.length, 0, 'every piece landed'); assert.ok(world.objects.filter(o => o.kind === 'scrap').length > scrap, 'and some became scrap');});
test('plates standing in the water are solid to it, and hold the ground beneath them', () => { const world = createWorld(); run(world, 1); const i = Math.floor((pool.x - X0) / CELL), j = Math.floor((pool.z - Z0) / CELL), k = j * 128 + i; const ground = Math.floor((world.h[k] - BLOCK_Y0) / BLOCK); part(world, i, j, ground, 'plate'); part(world, i, j, ground + 1, 'plate'); const top = BLOCK_Y0 + (ground + 2) * BLOCK; run(world, 1); assert.ok(Math.abs(world.h[k] + world.wall[k] - top) < 1e-4, 'the wall reaches the top plate'); assert.ok(world.S[k] >= top - 1e-4, 'water must rise over it to pass'); assert.ok(depthAt(world, cellX(i), cellZ(j)) < depthAt(world, cellX(i + 3), cellZ(j)), 'the water over it is shallower'); const before = world.h[k]; run(world, 1); assert.equal(world.h[k], before, 'the ground under it stays put');});