diff --git a/web/world/js/hud.js b/web/world/js/hud.js index 040482f..a51f2cd 100644 --- a/web/world/js/hud.js +++ b/web/world/js/hud.js @@ -355,11 +355,12 @@ export function createHud(d) { * @param {{key:string, def:object}[]} storms * @param {(key:string) => void} onPick */ -<<<<<<< HEAD - showForecast({ key, def }, wk, onGo) { - const peak = Math.max(...def.baseCurve.map((p) => p[1])); - const gustPeak = peak + (def.gusts?.powBase ?? 0) + (def.gusts?.powRamp ?? 0); - const rainPeak = Math.max(...(def.rain?.curve ?? [[0, 0]]).map((p) => p[1])); +showForecast({ key, def }, wk, onGo) { + // Integrator merge (Sprint 8): A's week card shape + C's forecastLines — + // MEASURED numbers, banded by lead (the old inline estimate read 30 m/s + // for a storm that really gusts 32.3). Tonight is lead 0 = exact; when + // the week wants to show tomorrow's card, pass its lead and it hedges. + const f = forecastLines(def, 0); const change = (def.events ?? []).find((e) => e.type === 'windchange'); const night = (def.sky?.darkness ?? 0) > 0.6; const hail = !!def.hail; @@ -372,34 +373,16 @@ export function createHud(d) { const held = done && wk.log[i]?.won; return `${ now ? '◆' : done ? (held ? '●' : '○') : '·'}`; -======= - showForecast(storms, onPick) { - // Numbers via Lane C's forecastLines(def, lead): MEASURED rather than - // estimated (the old inline `baseCurve peak + powBase + powRamp` read 30 - // m/s for storm_02, which really gusts to 32.3), and banded by how far out - // the night is. `lead` 0 = tonight = exact, which is what this reads as - // today; pass each night's lead when the week lands and the card starts - // hedging on its own. Same wording as before — only the numbers changed. - const rows = storms.map(({ key, def, lead = 0 }) => { - const f = forecastLines(def, lead); - return ``; ->>>>>>> origin/lane/c }).join(''); card.innerHTML = `

NIGHT ${wk.night} OF ${wk.nights}

${pips}
-

${(def.name ?? key).replace(/_/g, ' ').toUpperCase()}${night ? ' · NIGHT' : ''}

-
sustained to ${peak.toFixed(0)} m/s (${kmh(peak).toFixed(0)} km/h) - · gusts to ~${kmh(gustPeak).toFixed(0)} km/h
-
rain ${rainPeak >= 0.8 ? 'heavy' : rainPeak >= 0.4 ? 'steady' : 'light'}${ - hail ? ' · hail forecast' : ''} +

${(f.name ?? key).replace(/_/g, ' ').toUpperCase()}${night ? ' · NIGHT' : ''}

+
${f.wind}
+
${f.rain}${hail ? ' · hail forecast' : ''} ${change ? `· change at ${change.t}s` : '· no change forecast'}
+ ${f.confidence ? `
${f.confidence}
` : ''}
in the bank$${wk.bank}
${hail diff --git a/web/world/js/tests/balance.test.js b/web/world/js/tests/balance.test.js index ad6e074..73c2215 100644 --- a/web/world/js/tests/balance.test.js +++ b/web/world/js/tests/balance.test.js @@ -315,50 +315,10 @@ export default async function run(t) { // measurement, not the verdict. t.test("balance: storm_02's best line saves the garden (pyrrhic — see SPRINT8 gate 0')", () => { if (!line) throw new Error('the $80 shop cannot buy the candidate line at all'); -<<<<<<< HEAD // The garden half — the part the player is actually protecting — must hold. if (line.hp < 50) { throw new Error(`the wild night's best $${line.spent} line let the garden die: hp=${line.hp} ` + `(need >=50). This is a real balance regression, not the pyrrhic-win question.`); -======= - // SPRINT7 gate 0, settled 2026-07-18: the integrator's skip is deleted - // because the dispute had a cause, not a winner. This suite built skyfx - // WITHOUT a camera, and skyfx.step() opens `if (!camera) return;` — so the - // hail shadow grid was never rebuilt and every loadout was scored as though - // it had no sail. That is why this line read hp 36: 36 IS the bare-bed - // number. With a camera it reads what Lane A measured. Neither harness was - // lying; one of them was flying a yard with no cloth in it. - if (!WIN(line.hp, line.lost)) { - // SPRINT8 gate 0' — CLOSED, and this suite was RIGHT. The live game and this harness now - // agree, measured by Lane D 2026-07-18 on current main (fresh page, A's exact line, - // t2+p3+p4+t2b on 4×shackle+spare, tension 1.0): - // live game → hp 58, 2 lost [t2,t2b], t2 peak 4.55 kN - // this suite → hp 58, 2 lost [t2,t2b], t2 peak 4.54 kN - // Every suite-vs-game difference gate 0' listed has now been tested one at a time and NONE - // of them moves the corner count: live-calm settle (entry 1.94→0.40 kN, verdict unchanged), - // live tree sway (t2 4.33→4.55, verdict unchanged), walked repair timing (unchanged). - // Anchors are bit-identical between harnesses, ring order is identical (p4,t2,t2b,p3), and - // wind.sample() at t2 is identical to 2 dp (36.49 m/s @ t=75.9). - // - // RETRACTED, on the record: Lane D's "the settle flips 2 lost → 1" (SPRINT7). It does not - // reproduce — 0 s and 12 s settle both end 2 lost, because the storm's own peak (4.54 kN at - // t=75.9) dwarfs the attach transient. The settle is a real fidelity fix and NOT the - // corner-count cause. A's hp 58 reproduces exactly; A's "1 lost" reproduces in neither - // harness. - // - // What's left is B's arithmetic, which was correct from the first post: t2 pulls ~4.5 kN and - // a shackle is rated 3.2 kN. The corner cannot hold at any tension, and the spare cannot save - // it — the repaired corner simply blows again (measured: t2b blew @9 s, repaired, blew again). - // So this is no longer an engineering question. It is SPRINT8's designed escape hatch: - // hp 58 CLEARS the 50 bar while 2 corners are lost, i.e. the player saves the garden and - // loses the sail — DESIGN.md's own story — and the win rule (`hp>=50 && lost<2`) calls it a - // loss. Either the rule admits a PYRRHIC WIN, or the shop must be able to buy a third - // above-shackle corner ($80 buys two). That is a design call, not a harness bug. See THREADS. - throw new Error( - `no winnable line: ${COVER_QUAD.join(',')} on 4×shackle+spare ($${line.spent}) ended ` + - `hp=${line.hp}, lost=${line.lost}/4 (need hp>=50, lost<2). CONFIRMED against the live game ` + - `(hp 58, 2 lost) — the harnesses agree, so this is the pyrrhic-win design call, not a bug.`); ->>>>>>> origin/lane/d } const pyrrhic = line.lost >= 2; return `$${line.spent} on ${COVER_QUAD.join(',')} -> garden hp ${line.hp}` +