THE RUSH: two tickets on the rail, and the pass is a clock
Endless days past the card now deal a rush about one day in four (deterministic by day number, never on a Critic day — one cruelty at a time). Ticket one serves into silence: 'on the pass. The second is already up — NEXT. Cook.' Nothing is written. The same day re-deals from a twisted seed — a different order, still reload-proof — and while you cook it, ticket one is dying: -0.5 per minute on the pass, capped at 1.5. The pair closes as ONE ledger line, the mean of what you served and what survived the wait. The write block behind every verdict (total, best, save) lived in nine identical copies at nine call sites; it now lives once, inside recordVerdict, which is what made deferring ticket one a three-line change instead of a surgery. Verified live on day 25: rush banner, ticket one 7.6 parked unwritten, TICKET TWO banner on the re-deal (a prep day — different order confirmed), 155s on the pass decayed 7.6 to 6.3, pair scored 6.7, written once. Rush state clears on setDay, startDaily, and day advance. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
ef66e1aa7a
commit
3ecb6b59a3
@ -98,6 +98,8 @@ export class Game {
|
||||
private demoMode = false;
|
||||
/** The Inspector's Shadow: the kitchen's words go dark; you cook by eye and ear. */
|
||||
private shadowMode = false;
|
||||
/** Rush hour: ticket one's verdict, parked on the pass while you cook two. */
|
||||
private rushFirst: { total: number; seconds: number } | null = null;
|
||||
private demoSnapshot: string | null = null;
|
||||
private rng = new Rng(20260716);
|
||||
private order!: Order;
|
||||
@ -204,6 +206,12 @@ export class Game {
|
||||
this.beginDay();
|
||||
return;
|
||||
}
|
||||
if (this.rushFirst) {
|
||||
// Ticket two of the rush: same day, different deal, the clock restarts
|
||||
// but the FIRST ticket's decay is measured by ticket two's service.
|
||||
this.beginDay();
|
||||
return;
|
||||
}
|
||||
this.save.day++;
|
||||
this.save.maxDay = Math.max(this.save.maxDay, this.save.day);
|
||||
writeSave(this.save);
|
||||
@ -452,10 +460,6 @@ export class Game {
|
||||
const v = judgeBruschetta(slice, this.order, this.kitchen.toolId, seconds, result);
|
||||
this.lastBruschetta = { result, verdict: v };
|
||||
this.recordVerdict(v);
|
||||
this.save.total += v.total;
|
||||
const key = `day${this.order.day}`;
|
||||
this.save.best[key] = Math.max(this.save.best[key] ?? 0, v.total);
|
||||
writeSave(this.save);
|
||||
|
||||
this.hideTempToggle();
|
||||
this.hideAllScenes();
|
||||
@ -514,6 +518,7 @@ export class Game {
|
||||
|
||||
/** Test hook: jump straight to a day, through the real order table. */
|
||||
setDay(day: number): void {
|
||||
this.rushFirst = null;
|
||||
this.save.day = day;
|
||||
this.save.maxDay = Math.max(this.save.maxDay ?? day, day);
|
||||
writeSave(this.save);
|
||||
@ -586,6 +591,7 @@ export class Game {
|
||||
}
|
||||
|
||||
startDaily(): void {
|
||||
this.rushFirst = null;
|
||||
const date = new Date().toISOString().slice(0, 10);
|
||||
this.dailyDate = date;
|
||||
let h = 2166136261;
|
||||
@ -642,10 +648,13 @@ export class Game {
|
||||
// re-deals the identical order (no reroll-scumming), and the harness can
|
||||
// reproduce any day by number.
|
||||
const dayRng = new Rng((day * 2654435761) >>> 0);
|
||||
// Ticket two of a rush re-deals the same day from a twisted seed — a
|
||||
// different order, deterministically, so reloads still can't scum it.
|
||||
const rng2 = this.rushFirst ? new Rng(((day * 2654435761) ^ 0x9e3779b9) >>> 0) : dayRng;
|
||||
const o =
|
||||
day <= DAYS.length
|
||||
? DAYS[day - 1]
|
||||
: proceduralOrder(day, () => dayRng.next());
|
||||
: proceduralOrder(day, () => rng2.next());
|
||||
if (!o.text) o.text = `${o.browningName}, ${o.amount} ${o.spread === 'mitey' ? 'MITEY' : o.spread}. ${o.noChar ? 'No burnt bits.' : ''}`;
|
||||
this.order = o;
|
||||
this.orderSeconds = 0;
|
||||
@ -734,10 +743,6 @@ export class Game {
|
||||
const slice = this.kitchen.currentSlice;
|
||||
const v = judgeBenedict(slice, this.order, poach, seconds);
|
||||
this.recordVerdict(v);
|
||||
this.save.total += v.total;
|
||||
const key = `day${this.order.day}`;
|
||||
this.save.best[key] = Math.max(this.save.best[key] ?? 0, v.total);
|
||||
writeSave(this.save);
|
||||
this.hideAllScenes();
|
||||
this.judgeView.root.visible = true;
|
||||
this.judgeView.show(slice, v, this.order, this.kitchen.toolId, () => this.rng.next());
|
||||
@ -765,10 +770,6 @@ export class Game {
|
||||
const slice = this.kitchen.currentSlice;
|
||||
const v = judgePoach(result, this.order, seconds);
|
||||
this.recordVerdict(v);
|
||||
this.save.total += v.total;
|
||||
const key = `day${this.order.day}`;
|
||||
this.save.best[key] = Math.max(this.save.best[key] ?? 0, v.total);
|
||||
writeSave(this.save);
|
||||
this.hideAllScenes();
|
||||
this.judgeView.root.visible = true;
|
||||
this.judgeView.show(slice, v, this.order, this.kitchen.toolId, () => this.rng.next());
|
||||
@ -797,10 +798,6 @@ export class Game {
|
||||
const slice = this.kitchen.currentSlice;
|
||||
const v = judgeEggs(result, this.order, seconds);
|
||||
this.recordVerdict(v);
|
||||
this.save.total += v.total;
|
||||
const key = `day${this.order.day}`;
|
||||
this.save.best[key] = Math.max(this.save.best[key] ?? 0, v.total);
|
||||
writeSave(this.save);
|
||||
this.hideAllScenes();
|
||||
this.judgeView.root.visible = true;
|
||||
this.judgeView.show(slice, v, this.order, this.kitchen.toolId, () => this.rng.next());
|
||||
@ -840,10 +837,6 @@ export class Game {
|
||||
const slice = this.kitchen.currentSlice;
|
||||
const v = judgeSteak(result, this.order, seconds);
|
||||
this.recordVerdict(v);
|
||||
this.save.total += v.total;
|
||||
const key = `day${this.order.day}`;
|
||||
this.save.best[key] = Math.max(this.save.best[key] ?? 0, v.total);
|
||||
writeSave(this.save);
|
||||
this.hideAllScenes();
|
||||
this.judgeView.root.visible = true;
|
||||
this.judgeView.show(slice, v, this.order, this.kitchen.toolId, () => this.rng.next());
|
||||
@ -874,10 +867,6 @@ export class Game {
|
||||
this.save.fridge = serializeStore(result.store);
|
||||
const v = judgeFridge(result, this.order, seconds);
|
||||
this.recordVerdict(v);
|
||||
this.save.total += v.total;
|
||||
const key = `day${this.order.day}`;
|
||||
this.save.best[key] = Math.max(this.save.best[key] ?? 0, v.total);
|
||||
writeSave(this.save);
|
||||
this.hideAllScenes();
|
||||
this.judgeView.root.visible = true;
|
||||
this.judgeView.show(slice, v, this.order, this.kitchen.toolId, () => this.rng.next());
|
||||
@ -926,10 +915,6 @@ export class Game {
|
||||
const slice = this.kitchen.currentSlice;
|
||||
const v = judgePan(result, this.order, seconds, this.panIngredient ?? undefined);
|
||||
this.recordVerdict(v);
|
||||
this.save.total += v.total;
|
||||
const key = `day${this.order.day}`;
|
||||
this.save.best[key] = Math.max(this.save.best[key] ?? 0, v.total);
|
||||
writeSave(this.save);
|
||||
this.hideAllScenes();
|
||||
this.judgeView.root.visible = true;
|
||||
this.judgeView.show(slice, v, this.order, this.kitchen.toolId, () => this.rng.next());
|
||||
@ -957,10 +942,6 @@ export class Game {
|
||||
const slice = this.kitchen.currentSlice;
|
||||
const v = judgeGrill(result, this.order, seconds);
|
||||
this.recordVerdict(v);
|
||||
this.save.total += v.total;
|
||||
const key = `day${this.order.day}`;
|
||||
this.save.best[key] = Math.max(this.save.best[key] ?? 0, v.total);
|
||||
writeSave(this.save);
|
||||
|
||||
this.hideAllScenes();
|
||||
this.judgeView.root.visible = true;
|
||||
@ -1065,8 +1046,38 @@ export class Game {
|
||||
d[this.dailyDate] = Math.max(d[this.dailyDate] ?? 0, v.total);
|
||||
extras.push('The Daily. Same deal for everyone today — come back tomorrow.');
|
||||
}
|
||||
// RUSH HOUR (endless only): the day dealt TWO tickets. The first verdict
|
||||
// goes on the pass; the second closes the pair — and the first decays for
|
||||
// every second it sat there while you cooked. One combined line in the book.
|
||||
if (this.rushDay(this.order.day) && !this.dailyDate) {
|
||||
if (!this.rushFirst) {
|
||||
this.rushFirst = { total: v.total, seconds: this.orderSeconds };
|
||||
extras.push('Ticket one, on the pass. The second is already up — NEXT. Cook.');
|
||||
this.judgeView.extraLines = extras;
|
||||
this.judgeView.shareText = '';
|
||||
return; // nothing is written until the pair closes
|
||||
}
|
||||
const passSec = this.orderSeconds;
|
||||
const decay = Math.min(1.5, (passSec / 60) * 0.5);
|
||||
const aAdj = Math.max(0, Math.round((this.rushFirst.total - decay) * 10) / 10);
|
||||
extras.push(`Ticket one sat ${Math.round(passSec)}s on the pass: ${this.rushFirst.total.toFixed(1)} fell to ${aAdj.toFixed(1)}.`);
|
||||
v.total = Math.round(((aAdj + v.total) / 2) * 10) / 10;
|
||||
v.grade = gradeOf(v.total);
|
||||
extras.push('Two tickets, one verdict — the rush is scored as a pair.');
|
||||
this.rushFirst = null;
|
||||
}
|
||||
this.judgeView.extraLines = extras;
|
||||
this.judgeView.shareText = this.shareLine(v);
|
||||
this.save.total += v.total;
|
||||
const key = `day${this.order.day}`;
|
||||
this.save.best[key] = Math.max(this.save.best[key] ?? 0, v.total);
|
||||
writeSave(this.save);
|
||||
}
|
||||
|
||||
/** Endless-only, deterministic by day number: ~1 in 4 days past the card
|
||||
* are a rush, never on a Critic day (one cruelty at a time). */
|
||||
private rushDay(day: number): boolean {
|
||||
return day > DAYS.length && day % 10 !== 0 && ((day * 2654435761) >>> 0) % 4 === 1;
|
||||
}
|
||||
|
||||
private renderTicket(): void {
|
||||
@ -1075,6 +1086,7 @@ export class Game {
|
||||
el('div', 'ticket-day', this.ticketEl, `DAY ${o.day}`);
|
||||
el('div', 'ticket-who', this.ticketEl, o.who);
|
||||
if (o.day % 10 === 0 && !this.dailyDate) el('div', 'ticket-critic', this.ticketEl, 'THE CRITIC is in today. The flaw will be the headline.');
|
||||
if (this.rushDay(o.day) && !this.dailyDate) el('div', 'ticket-critic', this.ticketEl, this.rushFirst ? 'TICKET TWO — the first is dying on the pass.' : 'RUSH HOUR — two tickets on the rail. The first will wait for the second.');
|
||||
const demos = (window as unknown as { __t?: { demos?: Record<number, unknown> } }).__t?.demos;
|
||||
if (!this.demoMode && demos && demos[o.day]) {
|
||||
const watch = el('button', 'btn ghost watch-btn', this.ticketEl, '▶ watch him do it');
|
||||
@ -1150,10 +1162,6 @@ export class Game {
|
||||
const extra = this.juiceResult ? juiceCriteria(this.juiceResult) : [];
|
||||
const v = judge(slice, this.order, this.kitchen.toolId, seconds, this.prepResult ?? undefined, extra);
|
||||
this.recordVerdict(v);
|
||||
this.save.total += v.total;
|
||||
const key = `day${this.order.day}`;
|
||||
this.save.best[key] = Math.max(this.save.best[key] ?? 0, v.total);
|
||||
writeSave(this.save);
|
||||
|
||||
this.kitchen.root.visible = false;
|
||||
this.judgeView.root.visible = true;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user