Docs: Spionen i PROTOCOL.md og README

Faser, tider, stikordenes og gættets regler, afstemning, to spioner, point
og priser, handlinger og fejlkoder, hvad der er hemmeligt hvor, og et
snapshot. README nævner spillet, mappen, bots og dev.html.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
agw
2026-10-20 21:59:57 +02:00
co-authored by Claude Opus 5.5
parent 78fa557da4
commit 3ed6f252ec
2 changed files with 70 additions and 2 deletions
+67
View File
@@ -314,3 +314,70 @@ Point: +100 pr. hjerte til den, der lavede bidraget, og +500 til ejeren af en k
"Bedste tegning" og "Bedste gæt" (flest hjerter, ved lighed det første i afsløringen), "Kæden der overlevede" og
"Største forvandling" (mindst lighed mellem første ord og sidste gæt). Temaerne står i `party/data/themes.json`
og speakerens replikker i `tegnoggaet/narrator/lines.json`.
## Spionen
Oprettes med `game=spion` på `/host/new` med `rounds` = `3` | `4` | `5` (standard 4) og `rules` = `1` | `0`.
Samme endpoints, samme `join`/`resume`/`ping`/`react`, og samme `joined`/`state`/`error`. Snapshot har `"game": "spion"`.
4-10 spillere; dem der joiner efter start, bliver publikum. Temaerne er fælles med Tegn og Gæt (`party/data/themes.json`);
Spionen bruger kun de konkrete navneord på mindst tre bogstaver (`party/themes.py`, `spy_themes()`).
En runde: alle undtagen spionen (to spioner fra otte spillere) ser et hemmeligt ord; spionen ser kun temaet. Ingen er
spion to runder i træk, hvis det kan undgås. Faser: `lobby → rules` (kun rummets første spil) `→ clue` (40 s) `→ clues`
(stikordene ét ad gangen på storskærmen, tilfældig rækkefølge) `→ talk` (60 s) `→ vote` (30 s) `→ verdict` (7 s)
`→ guess` (30 s, kun hvis den anklagede er spion) `→ result` (10 s), så næste runde eller `final`. `clue` og `vote`
slutter, når alle forbundne spillere er færdige; alle tidsfaser kan springes over af værten. Spillet hænger aldrig:
manglende stikord og stemmer løber ud, går den anklagede spion under gættet, slutter runden, og er der færre end fire
spillere tilbage, slutter spillet efter runden.
**Stikord:** ét ord, højst 20 tegn, ikke temaet. For dem, der kender ordet, heller ikke ordet selv, en bøjning af det
(en/et/er/erne/ene/e/s, også med fordoblet konsonant og uden slut-e), et ord, hvor det står inde i (`hundehus`), eller et
stikord, en anden har skrevet i runden. Spionens stikord holdes hverken op mod ordet eller de andres (en afvisning ville
røbe dem). Store/små bogstaver, mellemrum og bindestreger tæller ikke.
**Afstemning:** ikke på sig selv; flest stemmer bliver anklaget; lighed eller ingen stemmer = ingen anklages, og
spionerne slipper. Publikum kan stemme for sjov (`audience` i `votes`, tæller ikke). Med to spioner er den anklagede
fanget og får gættet; den anden slap. **Spionens gæt** er rigtigt, hvis det er ordet med de samme regler som stikord,
plus små stavefejl (ingen under fire bogstaver, én op til syv, to derover).
**Point:** +100 for at stemme på en spion; +300 til en spion, der slap; +200 til en fanget spion, der gætter ordet.
Priser: Bedste løgner (flest point som spion), Skarpeste øje (flest rigtige stemmer), Mest mistænkt (flest stemmer).
**Spiller (`/ws/play/`)**
| action | felter | hvornår |
| --- | --- | --- |
| `start` | | VIP'en i lobbyen |
| `submit_clue` | `text` (ét ord, 1-20 tegn) | fase `clue`; må skiftes, til fasen slutter |
| `vote` | `player_id` | fase `vote`; må skiftes; publikum også |
| `spy_guess` | `text` (1-30 tegn) | fase `guess`, kun den anklagede spion |
| `react`, `play_again` | | som i Tegn og Gæt |
Fejlkoder ud over Fup og Fakta: `clue_invalid`, `clue_is_word`, `clue_is_theme`, `clue_taken`, `bad_vote`,
`not_in_round`, `guess_invalid`.
**Hemmeligheden:** ordet står kun i `me.card` til dem, der kender det, aldrig til spionen, publikum eller storskærmen før
`result`. Hvem der er spion står ingen steder før dommen: ikke i spillerlisten, ikke i rækkefølger eller tider.
`votes.accused_is_spy` får storskærmen under `verdict` (den afslører det i fasens sidste sekunder); telefonerne først i
`guess`. Telefonerne får ingen stikord i `clues` (de kigger på storskærmen). Speakeren siger aldrig ordet eller spionen
før `result`; dommens replikker spørger kun.
```jsonc
{
"game": "spion",
"phase": "vote", // lobby | rules | clue | clues | talk | vote | verdict | guess | result | final
"round": {"number": 2, "of": 4, "name": "Runde 2 af 4"},
"players": [{"id": "p1", "name": "Asger", "color": "#ff4d6d", "score": 100, "connected": true, "vip": true,
"done": false, "in_round": true}], // done = har skrevet stikord (clue) / stemt (vote)
"theme": {"name": "Transport", "icon": "🚂"},
"spies": 1, // hvor mange, ikke hvem
"clues": {"items": [{"player_id": "p3", "text": "vinger"}, {"player_id": "p1", "text": null}]},
// fase clues (kun storskærmen har items): {"started_at", "intro_ms", "clue_ms", "items"}
"votes": {"counts": [{"player_id": "p2", "votes": 4}], "audience": [{"player_id": "p2", "votes": 1}],
"accused": "p2", "accused_is_spy": true, "by": []}, // verdict/guess/result; by (hvem stemte på hvem) i result
"result": {"spies": ["p2"], "spy_names": ["Sus"], "word": "flyvemaskine", "caught": "p2",
"guess": {"text": "helikopter", "correct": false}, "spy_won": false, "deltas": {"p1": 100}},
"me": {"id": "p1", "role": "player", "card": {"spy": false, "theme": "Transport", "word": "flyvemaskine"},
"clue": "vinger", "vote": "p2", "can_guess": false}
}
```