f464e6f2e25974beb755f817ede8da9650471fd9
2
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
db208a1a86 |
Make big numbers readable, and make the export add up
Seeded a table that looks like a real game — "Mike from work", "Jean-Philippe", stacks from $12 to $3,200,000 — and measured what the layout does with it. ".00" was costing a third of a seat puck Every amount rendered with two forced decimal places. Measured, the trailing ".00" is ~22px, against the 64px a seat puck has to show a stack in. So "$1,250,000.00" needed 102px of a 64px box — and because a number has no spaces to break on, it neither wrapped nor ellipsised. It drew straight out over the seat next to it. Money now shows cents only when there are cents. A chip game deals in whole numbers nearly all the time, and "$12" says exactly what "$12.00" said in half the width. A quarter-blind game is untouched: $0.25, $12.50 and $0.75 pots all still read as before, checked against a 25c/50c table. That alone took "$1,250,000.00" down to "$1,250,000" — 102px to 80px, still wider than the 64px puck. The remaining overflow is handled by stepping the font down on long amounts, which keeps every digit. Abbreviating to "$1.25M" would have fit more comfortably and was the obvious move, but a dealer reading a rounded stack off the felt is a worse failure than a small one, and the exact figure is the entire point of the tool. Measured after: every stack now paints inside its own puck, nothing spills onto a neighbour. Because Money.format is the single formatter all 47 call sites go through, this lands on the seat pucks, action bar, pot breakdown, seat menu, standings and history at once — the payoff from consolidating it earlier. The CSV export couldn't be added up The file exists so a house manager or a disputed chip count has something that outlives the tab, and every money column in it was written as text: "$1,250,000.00". The commas were quoted correctly so the file always parsed — it just imported as strings, so the one thing anyone opens it to do, total a column, needed every cell cleaned first. Money columns now go in as bare numbers (1250000.00) with no symbol and no separators. The columns meant for humans stay human: names are still names, payouts still read "Alexandra: $1,250,000", and a name with a comma in it is still quoted. 12 new tests (88 total, all passing), including the case that would be ruined by dropping cents unconditionally, and a proper quote-aware CSV parser to check every money cell parses as a number. Re-ran the occlusion sweep from the previous round at 9 seats, 10 seats and tournament stacks: still clean, no regression from the font changes. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |
||
|
|
c1a484b97a |
Pay down the Hold'em dealer's tech debt and give it a test suite
Every module in the dealer ends with a `module.exports` guard, so they were all written to be testable, and none of them were ever tested. The correctness fixes that went in recently were checked by hand in a browser console that has since been closed. This puts a real safety net under them and clears out the duplication that made those bugs possible in the first place. Tests (58, ~300ms, no dependencies) Node's built-in runner, no package.json and no install step: the modules are plain script-tag files, so `load.js` just requires them and puts them on globalThis under the names they use to find each other in the page. The randomised suite is the part that matters. Every bug found in this tool so far has been shape-dependent — blinds posted to empty chairs, the button skipping players, heads-up acting out of order — and none of them show up on the full six-handed table anyone would write a fixed example for. It deals 1000 hands per property across random seat counts, empty chairs, stack depths and dealer input, checking that chips are never created or destroyed, that the pot breakdown plus uncalled returns equals the money in, that paying out settles to the cent, and that the button and blinds always land on real players. The generator is seeded so a failure reproduces instead of flaking. One implementation each - `Math.round(x * 100) / 100` was written out 16 times across 6 files. It's `Money.round` / `toCents` / `fromCents` now, in the module both pages already load first. - The chip epsilon was declared twice with the same value under two names (`PotCalculator.EPSILON`, `Betting.EPSILON_CHIPS`). Both now point at `Money.EPSILON`. - `Betting.seatAt` and `HoldemState.nextSeatIndex` were the same modular arithmetic written twice. Walking the seats is exactly where the button, the blinds and the action drifted apart from each other before, so there is one of it now. - The undo snapshot/restore dance appeared three times in main.js, each one re-deleting every key on `state` in place. It's `snapshotState` / `restoreState` once, with the reason it can't just reassign written down. A bug the tests found `layers()` and `uncalledReturns()` both decide what money was never contested, separately, and disagreed. `uncalledReturns` measured each player against the biggest live contribution *including their own*, which makes the biggest bet its own yardstick — so the one case it exists for came back empty, and `layers()` announced the overbet as a side pot only the bettor could win. With Ann shoving 200 into Ben's 60, the dealer was told "main pot 4, side pot 3, side pot 116, side pot 140" — four pots, one of them fictional. It now reads "main pot 123, uncalled 140 back to Ann", which is what a dealer says out loud. Layers are derived from contributions with the uncalled money already removed, so the two can't disagree again. A band every payer folded out of is now merged into the pot below rather than dropped, which was quietly shrinking the announced pot in another edge case. Validated with Playwright against a real hand on both surfaces — the table's live pot readout and Quick Split's all-in mode — plus undo, with no console errors. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb |