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

forked from trevato/cs

The board can be read one map at a time: the map a result carries becomes the comparison it was recorded for #419

Merged opened this

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

Merged

The all-time leaderboard can now be read one map at a time. `/?board=de_forge` or `/?board=gen_…` narrows the table to results played on that map. With no `?board=`, it shows every map added together, as before. Anything that isn't a map falls back to every map, with a sentence saying so. The feed and today's board are unchanged, and each feed row's map name was already a link that plays it (`/?ma…

shipped production is serving it deploy ae07c2d

opened this

The all-time leaderboard can now be read one map at a time. /?board=de_forge or /?board=gen_… narrows the table to results played on that map. With no ?board=, it shows every map added together, as before. Anything that isn't a map falls back to every map, with a sentence saying so. The feed and today's board are unchanged, and each feed row's map name was already a link that plays it (/?map=…). bun run test passes: typecheck plus 1735 tests. The app boots and / answers in a few milliseconds with and without ?board=.

Why the earlier pull (#411) stayed red: it was based on a main from before #412 and #413, so it conflicted with them and failed the typecheck. This push merges main into it, resolves the conflicts in src/db.test.ts and src/pages/game.ts, and fixes the type error in the 20,000-row timing test by typing its map variable. That was a mistake in the test, not the code.

What to look at:

  • Query: standing has a map column. The empty string holds each player's row across every map, and there is one row per map they played. standings(db, limit, map) takes the map as a parameter, and the index standing_by_rank is now (map, kills DESC, damage DESC, user_id, name).

- Query plan: SEARCH standing USING INDEX standing_by_rank (map=?), with no sort step. - Time at 20,000 matches on 200 maps: about 0.04 ms for either board, and 0.9 ms for the list of maps. - A database written before this change rebuilds standing once from match the first time it opens.

  • Input check: ?board= is checked in src/server.ts by playedOn from src/mapname.ts, the same rules a stored result must meet. Only a name it accepts is put into a link.
  • Wording on the page: the heading says which map is shown, and a sentence under it says the board is filtered. It also says that anyone missing hasn't played that map, and links back to every map.
  • Map links: above the table are every map, de_forge, and up to 6 generated maps that at least 2 players have results on, most players first. The page explains how that list is chosen, and a map that isn't listed is still reachable by typing its ?board= link.
  • Retention (#412): pruning old matches recomputes the affected players' rows for every map. A new test proves it: once the only match on a generated map is pruned, that map's board is empty.
  • Tests:

- Two maps give two different filtered boards and one combined one. - de_forge's board excludes rows from generated maps. - A junk ?board= falls back to every map. - The link on a feed row plays that map. - The map list is bounded. - Pruning keeps each map's board correct.

Not done:

  • No map filter in the terminal; #84 is deciding its shape.
  • Nothing from #234 or #89: this adds only the ?board= parameter, which either of them can build on.

To try it: in the preview, play a match on a generated map, then open /?board=<that map>, /?board=de_forge and /?board=junk.

🤖 Generated with Claude Code

Closes #242

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs