Heat M-H6 shipped: the charcoal grill is a playable day (day 15)
Routed the grill into a real order: day 15 'chuck these on the barbie'
sends the whole day outdoors — bank the coals hot one side / cool the
other, sear each piece then move it to the lee before the fat flares,
serve. judgeGrill scores THE SEAR / THE CHAR / THE GRILL with a char-snob
line bank ('There is no char on this. There is a memory of char. A
rumour.'). New scorecard group THE GRILL + grill/bruschetta order headers.
Balance retune (the floor): RENDER_AT 0.62 lifted above the good-sear
window so a clean sear is reachable with NO flare — sear-then-pull is a
reliable win, only overcooking invites the flames.
Verified end-to-end in-browser: day 15 routes to the grill; careful
sear-and-pull grades S (9.4/10), 100% well-seared, no carbon, no flares;
leaving pieces over the coals chars to carbon (0.9/10, 31 flares). All
M-H6 sim bars still green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
ff1c3deafc
commit
94a5f2f293
@ -7,7 +7,8 @@
|
|||||||
- **M-H1 + M-H2 DONE & LIVE (2026-07-19).** `src/sim/heatsource.ts` (the bone) + the golden-preserving oven retrofit + the gas-oven day are shipped. Verified in-browser: **golden** — no-source oven reads mean 0.698 / blister 1.0 / collapse 0, byte-identical to HEAD. **M-H1 chase 3→9** — gas 0.433s, electric 6.0s, induction 0.133s (distinct, no overlap). **Residual after 9→0** — gas 0 @1s, electric 6.6 @6s, charcoal 9 @30s. **M-H2** — gas oven stdev 0.1207 vs fan oven 0.0016 at equal mean 0.6001 (75× ratio; bar was 2×). Day 14 bruschetta now roasts on `oven_gas` and still grades **S (9.62)**, HUD shows "GAS OVEN · blue flame". Harness: `t.heatChase`, `t.residual`, `t.ovenStdev`, `t.ovenGolden`, `t.ovenDemo`.
|
- **M-H1 + M-H2 DONE & LIVE (2026-07-19).** `src/sim/heatsource.ts` (the bone) + the golden-preserving oven retrofit + the gas-oven day are shipped. Verified in-browser: **golden** — no-source oven reads mean 0.698 / blister 1.0 / collapse 0, byte-identical to HEAD. **M-H1 chase 3→9** — gas 0.433s, electric 6.0s, induction 0.133s (distinct, no overlap). **Residual after 9→0** — gas 0 @1s, electric 6.6 @6s, charcoal 9 @30s. **M-H2** — gas oven stdev 0.1207 vs fan oven 0.0016 at equal mean 0.6001 (75× ratio; bar was 2×). Day 14 bruschetta now roasts on `oven_gas` and still grades **S (9.62)**, HUD shows "GAS OVEN · blue flame". Harness: `t.heatChase`, `t.residual`, `t.ovenStdev`, `t.ovenGolden`, `t.ovenDemo`.
|
||||||
- **M-H3 DONE (2026-07-19).** Environment sim (gust walk, flame perturbation, ferrous rejection, indoor smoke→ZAP). Verified: windy gas flame stdev 0.2635 (bar >0.05) and mean drops to 5.66 (wind steals heat); sheltered stdev 0.0065 (bar <0.02, the dodgeable floor); induction-in-wind stdev 0 (no flame); the ZAP fires exactly once indoors, never outdoors. `newOutdoorEnv`, `addSmoke`, `SMOKE_ZAP`. Harness: `t.envVar`, `t.zapTest`.
|
- **M-H3 DONE (2026-07-19).** Environment sim (gust walk, flame perturbation, ferrous rejection, indoor smoke→ZAP). Verified: windy gas flame stdev 0.2635 (bar >0.05) and mean drops to 5.66 (wind steals heat); sheltered stdev 0.0065 (bar <0.02, the dodgeable floor); induction-in-wind stdev 0 (no flame); the ZAP fires exactly once indoors, never outdoors. `newOutdoorEnv`, `addSmoke`, `SMOKE_ZAP`. Harness: `t.envVar`, `t.zapTest`.
|
||||||
- **M-H5 (noise half) DONE.** cheap_electric hob stdev 0.1244 (bar >0.12) vs induction 0.0198 (bar <0.04) vs fan 0.0064. Harness: `t.hobNoise`. The "both reach 10/10" half needs the M17 pan to cook on — deferred with it.
|
- **M-H5 (noise half) DONE.** cheap_electric hob stdev 0.1244 (bar >0.12) vs induction 0.0198 (bar <0.04) vs fan 0.0064. Harness: `t.hobNoise`. The "both reach 10/10" half needs the M17 pan to cook on — deferred with it.
|
||||||
- **NEXT: M-H6 (charcoal — a heat-bed you arrange: the showpiece outdoor grill scene). M-H4 (pan/butter/flare) waits on M17 (Brief 3).** Everything below §2 not checked off here is unbuilt.
|
- **M-H6 DONE & LIVE (2026-07-19) — the charcoal grill, a heat-bed you arrange.** `src/sim/charcoal.ts` (bankCoals paints a 2D heat bed, zoneDecayStep only cools it, food sears by the coals beneath it, a rendering piece over hot coals flares and chars until you move it) + `src/scenes/grill.ts` (top-down open grill, the bed rendered as an additive glow, food dragged between the sear zone and the cool lee, flames lick up on flare) + the routed **day 15 BBQ order** with `judgeGrill` (THE SEAR / THE CHAR / THE GRILL) and the char-snob line bank. Verified: two-zone bed stdev 0.303; bed ceiling monotone-decays 1.68→0.61/90s; flare dodge — left over coals = done 2.08/31 flares (carbon), pulled to the lee = 0/0 clean; **the full day plays and grades S (9.4/10)** with careful sear-then-pull, F if you let it flare. Retune: RENDER_AT 0.62 (above the good-sear window's floor) so a clean sear is reachable with no flare — the floor holds (active play wins, neglect chars). Harness: `t.zoneStdev`, `t.zoneDecay`, `t.grillTwoZones`, `t.grillFlareDodge`, `t.grillDemo`, `t.grillDay`.
|
||||||
|
- **NEXT: M-H4 (pan/butter/flare) waits on M17 (Brief 3 — the pan itself). Environment bolts onto the grill next (wind/windbreak already in `heatsource.ts`, not yet wired to the grill's env).** Everything below §2 not checked off here is unbuilt.
|
||||||
- Presupposes the M16 salt doctrine and the M17 pan skeleton from Brief 3; those aren't in yet, so §2 ships as a standalone `heatsource.ts` (zero dependency on the pan) and M17 will read it from birth.
|
- Presupposes the M16 salt doctrine and the M17 pan skeleton from Brief 3; those aren't in yet, so §2 ships as a standalone `heatsource.ts` (zero dependency on the pan) and M17 will read it from birth.
|
||||||
- **LIVE 🌐** https://partly.party/toastsim/ — `bash scripts/deploy.sh` (build `--base=/toastsim/` → rsync staging → docker cp into `forum-nginx`). Curl cache-busted after every deploy.
|
- **LIVE 🌐** https://partly.party/toastsim/ — `bash scripts/deploy.sh` (build `--base=/toastsim/` → rsync staging → docker cp into `forum-nginx`). Curl cache-busted after every deploy.
|
||||||
- Seed lane for this brief's assets: **680–719** (open gap after P12's 630–679). A `do_heat()` lane (cooktops/pans/BBQ/utensils) was already fired at seeds 181–219 for the general cast; the 680–719 sprites (flames, glows, flare, ZAP) remain.
|
- Seed lane for this brief's assets: **680–719** (open gap after P12's 630–679). A `do_heat()` lane (cooktops/pans/BBQ/utensils) was already fired at seeds 181–219 for the general cast; the 680–719 sprites (flames, glows, flare, ZAP) remain.
|
||||||
|
|||||||
27
src/dev.ts
27
src/dev.ts
@ -621,6 +621,33 @@ export function installDevHarness(app: App, game: Game): void {
|
|||||||
return { indoorFires: fires, outdoorFires: outFires, finalSmoke: +env.smoke.toFixed(2) };
|
return { indoorFires: fires, outdoorFires: outFires, finalSmoke: +env.smoke.toFixed(2) };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Play the whole day-15 grill order: route in, bank a two-zone bed, sear
|
||||||
|
* each piece then move it to the lee, serve, and read the scorecard. */
|
||||||
|
grillDay() {
|
||||||
|
game.setDay(15);
|
||||||
|
run(5);
|
||||||
|
const g = game.grillView as unknown as { session: import('./sim/charcoal').GrillSession; result(): unknown };
|
||||||
|
const s = g.session;
|
||||||
|
if (!s) return { error: 'not at grill', view: 'other' };
|
||||||
|
// Bank hot on the left, cool on the right.
|
||||||
|
for (let k = 0; k < 45; k++) s.zone.brush(0.3, 0.5, 0.24, (i, w) => { s.zone.data[i] = Math.min(1.8, s.zone.data[i] + 0.12 * w); });
|
||||||
|
// Sear each piece over the coals, staggered, then shuffle it to the lee.
|
||||||
|
const lanes = [0.4, 0.5, 0.6];
|
||||||
|
for (let i = 0; i < s.foods.length; i++) { s.foods[i].u = 0.3; s.foods[i].v = lanes[i] ?? 0.5; }
|
||||||
|
for (let step = 0; step < 30; step++) {
|
||||||
|
// pull each piece to the lee the moment it hits a good sear — before its
|
||||||
|
// fat renders and flares (the whole technique).
|
||||||
|
for (const f of s.foods) if (f.sear >= 0.56 && f.u < 0.6) f.u = 0.82;
|
||||||
|
run(30); // half a second — tighter cadence so we catch the window
|
||||||
|
}
|
||||||
|
tap('Enter');
|
||||||
|
run(5);
|
||||||
|
const groups = [...document.querySelectorAll('.crit-group')].map((e) => e.textContent);
|
||||||
|
const rows = [...document.querySelectorAll('.crit')].map((e) => e.textContent);
|
||||||
|
const gradeEl = document.querySelector('.judge-grade, .grade');
|
||||||
|
return { grade: gradeEl ? gradeEl.textContent : null, groups, rows };
|
||||||
|
},
|
||||||
|
|
||||||
/** Drive the live grill scene: bank a hot-left/cool-right bed, sear a piece
|
/** Drive the live grill scene: bank a hot-left/cool-right bed, sear a piece
|
||||||
* over the coals then shuffle it to the lee, hold another on the cool side.
|
* over the coals then shuffle it to the lee, hold another on the cool side.
|
||||||
* For a screenshot + an end-to-end check of the real scene path. */
|
* For a screenshot + an end-to-end check of the real scene path. */
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import { juiceCriteria, type JuiceResult } from '../sim/juicing';
|
|||||||
import { TempClock } from '../sim/tempclock';
|
import { TempClock } from '../sim/tempclock';
|
||||||
import type { RoastResult } from '../sim/roasting';
|
import type { RoastResult } from '../sim/roasting';
|
||||||
import type { AssemblyResult } from '../sim/assembly';
|
import type { AssemblyResult } from '../sim/assembly';
|
||||||
import { judgeBruschetta, type BruschettaResult, type PrepResult, type Verdict } from './judging';
|
import { judgeBruschetta, judgeGrill, type BruschettaResult, type PrepResult, type Verdict } from './judging';
|
||||||
import { JudgeView } from '../scenes/judge';
|
import { JudgeView } from '../scenes/judge';
|
||||||
import { DrawerView } from '../scenes/drawer';
|
import { DrawerView } from '../scenes/drawer';
|
||||||
import { TOOLS, type ToolId } from '../sim/cutlery';
|
import { TOOLS, type ToolId } from '../sim/cutlery';
|
||||||
@ -484,9 +484,45 @@ export class Game {
|
|||||||
this.runNextPrep();
|
this.runNextPrep();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// A grill day goes straight outdoors to the coals — no toaster at all.
|
||||||
|
if (this.order.grill) {
|
||||||
|
this.enterGrill();
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.enterToastFlow();
|
this.enterToastFlow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The charcoal grill day (M-H6): arrange the coals, cook on the gradient, serve. */
|
||||||
|
private enterGrill(): void {
|
||||||
|
this.grill.reset(this.order.grill!);
|
||||||
|
this.hideAllScenes();
|
||||||
|
this.grill.root.visible = true;
|
||||||
|
this.app.setView(this.grill);
|
||||||
|
this.ticket.show();
|
||||||
|
this.grill.onDone = (result) => {
|
||||||
|
this.grill.root.visible = false;
|
||||||
|
this.serveGrill(result);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private serveGrill(result: ReturnType<GrillView['result']>): void {
|
||||||
|
this.timing = false;
|
||||||
|
const seconds = this.orderSeconds;
|
||||||
|
const slice = this.kitchen.currentSlice;
|
||||||
|
const v = judgeGrill(result, this.order, seconds);
|
||||||
|
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());
|
||||||
|
audio.stamp();
|
||||||
|
this.app.setView(this.judgeView);
|
||||||
|
this.ticket.hide();
|
||||||
|
}
|
||||||
|
|
||||||
/** The toaster half of the day: the loaf knife-trip, or straight to the bench. */
|
/** The toaster half of the day: the loaf knife-trip, or straight to the bench. */
|
||||||
private enterToastFlow(): void {
|
private enterToastFlow(): void {
|
||||||
if (this.order.fromLoaf) {
|
if (this.order.fromLoaf) {
|
||||||
|
|||||||
@ -7,6 +7,8 @@ import type { RoastResult } from '../sim/roasting';
|
|||||||
import { BLISTER_AT, COLLAPSE_AT } from '../sim/roasting';
|
import { BLISTER_AT, COLLAPSE_AT } from '../sim/roasting';
|
||||||
import type { AssemblyResult } from '../sim/assembly';
|
import type { AssemblyResult } from '../sim/assembly';
|
||||||
import { MASS_BAND, RUB_TARGET, sogWord } from '../sim/assembly';
|
import { MASS_BAND, RUB_TARGET, sogWord } from '../sim/assembly';
|
||||||
|
import type { GrillResult } from '../sim/charcoal';
|
||||||
|
import { grillWord } from '../sim/charcoal';
|
||||||
|
|
||||||
export interface Criterion {
|
export interface Criterion {
|
||||||
key: string;
|
key: string;
|
||||||
@ -19,7 +21,7 @@ export interface Criterion {
|
|||||||
/** Which station earned it — the scorecard groups by this on prep orders.
|
/** Which station earned it — the scorecard groups by this on prep orders.
|
||||||
* M15 adds the bruschetta trio: bread (cut+toast+rub), topping (roast,
|
* M15 adds the bruschetta trio: bread (cut+toast+rub), topping (roast,
|
||||||
* distribution, balance, sog), bench (temperature + mess). */
|
* distribution, balance, sog), bench (temperature + mess). */
|
||||||
group?: 'prep' | 'toast' | 'spread' | 'bread' | 'topping' | 'bench';
|
group?: 'prep' | 'toast' | 'spread' | 'bread' | 'topping' | 'bench' | 'grill';
|
||||||
}
|
}
|
||||||
|
|
||||||
/** What the prep bench hands the judge, when the order had prep on it. */
|
/** What the prep bench hands the judge, when the order had prep on it. */
|
||||||
@ -438,6 +440,59 @@ export function gradeOf(total: number): Grade {
|
|||||||
return 'F';
|
return 'F';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =====================================================================
|
||||||
|
// M-H6 — The charcoal grill. One bed you arranged, judged: did the pieces land
|
||||||
|
// a proper sear (not raw, not carbon), and did you keep the flares off them.
|
||||||
|
|
||||||
|
export function judgeGrill(r: GrillResult, order: Order, seconds: number): Verdict {
|
||||||
|
const criteria: Criterion[] = [
|
||||||
|
{
|
||||||
|
key: 'sear',
|
||||||
|
group: 'grill',
|
||||||
|
label: 'The Sear',
|
||||||
|
// How many pieces landed the good window — an under-seared piece reads here,
|
||||||
|
// and an uneven bed (one great, one raw) reads in the spread.
|
||||||
|
score: clamp01(r.searedFrac * (1 - r.stdev * 0.35)),
|
||||||
|
weight: 1.4,
|
||||||
|
detail: `${grillWord(r)} (${Math.round(r.searedFrac * 100)}% well-seared)`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'grillchar',
|
||||||
|
group: 'grill',
|
||||||
|
label: 'The Char',
|
||||||
|
score: clamp01(1 - r.charFrac * 1.5),
|
||||||
|
weight: 1.0,
|
||||||
|
detail: r.charFrac > 0.02 ? `${Math.round(r.charFrac * 100)}% carbon` : 'no carbon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'grillbench',
|
||||||
|
group: 'bench',
|
||||||
|
label: 'The Grill',
|
||||||
|
score: clamp01(1 - r.flares * 0.1 - r.ash * 1.5),
|
||||||
|
weight: 0.7,
|
||||||
|
detail: r.flares > 0 ? `${r.flares} flare-up${r.flares > 1 ? 's' : ''}` : 'no flare-ups',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'time',
|
||||||
|
label: 'Service',
|
||||||
|
score: clamp01(1 - (seconds - 55) / 90),
|
||||||
|
weight: 0.3,
|
||||||
|
detail: `${Math.round(seconds)}s`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let sum = 0;
|
||||||
|
let wsum = 0;
|
||||||
|
for (const c of criteria) {
|
||||||
|
sum += c.score * c.weight;
|
||||||
|
wsum += c.weight;
|
||||||
|
}
|
||||||
|
const total = (sum / wsum) * 10;
|
||||||
|
const rankable = criteria.filter((c) => c.key !== 'time');
|
||||||
|
const sorted = [...rankable].sort((a, b) => a.score - b.score);
|
||||||
|
void order;
|
||||||
|
return { criteria, total, grade: gradeOf(total), best: sorted[sorted.length - 1], worst: sorted[0], lines: [] };
|
||||||
|
}
|
||||||
|
|
||||||
// =====================================================================
|
// =====================================================================
|
||||||
// M15 — Bruschetta. The capstone verdict, grouped The Bread / The Topping /
|
// M15 — Bruschetta. The capstone verdict, grouped The Bread / The Topping /
|
||||||
// The Bench. Every row is a number some station computed — the cut and toast
|
// The Bench. Every row is a number some station computed — the cut and toast
|
||||||
|
|||||||
@ -300,6 +300,42 @@ const BANK: Bank = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The charcoal grill's rows — the deadpan char snob.
|
||||||
|
Object.assign(BANK, {
|
||||||
|
sear: {
|
||||||
|
bad: [
|
||||||
|
'There is no char on this. There is a memory of char. A rumour.',
|
||||||
|
'This never met the coals. It was in the same postcode as the coals.',
|
||||||
|
'One side is Sunday. The other side is a house fire. Commit to a Tuesday.',
|
||||||
|
'You had a screaming bed and a cool lee and you used neither. Bold. Wrong.',
|
||||||
|
'Raw. Not rare. Raw. There is a word for it and it is that one.',
|
||||||
|
],
|
||||||
|
good: [
|
||||||
|
'A proper sear. You arranged the coals and you moved with intent. I saw it.',
|
||||||
|
'That is a sear. Crust where crust belongs. I am almost moved.',
|
||||||
|
'You seared this over charcoal you built with genuine care. Almost.',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
grillchar: {
|
||||||
|
bad: [
|
||||||
|
'Flare-up. You let the fat conduct the orchestra. It chose inferno.',
|
||||||
|
'This is carbon. Delicious, structural carbon. Inedible.',
|
||||||
|
'You watched it flare and you did nothing. We both watched. Only one of us learned.',
|
||||||
|
],
|
||||||
|
good: [
|
||||||
|
'No carbon. You dodged the flares. That is the whole discipline, right there.',
|
||||||
|
'Charred where it should be, nowhere it should not. Correct.',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
grillbench: {
|
||||||
|
bad: [
|
||||||
|
'The flare-ups. I counted them. So did the smoke detector, spiritually.',
|
||||||
|
'Ash in the sear. The wind had opinions and you had no windbreak.',
|
||||||
|
],
|
||||||
|
good: ['A clean bed, no flares. You cooked, you did not fight a fire.', 'Tidy coals. Tidy cook.'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// The juicer's two rows (The Juice + Pips) ship their lines with the sim, so the
|
// The juicer's two rows (The Juice + Pips) ship their lines with the sim, so the
|
||||||
// station owns its whole voice. Merge them in rather than duplicate them here.
|
// station owns its whole voice. Merge them in rather than duplicate them here.
|
||||||
Object.assign(BANK, JUICE_LINES);
|
Object.assign(BANK, JUICE_LINES);
|
||||||
|
|||||||
@ -96,6 +96,9 @@ export interface Order {
|
|||||||
prep?: PrepStep[];
|
prep?: PrepStep[];
|
||||||
/** The bruschetta capstone: roast + assemble replace the spread flow. */
|
/** The bruschetta capstone: roast + assemble replace the spread flow. */
|
||||||
bruschetta?: BruschettaSpec;
|
bruschetta?: BruschettaSpec;
|
||||||
|
/** The charcoal grill (M-H6): the food to cook over the coals. Presence sends
|
||||||
|
* the whole day to the outdoor grill instead of the toaster. */
|
||||||
|
grill?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BROWNING_NAMES: [number, string][] = [
|
export const BROWNING_NAMES: [number, string][] = [
|
||||||
@ -345,6 +348,23 @@ export const DAYS: Order[] = [
|
|||||||
// order start; take the brie out early, leave the parmesan in.
|
// order start; take the brie out early, leave the parmesan in.
|
||||||
bruschetta: { roastHalves: 4, perishables: ['brie', 'parmesan'], oven: 'oven_gas' },
|
bruschetta: { roastHalves: 4, perishables: ['brie', 'parmesan'], oven: 'oven_gas' },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
day: 15,
|
||||||
|
brand: 'the bakery loaf',
|
||||||
|
bread: 'white',
|
||||||
|
// The grill day routes entirely to the outdoor coals — the toast defaults are
|
||||||
|
// never read, same as the bruschetta order.
|
||||||
|
spread: 'butter',
|
||||||
|
amount: 'normal',
|
||||||
|
browning: 0.55,
|
||||||
|
browningName: 'golden',
|
||||||
|
noChar: true,
|
||||||
|
tool: 'butter_knife',
|
||||||
|
butterSoftness: 0.5,
|
||||||
|
who: 'the bloke from two doors down',
|
||||||
|
text: "Chuck these on the barbie, love. Bank the coals hot one side, cool the other — sear 'em then move 'em off before they flare. A proper char, not a cremation.",
|
||||||
|
grill: ['steak', 'snag', 'corn'],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
/** Days beyond the script — keep going, with everything cranked. */
|
/** Days beyond the script — keep going, with everything cranked. */
|
||||||
|
|||||||
@ -103,7 +103,13 @@ export class JudgeView implements View {
|
|||||||
slice.mesh.rotation.set(0, 0, 0);
|
slice.mesh.rotation.set(0, 0, 0);
|
||||||
slice.mesh.scale.setScalar(1.55);
|
slice.mesh.scale.setScalar(1.55);
|
||||||
|
|
||||||
this.orderEl.textContent = `Day ${order.day} · ${order.who} asked for ${order.browningName}, ${order.amount} ${order.spread === 'mitey' ? 'MITEY' : order.spread}`;
|
// Grill and bruschetta days don't have a browning/spread to name — show who
|
||||||
|
// asked and what for, not the unread toast defaults.
|
||||||
|
this.orderEl.textContent = order.grill
|
||||||
|
? `Day ${order.day} · ${order.who} asked for the barbie`
|
||||||
|
: order.bruschetta
|
||||||
|
? `Day ${order.day} · ${order.who} asked for the bruschetta`
|
||||||
|
: `Day ${order.day} · ${order.who} asked for ${order.browningName}, ${order.amount} ${order.spread === 'mitey' ? 'MITEY' : order.spread}`;
|
||||||
|
|
||||||
clear(this.cardEl);
|
clear(this.cardEl);
|
||||||
// On prep orders the card is long enough to need signposts: group rows by
|
// On prep orders the card is long enough to need signposts: group rows by
|
||||||
@ -111,7 +117,7 @@ export class JudgeView implements View {
|
|||||||
// Station orders (prep, and M15's bruschetta trio) get the grouped card;
|
// Station orders (prep, and M15's bruschetta trio) get the grouped card;
|
||||||
// a plain toast order keeps the flat card it always had.
|
// a plain toast order keeps the flat card it always had.
|
||||||
const grouped = verdict.criteria.some(
|
const grouped = verdict.criteria.some(
|
||||||
(c) => c.group === 'prep' || c.group === 'bread' || c.group === 'topping' || c.group === 'bench',
|
(c) => c.group === 'prep' || c.group === 'bread' || c.group === 'topping' || c.group === 'bench' || c.group === 'grill',
|
||||||
);
|
);
|
||||||
const headers: Record<string, string> = {
|
const headers: Record<string, string> = {
|
||||||
prep: 'THE PREP',
|
prep: 'THE PREP',
|
||||||
@ -119,9 +125,10 @@ export class JudgeView implements View {
|
|||||||
spread: 'THE SPREAD',
|
spread: 'THE SPREAD',
|
||||||
bread: 'THE BREAD',
|
bread: 'THE BREAD',
|
||||||
topping: 'THE TOPPING',
|
topping: 'THE TOPPING',
|
||||||
|
grill: 'THE GRILL',
|
||||||
bench: 'THE BENCH',
|
bench: 'THE BENCH',
|
||||||
};
|
};
|
||||||
const groupRank: Record<string, number> = { prep: 0, bread: 1, toast: 2, topping: 3, spread: 4, bench: 5 };
|
const groupRank: Record<string, number> = { prep: 0, bread: 1, toast: 2, topping: 3, grill: 3, spread: 4, bench: 5 };
|
||||||
let lastGroup: string | undefined;
|
let lastGroup: string | undefined;
|
||||||
const rank = (g?: string) => (g && g in groupRank ? groupRank[g] : 9);
|
const rank = (g?: string) => (g && g in groupRank ? groupRank[g] : 9);
|
||||||
const ordered = grouped
|
const ordered = grouped
|
||||||
|
|||||||
@ -22,12 +22,14 @@ const N = 48;
|
|||||||
|
|
||||||
/** Where the coals top out when freshly banked. */
|
/** Where the coals top out when freshly banked. */
|
||||||
export const BED_MAX = 1.8;
|
export const BED_MAX = 1.8;
|
||||||
/** Sear past here renders fat that can flare. */
|
|
||||||
export const RENDER_AT = 0.45;
|
|
||||||
/** A good sear lives in this window; past it is char, then carbon. */
|
/** A good sear lives in this window; past it is char, then carbon. */
|
||||||
export const SEAR_LO = 0.55;
|
export const SEAR_LO = 0.5;
|
||||||
export const SEAR_HI = 0.8;
|
export const SEAR_HI = 0.78;
|
||||||
export const CHAR_AT = 0.9;
|
export const CHAR_AT = 0.9;
|
||||||
|
/** Sear past here renders fat that can flare — set ABOVE the good window's floor
|
||||||
|
* so a clean sear is reachable with no flare (the floor: sear, then pull). Only
|
||||||
|
* pushing a piece toward the top of the window and past it invites the flames. */
|
||||||
|
export const RENDER_AT = 0.62;
|
||||||
|
|
||||||
/** Sear climbs this fast per unit of coal heat beneath the piece. Tuned so a good
|
/** Sear climbs this fast per unit of coal heat beneath the piece. Tuned so a good
|
||||||
* sear over a fresh (~1.7) bed takes ~18s and chars by ~28s — the oven's timescale. */
|
* sear over a fresh (~1.7) bed takes ~18s and chars by ~28s — the oven's timescale. */
|
||||||
@ -37,7 +39,7 @@ const DECAY = 0.012;
|
|||||||
/** Coals hotter than this can ignite dripping fat. */
|
/** Coals hotter than this can ignite dripping fat. */
|
||||||
const FLARE_HEAT = 0.8;
|
const FLARE_HEAT = 0.8;
|
||||||
/** How fast a live flare scorches the piece above it (past honest searing). */
|
/** How fast a live flare scorches the piece above it (past honest searing). */
|
||||||
const FLARE_CHAR_RATE = 0.6;
|
const FLARE_CHAR_RATE = 0.45;
|
||||||
|
|
||||||
export interface GrillFood {
|
export interface GrillFood {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user