Z terminálového počitadla se stává doprovod ke stolu: telefon zapisuje skóre, notebook ukazuje zápisník a hry se pamatují mezi večery. Hráči se napříč hrami poznávají podle jména, arkádově — Adam ze středečního večera je ten samý Adam jako z minulého měsíce, bez účtů a hesel.
Terminálová verze zůstává funkční a navenek beze změny.
Rozhodnutí, ze kterých to vyrostlo
Web, ne nativní aplikace — hosti u stolu si nic neinstalují, otevřou adresu.
Zapisuje jeden člověk z telefonu, ne každý hráč vlastní zařízení.
Jen LAN, žádné přihlašování.
Trvalá historie od začátku, včetně jednotlivých tahů — statistiku z dat, která se nesbírala, zpětně nedopočítáš.
Jednodušší = lepší: žádný build step, žádný ORM, žádné async, žádný JS framework. Závislosti jsou dvě, Flask a qrcode.
Co je uvnitř
Soubor
Role
core.py
pravidla hry, žádné print, input ani DB
storage.py
SQLite: hráči, hry, tahy
net.py
hledání adres, na kterých je počitadlo dostupné
stats.py
síň slávy a kariérní statistiky
web.py
Flask, routy, spouštěč
dice.py
totéž v terminálu, bez ukládání
Pravidla se vytáhla z CLI
add_score() se dřív uprostřed pravidla zastavovalo na input(), takže logika nešla testovat ani použít z webu. Teď core.Game vrací, co se stalo, a volající se rozhodne, jak to ukáže.
Tři věci, na kterých to stojí:
Součty jsou vždy odvozené z tahů. Díky tomu je undo() jen zahození posledního zápisu — funguje to i pro vracení vynulování.
Vynulování se ukládá jako hodnota 0 s příznakem, ne jako záporný tah. Původní trik se zápisem -sum(...) byl v CLI elegantní, ale v databázi by rozbil statistiky: „průměr na tah" by počítal s číslem, které nikdo nehodil.
zero_streak() je jediný zdroj pravdy pro pravidlo i pro indikátor x/xx, a počítá se od posledního vynulování.
Zápisník
Sloupce hráči, řádky kola — mřížka, kterou lidi u kostek stejně malují na papír. Tahy zapsané před vynulováním se škrtnou červeně, sloupec hráče na tahu drží zvýrazňovač.
Tabule se doptává serveru každé dvě vteřiny a vyměňuje jen hlavičku a zápisník. Nejnovější kolo je nahoře a součty nad historií — po sto kolech byla původní chronologická verze k ničemu, na obrazovku se vešla kola 1–14 a řádek se součty skončil asi 4 500 pixelů pod okrajem. Zobrazuje se posledních 15 kol (BOARD_ROUNDS), zbytek shrne řádka „… a dalších 85 kol".
Adresy
Notebook se během večera stěhuje mezi domácí WiFi a hotspotem z telefonu, takže se adresy hledají za běhu a nabízí se jich víc. Řadí se podle rozsahu adresy a obecného typu rozhraní, ne podle seznamu konkrétních jmen: výchozí trasa, místní sítě, tailnet, virtuální sítě. Loopback a link-local vypadnou, nic dalšího se nezahazuje — o tom, co funguje, rozhodne člověk u stolu. Když se notebook přesune jinam, tabule to pozná a načte se znovu. Když se nenajde nic, řekne, co s tím, místo QR kódu naslepo.
Statistiky
Síň slávy sbírá rekordy (nejvyšší tah, nejvyšší konečné skóre, nejrychlejší výhra), kariéra ukazuje, kdo je dlouhodobě dobrý. Počítají se jen dohrané hry. Celkový součet bodů napříč hrami tam vědomě není — cílové skóre je u každé hry jiné, hry nejsou srovnatelné a metrika by odměňovala jen toho, kdo hrál nejvíc.
Vzhled
Stojí na tom, co ta appka nahrazuje: čtverečkovaný papír, modrá propiska zapisuje, červená škrtá, zvýrazňovač drží tah. Písma jsou systémová, nic se nestahuje — na LAN nemusí být internet.
Ověření
80 testů (pytest tests/): pravidla hry, úložiště, klasifikace a řazení adres nezávisle na aktuální síti, a funkční průchody webem přes Flask test client.
Ručně odzkoušeno v prohlížeči, mimo jiné že se tabule sama přepnula na dalšího hráče poté, co skóre zapsal někdo jiný, a že se z telefonu na hotspotu dá připojit. Terminálová verze projela stejný skriptovaný průchod jako před refaktorem a chová se identicky.
Z terminálového počitadla se stává doprovod ke stolu: **telefon zapisuje skóre, notebook ukazuje zápisník** a hry se pamatují mezi večery. Hráči se napříč hrami poznávají podle jména, arkádově — Adam ze středečního večera je ten samý Adam jako z minulého měsíce, bez účtů a hesel.
Terminálová verze zůstává funkční a navenek beze změny.
## Rozhodnutí, ze kterých to vyrostlo
- **Web, ne nativní aplikace** — hosti u stolu si nic neinstalují, otevřou adresu.
- **Zapisuje jeden člověk** z telefonu, ne každý hráč vlastní zařízení.
- **Jen LAN**, žádné přihlašování.
- **Trvalá historie od začátku**, včetně jednotlivých tahů — statistiku z dat, která se nesbírala, zpětně nedopočítáš.
- **Jednodušší = lepší**: žádný build step, žádný ORM, žádné async, žádný JS framework. Závislosti jsou dvě, Flask a qrcode.
## Co je uvnitř
| Soubor | Role |
|---|---|
| `core.py` | pravidla hry, žádné `print`, `input` ani DB |
| `storage.py` | SQLite: hráči, hry, tahy |
| `net.py` | hledání adres, na kterých je počitadlo dostupné |
| `stats.py` | síň slávy a kariérní statistiky |
| `web.py` | Flask, routy, spouštěč |
| `dice.py` | totéž v terminálu, bez ukládání |
### Pravidla se vytáhla z CLI
`add_score()` se dřív uprostřed pravidla zastavovalo na `input()`, takže logika nešla testovat ani použít z webu. Teď `core.Game` vrací, co se stalo, a volající se rozhodne, jak to ukáže.
Tři věci, na kterých to stojí:
- **Součty jsou vždy odvozené z tahů.** Díky tomu je `undo()` jen zahození posledního zápisu — funguje to i pro vracení vynulování.
- **Vynulování se ukládá jako hodnota 0 s příznakem**, ne jako záporný tah. Původní trik se zápisem `-sum(...)` byl v CLI elegantní, ale v databázi by rozbil statistiky: „průměr na tah" by počítal s číslem, které nikdo nehodil.
- **`zero_streak()` je jediný zdroj pravdy** pro pravidlo i pro indikátor `x`/`xx`, a počítá se od posledního vynulování.
### Zápisník
Sloupce hráči, řádky kola — mřížka, kterou lidi u kostek stejně malují na papír. Tahy zapsané před vynulováním se škrtnou červeně, sloupec hráče na tahu drží zvýrazňovač.
Tabule se doptává serveru každé dvě vteřiny a vyměňuje jen hlavičku a zápisník. **Nejnovější kolo je nahoře a součty nad historií** — po sto kolech byla původní chronologická verze k ničemu, na obrazovku se vešla kola 1–14 a řádek se součty skončil asi 4 500 pixelů pod okrajem. Zobrazuje se posledních 15 kol (`BOARD_ROUNDS`), zbytek shrne řádka „… a dalších 85 kol".
### Adresy
Notebook se během večera stěhuje mezi domácí WiFi a hotspotem z telefonu, takže se adresy hledají za běhu a nabízí se jich víc. Řadí se podle **rozsahu adresy a obecného typu rozhraní**, ne podle seznamu konkrétních jmen: výchozí trasa, místní sítě, tailnet, virtuální sítě. Loopback a link-local vypadnou, nic dalšího se nezahazuje — o tom, co funguje, rozhodne člověk u stolu. Když se notebook přesune jinam, tabule to pozná a načte se znovu. Když se nenajde nic, řekne, co s tím, místo QR kódu naslepo.
### Statistiky
Síň slávy sbírá rekordy (nejvyšší tah, nejvyšší konečné skóre, nejrychlejší výhra), kariéra ukazuje, kdo je dlouhodobě dobrý. Počítají se jen dohrané hry. Celkový součet bodů napříč hrami tam **vědomě není** — cílové skóre je u každé hry jiné, hry nejsou srovnatelné a metrika by odměňovala jen toho, kdo hrál nejvíc.
## Vzhled
Stojí na tom, co ta appka nahrazuje: čtverečkovaný papír, modrá propiska zapisuje, červená škrtá, zvýrazňovač drží tah. Písma jsou systémová, nic se nestahuje — na LAN nemusí být internet.
## Ověření
80 testů (`pytest tests/`): pravidla hry, úložiště, klasifikace a řazení adres nezávisle na aktuální síti, a funkční průchody webem přes Flask test client.
Ručně odzkoušeno v prohlížeči, mimo jiné že se tabule sama přepnula na dalšího hráče poté, co skóre zapsal někdo jiný, a že se z telefonu na hotspotu dá připojit. Terminálová verze projela stejný skriptovaný průchod jako před refaktorem a chová se identicky.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Pravidla byla propletená se vstupem a výstupem (add_score se uprostřed
zastavovalo na input()), takže nešla testovat ani použít z webu.
- core.Game drží stav a pravidla, nic nevypisuje a na nic se neptá
- součty jsou vždy odvozené z tahů, díky tomu je undo() jen zahození
posledního tahu — funguje i pro vracení vynulování
- vynulování se ukládá jako hodnota 0 s příznakem reset místo záporného
tahu, aby do historie nepadala čísla, která nikdo nehodil
- zero_streak() je jediný zdroj pravdy pro pravidlo i pro indikátor x/xx
a počítá se od posledního vynulování
- pořadí na tahu plyne z počtu zápisů, globální pointer zmizel
- dice.py zůstává navenek stejné, jen deleguje na Game
- 15 testů nad pravidly
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ručně psané SQL nad pěti tabulkami, žádné ORM.
- hry ukazují na players.id, nikdy na jméno, takže se přejmenování
propíše i do starých her
- párování hráčů napříč hrami přes normalizovaný klíč (trim, casefold),
plus slučování pro případ překlepu
- zapisuje se každý tah hned, ne až na konci — rozehraná hra se po pádu
serveru najde podle stavu a dá se v ní pokračovat
- součty se neukládají nikde, stav hry se rekonstruuje načtením tahů
- časy jako ISO text, sqlite3 vlastní adaptéry pro datetime zavrhl
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Flask, server-rendered HTML, formuláře přes POST/redirect/GET.
Žádný build step, žádný JS framework.
- / zakládá hru výběrem známých hráčů v pořadí u stolu
- /game/<id> je zadávací pohled: kdo je na tahu, skóre, x/xx,
průběžné pořadí, vrácení posledního zápisu
- /board/<id> je zápisník po kolech; tahy před vynulováním se škrtnou
červeně, sloupec hráče na tahu drží zvýrazňovač
- tabule se doptává serveru každé dvě vteřiny a vyměňuje jen hlavičku
a zápisník, QR s adresou zůstává mimo obnovovanou část
- adresa pro telefon se hledá za běhu, QR se skládá ze čtverečků
přímo do SVG, takže není potřeba Pillow
- 16 funkčních testů přes Flask test client
Vizuálně to stojí na tom, co appka nahrazuje: čtverečkovaný papír,
modrá propiska zapisuje, červená škrtá, zvýrazňovač drží tah.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- rekordy: nejvyšší tah, nejvyšší konečné skóre, nejrychlejší výhra
- kariéra: počet her, výher, úspěšnost, průměrné pořadí, průměr na tah
a kolikrát koho postihlo vynulování
- do statistik jdou jen dohrané hry, nedohrané a opuštěné se ignorují
- celkové skóre počítá SQL stejným pravidlem jako core.Game.totals(),
tedy součet tahů po posledním vynulování; testem ověřeno, že se obě
cesty shodují
- vědomě chybí celkový součet bodů napříč hrami — cílové skóre je u každé
hry jiné, metrika by odměňovala jen toho, kdo hrál nejvíc
- README se spuštěním a přehledem modulů
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Po sto kolech byla tabule k ničemu: na obrazovku se vešla kola 1–14
a řádek se součty skončil asi 4 500 pixelů pod okrajem. Tabule tedy
ukazovala nejstarší historii a nic z toho, co se právě děje.
- kola se řadí od nejnovějšího, poslední zápis je vždy nahoře
- součty se přesunuly z patičky nad historii, hned pod jména hráčů
- tabule ukazuje posledních 15 kol, zbytek shrne řádka "... a dalších
85 kol"; má ukazovat stav, ne archiv, a nepřekresluje se pak zbytečně
každé dvě vteřiny přes tři sta buněk
- skloňování kol podle češtiny (1 kolo / 2 kola / 5 kol)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Detekce vracela jedinou adresu podle výchozí trasy. Když ta nevede tam,
kam je připojený telefon, QR kód míří do prázdna a nezbývá než sahat do
konfigurace. Při ladění se ukázalo i to, že záložní větev může vrátit
127.0.0.1, tedy QR kód na localhost.
- net.py hledá adresy všech rozhraní přes if_nameindex a ioctl, se zálohou
přes getaddrinfo; DICE_HOST má poslední slovo
- řadí se podle rozsahu adresy a obecného typu rozhraní, ne podle seznamu
konkrétních jmen: výchozí trasa, místní sítě, tailnet, virtuální sítě
- loopback a link-local vypadnou, nic dalšího se nezahazuje — o tom, co
funguje, rozhoduje člověk u stolu
- tailnet adresu vědomě necháváme: funguje i napříč sítěmi
- adresy se hledají při každém načtení; když se notebook přesune na jinou
síť, tabule to pozná podle data-address a načte se znovu
- když se nenajde nic, tabule řekne, co s tím, místo QR kódu naslepo
- 20 testů nad klasifikací a řazením, nezávislých na aktuální síti
- README doplněn o řešení potíží s připojením
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Trojice venv/pip/python byla zbytečná ceremonie pro appku, která se pouští
jednou za herní večer.
- web.py i dice.py si nesou závislosti v hlavičce podle PEP 723, takže
je uv obstará sám a není co instalovat ani aktivovat
- pytest.ini staví kořen repa na pythonpath, takže testy jdou spustit
i mimo připravené prostředí
- README nabízí uv jako hlavní cestu, venv a systémové balíčky jako
záložní
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Z terminálového počitadla se stává doprovod ke stolu: telefon zapisuje skóre, notebook ukazuje zápisník a hry se pamatují mezi večery. Hráči se napříč hrami poznávají podle jména, arkádově — Adam ze středečního večera je ten samý Adam jako z minulého měsíce, bez účtů a hesel.
Terminálová verze zůstává funkční a navenek beze změny.
Rozhodnutí, ze kterých to vyrostlo
Co je uvnitř
core.pyprint,inputani DBstorage.pynet.pystats.pyweb.pydice.pyPravidla se vytáhla z CLI
add_score()se dřív uprostřed pravidla zastavovalo nainput(), takže logika nešla testovat ani použít z webu. Teďcore.Gamevrací, co se stalo, a volající se rozhodne, jak to ukáže.Tři věci, na kterých to stojí:
undo()jen zahození posledního zápisu — funguje to i pro vracení vynulování.-sum(...)byl v CLI elegantní, ale v databázi by rozbil statistiky: „průměr na tah" by počítal s číslem, které nikdo nehodil.zero_streak()je jediný zdroj pravdy pro pravidlo i pro indikátorx/xx, a počítá se od posledního vynulování.Zápisník
Sloupce hráči, řádky kola — mřížka, kterou lidi u kostek stejně malují na papír. Tahy zapsané před vynulováním se škrtnou červeně, sloupec hráče na tahu drží zvýrazňovač.
Tabule se doptává serveru každé dvě vteřiny a vyměňuje jen hlavičku a zápisník. Nejnovější kolo je nahoře a součty nad historií — po sto kolech byla původní chronologická verze k ničemu, na obrazovku se vešla kola 1–14 a řádek se součty skončil asi 4 500 pixelů pod okrajem. Zobrazuje se posledních 15 kol (
BOARD_ROUNDS), zbytek shrne řádka „… a dalších 85 kol".Adresy
Notebook se během večera stěhuje mezi domácí WiFi a hotspotem z telefonu, takže se adresy hledají za běhu a nabízí se jich víc. Řadí se podle rozsahu adresy a obecného typu rozhraní, ne podle seznamu konkrétních jmen: výchozí trasa, místní sítě, tailnet, virtuální sítě. Loopback a link-local vypadnou, nic dalšího se nezahazuje — o tom, co funguje, rozhodne člověk u stolu. Když se notebook přesune jinam, tabule to pozná a načte se znovu. Když se nenajde nic, řekne, co s tím, místo QR kódu naslepo.
Statistiky
Síň slávy sbírá rekordy (nejvyšší tah, nejvyšší konečné skóre, nejrychlejší výhra), kariéra ukazuje, kdo je dlouhodobě dobrý. Počítají se jen dohrané hry. Celkový součet bodů napříč hrami tam vědomě není — cílové skóre je u každé hry jiné, hry nejsou srovnatelné a metrika by odměňovala jen toho, kdo hrál nejvíc.
Vzhled
Stojí na tom, co ta appka nahrazuje: čtverečkovaný papír, modrá propiska zapisuje, červená škrtá, zvýrazňovač drží tah. Písma jsou systémová, nic se nestahuje — na LAN nemusí být internet.
Ověření
80 testů (
pytest tests/): pravidla hry, úložiště, klasifikace a řazení adres nezávisle na aktuální síti, a funkční průchody webem přes Flask test client.Ručně odzkoušeno v prohlížeči, mimo jiné že se tabule sama přepnula na dalšího hráče poté, co skóre zapsal někdo jiný, a že se z telefonu na hotspotu dá připojit. Terminálová verze projela stejný skriptovaný průchod jako před refaktorem a chová se identicky.
🤖 Generated with Claude Code