diff --git a/src/engine/engine.ts b/src/engine/engine.ts index 021819b..f008204 100644 --- a/src/engine/engine.ts +++ b/src/engine/engine.ts @@ -1014,7 +1014,8 @@ export interface EngineStats { * (and, within the current level, can fluctuate) until the exit is reached, * at which point it becomes the baseline the next level carries forward. */ score: number; - /** Enemies defeated this level ("bugs squashed" for the commit summary). */ + /** Enemies defeated this level. Surfaced to the player as the commit + * summary's and both run-end screens' "Kills" row, via `statRows`. */ kills: number; /** True once any cheat has fired this run — drives the HUD's persistent * "run not recorded" badge. See `PlayerState.cheatsUsed`. */ diff --git a/src/main.ts b/src/main.ts index 810e849..8a82082 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3674,7 +3674,6 @@ function launchLevel(path: string, parsed: ParsedFile, source: string | null, ca hud.showCommitSummary( { linesRefactored: parsed.linesOfCode, - bugsSquashed: stats.kills, stats: statsScreenInfo(stats.levelScoreBreakdown, stats.levelPlayerStats), }, () => void advanceToNextLevel(stats), diff --git a/src/ui/gameHud.test.ts b/src/ui/gameHud.test.ts index 2d12aa1..cc47cf1 100644 --- a/src/ui/gameHud.test.ts +++ b/src/ui/gameHud.test.ts @@ -166,24 +166,33 @@ describe("GameHud — overlay content per method", () => { }); it("showCommitSummary draws its stats and has no body lines", () => { - hud.showCommitSummary({ linesRefactored: 120, bugsSquashed: 3 }, vi.fn()); + hud.showCommitSummary({ linesRefactored: 120 }, vi.fn()); const texts = fillTextCalls(); expect(texts).toContain("COMMIT SUMMARY"); expect(texts).toContain("Lines refactored"); expect(texts).toContain("120"); - expect(texts).toContain("Bugs squashed"); - expect(texts).toContain("3"); expect(texts).toContain("Continue"); }); it("showCommitSummary also draws the curated stat rows when given stats", () => { - hud.showCommitSummary({ linesRefactored: 120, bugsSquashed: 3, stats: fakeStatsScreenInfo() }, vi.fn()); + hud.showCommitSummary({ linesRefactored: 120, stats: fakeStatsScreenInfo() }, vi.fn()); const texts = fillTextCalls(); expect(texts).toContain("Weapon accuracy"); expect(texts).toContain("Time survived"); expect(texts).toContain("Damage taken"); }); + it("shows the kill count exactly once, as \"Kills\"", () => { + // Regression: "Bugs squashed" used to sit directly above the stats block + // and render the same number as its "Kills" row, one line apart. The two + // rows were built in different files, which is why it survived review and + // was only caught by playing a level. + hud.showCommitSummary({ linesRefactored: 120, stats: fakeStatsScreenInfo() }, vi.fn()); + const texts = fillTextCalls(); + expect(texts).not.toContain("Bugs squashed"); + expect(texts.filter((t) => t === "Kills")).toHaveLength(1); + }); + it("showMultiplayerResults draws the given title/color and one row per player", () => { hud.showMultiplayerResults( "MULTIPLAYER: CAMPAIGN COMPLETE", @@ -652,7 +661,7 @@ describe("GameHud — no overlay squeezes its own text", () => { // "Path … · Map … · Lore … · Secrets … · Streaks …" needs 446px against // the 286px each side gets in a 620-wide box — the one case that was // squeezed even *with* `wide` set, so no fixed width could have fixed it. - hud.showCommitSummary({ linesRefactored: 1234, bugsSquashed: 567, stats: fakeStatsScreenInfo() }, vi.fn()); + hud.showCommitSummary({ linesRefactored: 1234, stats: fakeStatsScreenInfo() }, vi.fn()); expectNothingSqueezed(); }); @@ -692,7 +701,7 @@ describe("GameHud — no overlay squeezes its own text", () => { const screens: [string, () => void][] = [ ["kernel panic", () => hud.showKernelPanic(stats, vi.fn(), { cheated: true })], ["kernel panic + rollback", () => hud.showKernelPanic(undefined, vi.fn(), { rollback: { remaining: 2, onRollback: vi.fn() } })], - ["commit summary", () => hud.showCommitSummary({ linesRefactored: 1234, bugsSquashed: 567, stats }, vi.fn())], + ["commit summary", () => hud.showCommitSummary({ linesRefactored: 1234, stats }, vi.fn())], ["build successful", () => hud.showBuildSuccessful(stats, vi.fn())], ["level start", () => hud.showLevelStart({ campaign: "stage06_pipeline.py", levelName: "stage06_pipeline.py", roomCount: 12, enemyCount: 34, secretRoomCount: 2 }, vi.fn())], // The worst string in the repo by a distance: `main.ts`'s balance-mismatch diff --git a/src/ui/gameHud.ts b/src/ui/gameHud.ts index d12cc68..547c415 100644 --- a/src/ui/gameHud.ts +++ b/src/ui/gameHud.ts @@ -38,9 +38,29 @@ export interface StatsScreenInfo { /** Stats shown on the post-level commit summary. */ export interface CommitSummaryInfo { linesRefactored: number; - bugsSquashed: number; - /** This level's own curated stats/breakdown — omitted shows just the two - * fields above, same as before this existed. */ + /** This level's own curated stats/breakdown — omitted shows just the line + * count above, same as before this existed. + * + * The kill count lives in here (`statRows`' "Kills"), not as a field of its + * own. It used to be both: a "Bugs squashed" row sat directly above the + * stats block and rendered the identical number, because the two rows are + * built in different files and nobody saw them together until someone + * played a level. + * + * **The duplication is why it was noticed; the name is why "Kills" won.** + * This game reads somebody's real source code, so "Bugs squashed: 47" is a + * claim that their file contained 47 defects. It did not — it contained + * enemies derived from cyclomatic complexity, and complexity is not defects. + * Never let a label assert something about the player's codebase that the + * generator has no basis for; "Kills" describes what actually happened and + * claims nothing. + * + * Secondary, but it points the same way: the game has a *literal* `Bug`. + * `placeTodoEncounter` (`map/generation/lore.ts`) spawns one beside a + * TODO/FIXME terminal, and it is rare — measured on the demo campaign the + * tech-debt mix is trap 4 / mine 3 / Bug 1 across all 17 levels. So the row + * named the one entity here that really is a bug, while counting everything + * except it. */ stats?: StatsScreenInfo; } @@ -178,7 +198,6 @@ export class GameHud { lines: [], stats: [ ["Lines refactored", String(info.linesRefactored)], - ["Bugs squashed", String(info.bugsSquashed)], ...(info.stats ? statRows(info.stats) : []), ], buttonLabel: "Continue",