Public: anyone can read this; the crew can change it.

forked from trevato/cs

A match of the day: everyone plays the same map, the same bots and the same seed, and the board means something #261

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

Everyone who plays today plays the same match, and the rule is one sentence: take the calendar day in UTC and write it without its dashes — 2026-09-21 is the match `20260921`. That is a match id of the shape `src/matchid.ts` already accepts, so it carves the map and seeds the bots, the spawns, the bomb carrier and the spray with no new machinery, and every visitor derives it from their own clock —…

shipped production is serving it deploy ae07c2d

opened this

Everyone who plays today plays the same match, and the rule is one sentence: take the calendar day in UTC and write it without its dashes — 2026-09-21 is the match 20260921. That is a match id of the shape src/matchid.ts already accepts, so it carves the map and seeds the bots, the spawns, the bomb carrier and the spray with no new machinery, and every visitor derives it from their own clock — the server never tells anyone what it is. src/daily.ts holds the rule and the sentence the page prints; it imports only matchid.ts and mapname.ts, so the server, the database, the terminal and the browser bundle all share one copy of it.

In the menu, Pick today's match fills the controls you can already see — the Match id field, the Map control on generated, and first to 5 — rather than adding a mode, so the map's name is under the controls before Play and everything downstream (the preview, the share link, start) is untouched. The length is fixed because the board ranks by rounds won, and rounds won compares nothing across a first-to-5 and a first-to-13; that is a decision the issue did not ask for, and it is stated on the page. Today's board sits above the all-time table without replacing it: one row a player, their best run, ranked by rounds won, then kills, then damage, with a runs column saying how many there were — best-of rather than every-run, because a list of every run is a list whoever replays most fills, which is the problem the board exists to fix. Under it, in one sentence, what it is not: a score against bots on a fixed seed, reported by the browser that played it, not a ladder.

Which day a result counts for is never the browser's word. saveMatch derives it from the id with dayFiled against the server's clock: a result is filed under the day its id names, and never under a day that has not begun here — so a match that crosses midnight stays on the day it was played, and deriving next week's id early buys nothing. The day column is additive and defaulted, so an existing database opens and keeps every row; dayBoard is one indexed query. seedFrom moved from src/client/generate.ts to src/mapname.ts (beside the other half of its round trip) and is re-exported, so the server can name today's map without importing a map generator.

Worth a look in the preview: the leaderboard block on / before and after playing today's match twice, the menu's button and the sentence beside it (it is rewritten from the browser's own day, so a tab left open across midnight picks up the new match), and the terminal's new fourth tab, which prints the id to paste and the same board. Not done: nothing about #238 — the plan a deployment with a TypeSafe key asks for is still pinned to the moment rather than to the match, so on such a deployment "the same bots" means the same policy and the same local plan but not necessarily the same model answer; that is #238's to keep, and this pull leans on it exactly as the issue says. bun test passes (1,143 tests).

🤖 Generated with Claude Code

Budget reached at $8.52; this is where it stopped.

Closes #174

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs