Tegn og Gæt: telefonerne ser kun det, storskærmen har vist
Under afsløringen får telefonerne kun de bidrag, der allerede er vist (og om kæden overlevede, når slutningen er vist). Rummet sender et nyt snapshot, hver gang et trin begynder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,29 @@ class TegnRoom(rooms.BaseRoom):
|
||||
self.narrator = Narrator(lines=LINES)
|
||||
self._last_join_line = 0.0
|
||||
self._lobby_waiting_said = False
|
||||
self._beat_timers: list[asyncio.TimerHandle] = []
|
||||
|
||||
def changed(self):
|
||||
super().changed()
|
||||
self._schedule_beats()
|
||||
|
||||
def _schedule_beats(self):
|
||||
"""Telefonerne får kun de trin, storskærmen har vist: et nyt snapshot hver gang et trin begynder."""
|
||||
for h in self._beat_timers:
|
||||
h.cancel()
|
||||
self._beat_timers = []
|
||||
g = self.game
|
||||
if g.phase != "reveal":
|
||||
return
|
||||
loop, now = asyncio.get_running_loop(), time.time()
|
||||
for t in g.beat_starts():
|
||||
if t > now:
|
||||
self._beat_timers.append(loop.call_later(t - now + 0.02, self.broadcast))
|
||||
|
||||
def close(self):
|
||||
super().close()
|
||||
for h in self._beat_timers:
|
||||
h.cancel()
|
||||
|
||||
# ── handlinger ───────────────────────────────────────────────────────────
|
||||
async def host_action(self, conn, action: str, content: dict, now: float) -> bool:
|
||||
|
||||
+32
-12
@@ -540,15 +540,14 @@ class Game:
|
||||
el -= b["ms"]
|
||||
return len(self.plan) - 1
|
||||
|
||||
def _revealed(self, entry_id: str, now: float) -> bool:
|
||||
def shown_beats(self, now: float) -> list[dict]:
|
||||
"""Trinene, storskærmen har vist indtil nu (inklusive det aktuelle)."""
|
||||
i = self.beat_at(now)
|
||||
if i is None:
|
||||
return False
|
||||
for b in self.plan[: i + 1]:
|
||||
chain = self.chains[b["chain"]]
|
||||
if 0 <= b["entry"] < len(chain) and chain[b["entry"]]["id"] == entry_id:
|
||||
return True
|
||||
return False
|
||||
return [] if i is None else self.plan[: i + 1]
|
||||
|
||||
def _revealed(self, entry_id: str, now: float) -> bool:
|
||||
return any(0 <= b["entry"] < len(self.chains[b["chain"]]) and self.chains[b["chain"]][b["entry"]]["id"] == entry_id
|
||||
for b in self.shown_beats(now))
|
||||
|
||||
def like(self, member_id: str, entry_id: str, now: float) -> None:
|
||||
self._require_phase("reveal")
|
||||
@@ -736,10 +735,7 @@ class Game:
|
||||
"started_at": int(self.reveal_started_at * 1000) if self.reveal_started_at else None,
|
||||
"plan": [{"chain": b["chain"], "entry": b["entry"], "ms": b["ms"], "audio": b["audio"]} for b in self.plan],
|
||||
# Tegningerne følger ikke med her (de er store); storskærmen får dem i beskeden "drawings".
|
||||
"chains": [{"owner": self.order[k], "survived": self.survived[k],
|
||||
"entries": [{"id": e["id"], "kind": e["kind"], "by": e["by"], "text": e["text"],
|
||||
"auto": e["auto"], "likes": len(self.likes.get(e["id"], ()))} for e in chain]}
|
||||
for k, chain in enumerate(self.chains)],
|
||||
"chains": self._reveal_chains(now, everything=host),
|
||||
}
|
||||
if self.phase == "final":
|
||||
state["final"] = self.final_result
|
||||
@@ -756,6 +752,30 @@ class Game:
|
||||
}
|
||||
return state
|
||||
|
||||
def _reveal_chains(self, now: float, everything: bool) -> list[dict]:
|
||||
"""Kæderne i afsløringen. Telefonerne får kun det, storskærmen har vist (ellers kunne man læse
|
||||
sidste gæt før tid); storskærmen får det hele og styrer selv tempoet. Rummet sender et nyt
|
||||
snapshot ved hvert trin."""
|
||||
shown = None if everything else {(b["chain"], b["entry"]) for b in self.shown_beats(now)}
|
||||
out = []
|
||||
for k, chain in enumerate(self.chains):
|
||||
entries = [{"id": e["id"], "kind": e["kind"], "by": e["by"], "text": e["text"], "auto": e["auto"],
|
||||
"likes": len(self.likes.get(e["id"], ()))}
|
||||
for i, e in enumerate(chain) if shown is None or (k, i) in shown]
|
||||
ended = shown is None or (k, len(chain)) in shown
|
||||
out.append({"owner": self.order[k], "survived": self.survived[k] if ended else None, "entries": entries})
|
||||
return out
|
||||
|
||||
def beat_starts(self) -> list[float]:
|
||||
"""Hvornår hvert trin i afsløringen begynder (sekunder siden epoch)."""
|
||||
if self.reveal_started_at is None:
|
||||
return []
|
||||
t, out = self.reveal_started_at, []
|
||||
for b in self.plan:
|
||||
out.append(t)
|
||||
t += b["ms"] / 1000
|
||||
return out
|
||||
|
||||
def drawings(self) -> dict[str, list]:
|
||||
"""Alle tegninger i den aktuelle afsløring (til storskærmen)."""
|
||||
return {e["id"]: e["strokes"] for chain in self.chains for e in chain if e["kind"] == "drawing"}
|
||||
|
||||
@@ -256,6 +256,24 @@ class RevealTests(TegnTestCase):
|
||||
self.assertEqual(len(self.g.drawings()), 4 * 2)
|
||||
self.assertEqual(snap["reveal"]["chains"][0]["entries"][0]["kind"], "word")
|
||||
|
||||
def test_phones_only_get_what_the_tv_has_shown(self):
|
||||
a, b, c = self.play_game(3, survive=True)
|
||||
def chains(t):
|
||||
return self.g.snapshot(t, viewer=a)["reveal"]["chains"]
|
||||
self.assertEqual([len(ch["entries"]) for ch in chains(self.now)], [0, 0, 0]) # kædens intro
|
||||
t = self.now + (engine.CHAIN_INTRO_MS + 10) / 1000
|
||||
self.assertEqual([e["id"] for e in chains(t)[0]["entries"]], ["c0e0"])
|
||||
self.assertIsNone(chains(t)[0]["survived"]) # afgøres først på storskærmen
|
||||
t = self.now + (engine.CHAIN_INTRO_MS + engine.WORD_MS + engine.DRAWING_MS + engine.GUESS_MS + 10) / 1000
|
||||
ch = chains(t)
|
||||
self.assertEqual((len(ch[0]["entries"]), ch[0]["survived"], len(ch[1]["entries"])), (3, True, 0))
|
||||
self.assertNotIn("kat", str(ch[1:]))
|
||||
host = self.g.snapshot(self.now, host=True)["reveal"]["chains"]
|
||||
self.assertEqual([len(x["entries"]) for x in host], [3, 3, 3]) # storskærmen får alt
|
||||
starts = self.g.beat_starts()
|
||||
self.assertEqual(len(starts), len(self.g.plan))
|
||||
self.assertEqual(starts[1], self.now + engine.CHAIN_INTRO_MS / 1000)
|
||||
|
||||
def test_likes_only_on_revealed_drawings_and_guesses_by_others(self):
|
||||
a, b, c = self.play_game(3)
|
||||
word, drawing, guess = self.g.chains[0] # a's kæde: ord af a, tegning af b, gæt af c
|
||||
|
||||
Reference in New Issue
Block a user