Hjernen: ingen kald til replikker, der alligevel ikke kan siges
Kan stemmen ikke laves live (WPP_TTS_LIVE=off, ingen nøgle, pause efter en fejl eller lav saldo), siges en fast replik i stedet for hjernens, så kaldene var spildt. Nu springes de over, når tts.live_ok() er falsk: fremskridt under løgnene, drillerier, afsløringens og oplæsningens kommentarer og slutanmeldelsen. Dommeren (judge_lie) kaldes som før, for den afgør, om en løgn er sandheden. Lederens beslutning efter review. Test: i off kaldes dommeren én gang og ingen af de andre; med live-stemme kaldes de alle som før. test_brain's rum-tests siger nu selv, at stemmen er live. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+10
-4
@@ -391,6 +391,12 @@ class Room(BaseRoom):
|
||||
self._schedule_timer()
|
||||
asyncio.get_running_loop().create_task(run())
|
||||
|
||||
def _brain_speaks(self) -> bool:
|
||||
"""Hjernens replikker, der kun bliver sagt (fremskridt, drillerier, kommentarer, slutanmeldelse), caches
|
||||
aldrig. Kan stemmen ikke laves live, siges en fast replik i stedet, så kaldet springes over. Dommeren
|
||||
(judge_lie) påvirker spillet og kaldes altid, når der er en hjerne."""
|
||||
return bool(self.brain) and tts.live_ok()
|
||||
|
||||
def prepare_judging(self):
|
||||
"""Kaldes før en løgn afleveres: hold skærmen et øjeblik, så dommeren når at se den sidste løgn."""
|
||||
if self.brain:
|
||||
@@ -432,7 +438,7 @@ class Room(BaseRoom):
|
||||
f"{names.get(author, 'huset')} {n} gange i aften.")
|
||||
elif t == "scores":
|
||||
self.remember(f"Efter runden fører {ev['leader']}, og {ev['last']} ligger sidst.")
|
||||
elif t == "final" and self.brain:
|
||||
elif t == "final" and self._brain_speaks():
|
||||
players = [{"player_id": p.id, "navn": p.name, "plads": i + 1, "point": p.score, "narrede_andre": p.fooled,
|
||||
"fandt_sandheden": p.correct, "hjerter": p.likes}
|
||||
for i, p in enumerate(sorted(g.players.values(), key=lambda p: -p.score))]
|
||||
@@ -472,7 +478,7 @@ class Room(BaseRoom):
|
||||
# faste replikker med navnet: lyden caches, efterhånden som navnene går igen
|
||||
key = ("lie_progress_retry" if info["forsøg"] > 1 else
|
||||
"lie_progress_fast" if info["sekunder_om_det"] < 15 else "lie_progress")
|
||||
if self.brain and settings.BRAIN_PROGRESS:
|
||||
if self._brain_speaks() and settings.BRAIN_PROGRESS:
|
||||
line = await self.brain.lie_progress(info, list(self.memory))
|
||||
line = instead_of(self.narrator.clean_ai(line), self.narrator.line(key, navn=p.name)) if line else line
|
||||
else:
|
||||
@@ -588,7 +594,7 @@ class Room(BaseRoom):
|
||||
g.hold_reveal(time.time(), max_wait=COMMENT_WAIT + REVEAL_AUDIO_WAIT + 2 + max(0.0, self.speech_end - time.time()))
|
||||
self.changed()
|
||||
names = {p.id: p.name for p in g.players.values()}
|
||||
if self.brain:
|
||||
if self._brain_speaks():
|
||||
# drillerierne er som standard faste (cachet lyd); hjernens personlige udgave er et tilvalg
|
||||
tease_job = (asyncio.get_running_loop().create_task(self._brain_teases(names))
|
||||
if settings.BRAIN_TEASES else None)
|
||||
@@ -662,7 +668,7 @@ class Room(BaseRoom):
|
||||
pitch = {engine.normalize(k): v for k, v in ((g.question or {}).get("pitch") or {}).items()}
|
||||
comments = {a.id: pitch[engine.normalize(a.text)] for a in g.answers if engine.normalize(a.text) in pitch}
|
||||
missing = [{"id": a.id, "text": a.text} for a in g.answers if a.id not in comments]
|
||||
if missing and self.brain:
|
||||
if missing and self._brain_speaks():
|
||||
try:
|
||||
comments.update(await asyncio.wait_for(
|
||||
self.brain.present_lines(dict(g.question or {}), missing), timeout=COMMENT_WAIT))
|
||||
|
||||
@@ -89,7 +89,8 @@ class BrainInGameTests(TransactionTestCase):
|
||||
|
||||
async def test_judge_rejects_lie_and_comments_reach_reveal(self):
|
||||
room = await self._room()
|
||||
with mock.patch.object(tts, "enabled", return_value=True), mock.patch.object(tts, "synth", fake_synth):
|
||||
with mock.patch.object(tts, "enabled", return_value=True), mock.patch.object(tts, "synth", fake_synth), \
|
||||
mock.patch.object(tts, "live_ok", return_value=True): # en live-stemme: hjernens replikker siges
|
||||
host = WebsocketCommunicator(application, f"/ws/host/{room.code}/", headers=HOST_HEADERS)
|
||||
host.scope["client"] = ("127.0.0.1", 5000)
|
||||
await host.connect()
|
||||
@@ -155,7 +156,8 @@ class PresentAndSummaryTests(TransactionTestCase):
|
||||
with mock.patch.object(rooms, "get_brain", return_value=GameBrain(fake)):
|
||||
room = rooms.create_room(await sync_to_async(load_game_questions)(), "agw", "http://testserver")
|
||||
g = room.game
|
||||
with mock.patch.object(tts, "enabled", return_value=True), mock.patch.object(tts, "synth", fake_synth):
|
||||
with mock.patch.object(tts, "enabled", return_value=True), mock.patch.object(tts, "synth", fake_synth), \
|
||||
mock.patch.object(tts, "live_ok", return_value=True): # en live-stemme: hjernens replikker siges
|
||||
import time as _t
|
||||
a, b = g.join("Asger"), g.join("Sus")
|
||||
g.start(None, _t.time())
|
||||
|
||||
@@ -19,7 +19,8 @@ from party.tegn import TegnRoom
|
||||
from tegnoggaet.engine import Game as TegnGame
|
||||
from tegnoggaet.themes import load_themes
|
||||
|
||||
QUESTIONS = [{"id": i, "category": "Kat", "prompt": f"Hvad _____ {i}?", "answer": f"svar{i}"} for i in range(9)]
|
||||
QUESTIONS = [{"id": i, "category_id": i % 3, "category": f"Kat {i % 3}", "prompt": f"Hvad _____ {i}?", "answer": f"svar{i}",
|
||||
"decoys": ["x", "y", "z"]} for i in range(9)]
|
||||
|
||||
|
||||
def http_error(code):
|
||||
@@ -190,3 +191,65 @@ class FallbackSeamTests(CacheMixin, SimpleTestCase):
|
||||
combos = first_chain_generics(r.narrator, r.narrator.variants("reveal_intro")[0])
|
||||
self.assertEqual(len(combos), len(r.narrator.generic_variants("chain_intro_first")))
|
||||
self.assertTrue(all(c.startswith(r.narrator.variants("reveal_intro")[0] + " ") for c in combos))
|
||||
|
||||
|
||||
class BrainOnlyWhenSpokenTests(CacheMixin, SimpleTestCase):
|
||||
"""Lederens beslutning: uden live-stemme kaldes hjernen ikke til det, der kun bliver sagt; dommeren kaldes altid."""
|
||||
|
||||
def brain(self):
|
||||
b = mock.Mock()
|
||||
b.judge_lie = mock.AsyncMock(return_value={"is_truth": False, "reason": ""})
|
||||
b.lie_progress = mock.AsyncMock(return_value="Qxa skriver hurtigt.")
|
||||
b.final_review = mock.AsyncMock(return_value=[])
|
||||
b.reveal_lines = mock.AsyncMock(return_value={})
|
||||
b.teases = mock.AsyncMock(return_value={})
|
||||
b.present_lines = mock.AsyncMock(return_value={})
|
||||
return b
|
||||
|
||||
def play(self):
|
||||
"""Et rum med hjerne, ført gennem ét spørgsmål til afsløringen; hjernens kald tælles."""
|
||||
calls = {}
|
||||
|
||||
async def run():
|
||||
g = FofGame("TEST", QUESTIONS, rng=random.Random(1))
|
||||
g.show_rules = False
|
||||
r = Room("TEST", g, "agw")
|
||||
r.brain = self.brain()
|
||||
now = time.time()
|
||||
ps = [g.join(n) for n in ("Qxa", "Zyb", "Wvc")]
|
||||
g.start(None, now)
|
||||
g.choose_category(g.chooser_id, g.category_options[0]["id"], now)
|
||||
g.tick(now + 60)
|
||||
await r._judge(ps[0].id, "en løgn", dict(g.question))
|
||||
await r._progress_remark(ps[1].id) # mens der løjes
|
||||
for i, p in enumerate(ps):
|
||||
g.submit_lie(p.id, f"løgn {i}", now)
|
||||
truth = next(a for a in g.answers if a.is_truth)
|
||||
for p in ps:
|
||||
g.guess(p.id, truth.id, now)
|
||||
g.drain_events()
|
||||
await r._narrate_reveal()
|
||||
g.answers = g.answers[:2]
|
||||
await r._narrate_present()
|
||||
r._think({"type": "final"})
|
||||
await asyncio.sleep(0.01)
|
||||
r.close()
|
||||
for name in ("judge_lie", "lie_progress", "final_review", "reveal_lines", "present_lines"):
|
||||
calls[name] = getattr(r.brain, name).await_count
|
||||
with override_settings(BRAIN_PROGRESS=True, BRAIN_TEASES=True), mock.patch.object(rooms, "MAX_HOLD", 0), \
|
||||
mock.patch.object(rooms, "COMMENT_WAIT", 0.2), mock.patch.object(rooms, "REVEAL_AUDIO_WAIT", 0.2), \
|
||||
self.fake_fetch():
|
||||
asyncio.run(run())
|
||||
return calls
|
||||
|
||||
def test_off_skips_speech_only_calls_but_still_judges(self):
|
||||
with override_settings(TTS_LIVE="off"):
|
||||
calls = self.play()
|
||||
self.assertEqual(calls, {"judge_lie": 1, "lie_progress": 0, "final_review": 0, "reveal_lines": 0,
|
||||
"present_lines": 0})
|
||||
|
||||
def test_live_calls_the_brain_as_before(self):
|
||||
calls = self.play()
|
||||
self.assertEqual(calls["judge_lie"], 1)
|
||||
self.assertEqual((calls["lie_progress"], calls["final_review"], calls["reveal_lines"], calls["present_lines"]),
|
||||
(1, 1, 1, 1))
|
||||
|
||||
Reference in New Issue
Block a user