Commit Graph

1 Commits

Author SHA1 Message Date
type-two
d2ab84d549 M12 WIP: oranges and the ribbed pyramid — the reamer twist is causal
The juice station, on the same bones. Press-and-twist reuses the jar's swirl
detector: signed angular sweep of the pointer around the reamer becomes juice
one frame at a time, so twist rate IS juice rate (the feel that had to be right,
brief §9). Dwell is the downforce — holding the half down ramps press, and the
twist while you hold is what juices.

NEW src/sim/juicing.ts (pure, no three — mirrors cutting.ts):
- theoreticalYield(halve) = base·(1−2|offset|)·(1−0.5·wedge): the halve sets the
  reachable ceiling, so a butchered 60/40 caps you at ~70% and the bar can't lie.
- juiceTwist(s, dTheta, press, dt, rand): pulse = sweep·press·ream, capped by
  what's reachable; twist REVERSALS bite a fresh face (a small bonus, real reamer
  physics); pressing WITHOUT twisting mashes → pulp + bench spray + no juice;
  pressing too HARD shoves seeds past the moat; grinding a DRY half reams the pith
  → bitterness that no yield buys back.
- Three tiers as tools (like knives differ): Pyramid Classic (no moat, ream 0.92),
  Ribbed Deluxe (catches ~80%, +4%), The Juicernaut (catches all, +10%, ream 1.32).
- Single game integration point, documented in-file: juiceResult(session) → the
  number-bag; juiceCriteria(result) → The Juice (yield band − bitterness − murk,
  w1.3) + Pips (seed count, w1.1); JUICE_LINES (5 bad / 3 good each) +
  juicerRecognitionLine(). Bench rides the existing benchCriterion via result.bench.

NEW src/scenes/juice.ts: JuiceView mirroring prep.ts — procedural ribbed cone in
a moat dish, glass with a rising juice column, orange half, mess field for spray,
loadProp GLB fallbacks (juicer_pyramid/juice_glass). Live HUD reads the judge's
own numbers: "62% of what this orange had", a ceiling marker the fill can't pass,
pips (click to fish out), bench word, pith warning.

NEW src/dev-juice.ts: SEPARATE installer (no collision with dev.ts) — owns its own
JuiceView, drives the REAL input path (pointer circles for the twist, press
injected like dev.ts injects kitchen.power). Exposes window.__tj. main.ts: one
additive DEV-only import line.

VERIFY (reviewer, ~3 console calls after the dev server loads):
  __tj.enter({ seeds: 5 }); __tj.twist(3, 0.7); __tj.stats()
    → yieldOfOrange ~0.73, pips 4 (Pyramid lets seeds through), bench dirtied
  __tj.enter({ juicer: 'juicernaut', seeds: 5 }); __tj.twist(4, 0.8); __tj.stats()
    → yieldOfReachable ~1.0, pips 0 (catches all), bitterness rises if you grind on
  __tj.enter({ offset: 0.2 }); __tj.twist(6, 0.8); __tj.stats()
    → theoretical ~0.60, yieldOfOrange caps at ~0.60 (the butchered halve)
  __tj.enter(); __tj.mash(1.0, 0.9); __tj.stats()
    → extracted 0, pulp/spray > 0, pips forced out (lean-without-twist = a mash)
  __tj.enter(); __tj.wristle(6, 0.7); __tj.stats()  vs  __tj.twist(3, 0.7)
    → wristle ~0.77 vs twist ~0.73 at equal sweep: reversals pay off

Measured (standalone port of the sim, same constants):
  pyramid 3rev@0.7 → 0.728 | @0.9 → 0.936 | 5rev@0.85 → 1.0 (bitter 0.38, over-reamed)
  deluxe 4rev@0.8 → caught 3/5, 2 pips | juicernaut → caught 5/5, 0 pips
  butcher off .2 → capped 0.60 | mash 1s@0.9 → ext 0, pulp 0.135, 3 pips

tsc --noEmit clean; npm run build clean. Not routed into game.ts yet (game lane's
job) — the harness owns the view so the station is fully drivable now.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 18:48:37 +10:00