:PROPERTIES: :ID: 9465af82-4383-466c-bf09-5be19c328f0b :END: #+title: trmn/life #+filetags: :project: :knowledge: :life: ** Architecture ~life.js~ is a standalone ES module (no dependencies) that exports: - ~simulateLife(masterSeed)~ → ~{ traits, buf, eventLog, people }~ - ~getDay(buf, day)~ → plain object (unpack one day from typed array) - ~makeRng(masterSeed, day)~ → mulberry32 closure (day-local PRNG) - ~DAYS~ (36501), ~FIELDS~ (22), ~F~ (field index map) State is packed into ~Float32Array(DAYS * FIELDS)~ (~3.2MB). ~eventLog~ is ~Map>~ (entries are objects, NOT plain strings — changed when NPC system was added). ~people~ is ~Map~ for all tracked NPCs. ~window.__life~ exposes ~{ traits, buf, eventLog, people, today, currentDay, masterSeed }~. ** PRNG mulberry32 seeded per-day via splitmix32 hash: #+begin_src js makeRng(masterSeed, day) // derives seed from hash(masterSeed ^ day*0x9e3779b9) #+end_src - ~Math.imul~ throughout — bitwise-identical on all IEEE-754 JS engines - Any day independently computable; no need to simulate prior days - masterSeed = ~(Date.now() / 1000) | 0~ (wall-clock seconds) - Reserved day slots: ~-1~ = protagonist traits; ~-2~ = family layout offsets (NPC system) - NPC streams are fully isolated — see [[id:423cac95-a80d-4db6-8bef-14297fb38437][trmn/people]] ** State Fields (22 packed floats per day) | Index | Field | Notes | |-------|-------|-------| | 0 | alive | 0/1 | | 1 | health | 0–100 | | 2–4 | sick, disabled, chronicDisease | 0/1 flags | | 5–7 | eduStage (0–6), inCollege, collegeDropout | | | 8–11 | employed, jobTier (0–4), incomeLevel, retired | | | 12–16 | inRelationship, married, everDivorced, divorceCount, widowed | | | 17–21 | numChildren, friendCount, loneliness, wealthLevel, debtLevel | | Fixed-at-birth traits (not packed): ~sex, introversion, ambition, resilience, baseHealth~ Internal-only (not packed): ~friendList, friendLifetimeIndex, childrenList, childLifetimeIndex~ ** Scheduled Events (deterministic) birth(0), kindergarten(1826), primary(3652), middle(5113), hs_grad(6575), college_grad(8036), retirement(23725), death_cap(36500) Plus dynamically injected ~sibling_born~ entries (built inside ~simulateLife()~, merged + sorted into ~mergedSchedule~). ** Random Events (31 total, evaluated in priority order, cap 2/day) Categories: death, disaster, health, financial, relationship, family, career, social Death uses Gompertz curve: ~0.000008 * exp((age-40)/60 * 5) * healthMod * resilienceMod~ NPC-triggered events (not in EVENTS array): ~family_member_death~ (day loop step 4), ~friend_death~ (post-apply of lose_friend) ** Gotchas - NEVER spread ~buf~ inside a loop — ~[...buf]~ on 800k-element Float32Array per iteration hangs the browser. Use ~buf[d * FIELDS + fieldIdx]~ directly. - ~life.js~ must be listed in ~flake.nix~ installPhase (~cp $src/life.js $out/~) or Caddy serves a 404. - eventLog entries are now ~{id, personId?, attended?}~ objects — any consumer using ~evs.includes('event_id')~ or ~counts[ev]~ will silently break. Use ~e.id~. - ~breakup~ and ~marry~ both have weight 0.0008/day → equal competition → many characters die "in relationship, not married". May need tuning. - "Unexpected end of input" on ~import('./life.js')~ usually means 404 (not rebuilt yet), not a real syntax error. ** Console Snippets #+begin_src js // Import and run const { simulateLife, getDay, DAYS, FIELDS } = await import('/life.js'); const sim = simulateLife(42); // Readable timeline (eventLog entries are objects now) const timeline = [...sim.eventLog.entries()].map(([d, evs]) => ({ age: (d / 365.25 | 0), day: d, events: evs.map(e => e.id).join(', ') })); console.table(timeline); // Inspect people (family + friends + children) [...sim.people.entries()].forEach(([id, p]) => console.log(id, 'sex', p.sex, 'born', p.birthDay, 'dies', p.deathDay) ); // Find death day (safe — no buf spreading) const death = Array.from({length: DAYS}, (_, d) => d).find(d => d > 0 && sim.buf[d * FIELDS] === 0); console.log('died age', death && (death / 365.25 | 0)); #+end_src ** Subnodes - [[id:423cac95-a80d-4db6-8bef-14297fb38437][trmn/people]] — NPC system: PersonRecord schema, PRNG isolation, family generation, post-apply hooks, gotchas