Resolve Sprint 8 merge conflicts: week card + forecast bands; pyrrhic assert
hud.js: A's week forecast card now uses C's measured forecastLines with uncertainty bands. balance.test.js: B's measurement-style assert kept (garden half enforced, corner count reported as A's open design call). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
d3bb7c2a93
commit
030349d152
@ -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 `<span class="pip ${now ? 'now' : done ? (held ? 'held' : 'lost') : ''}">${
|
||||
now ? '◆' : done ? (held ? '●' : '○') : '·'}</span>`;
|
||||
=======
|
||||
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 `<button class="storm" data-key="${key}">
|
||||
<div class="name">${f.name || key.replace(/_/g, ' ').toUpperCase()}${f.night ? ' · NIGHT' : ''}</div>
|
||||
<div class="stat">${f.wind}</div>
|
||||
<div class="stat">${f.rain}</div>
|
||||
${f.confidence ? `<div class="stat" style="color:#8ba0ad">${f.confidence}</div>` : ''}
|
||||
</button>`;
|
||||
>>>>>>> origin/lane/c
|
||||
}).join('');
|
||||
|
||||
card.innerHTML = `<div class="card">
|
||||
<h1>NIGHT ${wk.night} OF ${wk.nights}</h1>
|
||||
<div class="pips">${pips}</div>
|
||||
<h2>${(def.name ?? key).replace(/_/g, ' ').toUpperCase()}${night ? ' · NIGHT' : ''}</h2>
|
||||
<div class="stat">sustained to ${peak.toFixed(0)} m/s (${kmh(peak).toFixed(0)} km/h)
|
||||
· gusts to ~${kmh(gustPeak).toFixed(0)} km/h</div>
|
||||
<div class="stat">rain ${rainPeak >= 0.8 ? 'heavy' : rainPeak >= 0.4 ? 'steady' : 'light'}${
|
||||
hail ? ' · hail forecast' : ''}
|
||||
<h2>${(f.name ?? key).replace(/_/g, ' ').toUpperCase()}${night ? ' · NIGHT' : ''}</h2>
|
||||
<div class="stat">${f.wind}</div>
|
||||
<div class="stat">${f.rain}${hail ? ' · hail forecast' : ''}
|
||||
${change ? `· change at ${change.t}s` : '· no change forecast'}</div>
|
||||
${f.confidence ? `<div class="stat" style="color:#8ba0ad">${f.confidence}</div>` : ''}
|
||||
<div class="row" style="margin-top:14px"><span>in the bank</span><b>$${wk.bank}</b></div>
|
||||
<div class="stat" style="color:#8ba0ad;margin-top:10px">
|
||||
${hail
|
||||
|
||||
@ -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}` +
|
||||
|
||||
Loading…
Reference in New Issue
Block a user