Eight strings, one function

A five-level nested ternary picked the caption for how a PvP game ended. The only way to exercise all eight of its outcomes was rendering the whole page.

The same review’s fourth find, right after the reveal timer, was a PvP page deciding what to say about how a game ended: crashed into a wall, crashed into yourself, ran into the opponent, ran out of lives, or timed out and let a fallback word play instead, each phrased differently depending on which of the two players it happened to. Five levels of nested ternary picked one of eight resulting strings, sitting inline in the page’s own script block where the only way to actually exercise all eight was rendering the whole component and reading what came out.

The fix is a single function that takes the ending’s raw facts and the strings it might need, and returns one caption or nothing at all:

export interface PvpEndReasonStrings {
  pvpEndFallbackYou: string;
  pvpEndFallbackThem: (name: string) => string;
  pvpEndYouHitWall: string;
  pvpEndYouHitSelf: string;
  pvpEndYouHitThem: (name: string) => string;
  pvpEndTheyHitWall: (name: string) => string;
  pvpEndTheyHitSelf: (name: string) => string;
  pvpEndTheyHitYou: (name: string) => string;
  pvpEndOutOfLivesYou: string;
  pvpEndOutOfLivesThem: (name: string) => string;
}

Ten fields for what had been an eight-way branch, since a couple of outcomes carry the opponent’s name and a couple don’t; the page now makes one call and renders whatever comes back, nothing for a forfeit, since that ending’s own title already explains itself.

A second, smaller duplication rode along in the same file, unrelated to the ternary but caught by the same pass over the page: two other spots, a derived value and an effect, each independently reassembled the identical five-field bag of facts a completely separate module needed for its own two predicates. Not duplicated logic there, the module itself was already a clean seam, just duplicated assembly of the same facts handed to it twice. Building that bag once and passing it to both call sites was a one-line fix once the review had already found it.

Ten new tests now cover every crasher, every reason, and the fallback case in combination, along with the forfeit and unknown-ending paths that used to just fall through to nothing without a test anywhere saying that was on purpose.