"""Live table roster: seat players, attach reads by handle, roster on the HUD.""" from __future__ import annotations import importlib import numpy as np import pytest def _fake_embed(texts): out = [] for t in texts: v = np.zeros(64, dtype=np.float32) for w in t.lower().split(): v[hash(w) % 64] += 1.0 out.append((v if v.any() else np.full(64, 1e-6, dtype=np.float32)).tolist()) return out @pytest.fixture def mods(tmp_path, monkeypatch): monkeypatch.setenv("LYRA_DB_PATH", str(tmp_path / "test.db")) from lyra import llm monkeypatch.setattr(llm, "embed", _fake_embed) import lyra.memory as memory importlib.reload(memory) import lyra.poker as poker importlib.reload(poker) import lyra.tools as tools importlib.reload(tools) return poker, tools def test_seat_players_builds_roster(mods): poker, _ = mods poker.start_session(venue="Meadows", buy_in=300) n = poker.seat_players(["TAG", "Jonathan", {"name": "Wheelz", "seat": "3"}]) assert n == 3 roster = poker.session_roster() names = {r["name"] for r in roster} assert names == {"TAG", "Jonathan", "Wheelz"} assert next(r for r in roster if r["name"] == "Wheelz")["seat"] == "3" def test_read_attaches_to_seated_player_by_handle(mods): poker, _ = mods poker.start_session(venue="Meadows", buy_in=300) poker.seat_players(["TAG"]) poker.add_read(note="limped A4o from the SB, UTG straddle", name="TAG") roster = poker.session_roster() tag = next(r for r in roster if r["name"] == "TAG") assert tag["reads"] == 1 and "A4o" in tag["last_note"] # No duplicate TAG spawned — the read landed on the seated player. assert sum(p["name"] == "TAG" for p in poker.get_villain_file()) == 1 def test_seat_players_tool_and_roster_in_hud(mods): poker, tools = mods poker.start_session(venue="Meadows", buy_in=300) out = tools.dispatch("seat_players", {"players": [{"name": "TAG"}, {"name": "JD"}]}, {}) assert "TAG" in out and "JD" in out assert len(poker.hud()["roster"]) == 2 def test_unseat_player_removes_from_roster_keeps_history(mods): poker, _ = mods poker.start_session(venue="Meadows", buy_in=300) poker.seat_players(["TAG"]) poker.add_read(note="showed a bluff", name="TAG") assert poker.unseat_player(name="TAG") is True assert poker.session_roster() == [] # off the table assert poker.player_profile("TAG")["reads"] # history intact def test_clear_table_empties_roster_keeps_reads(mods): poker, tools = mods poker.start_session(venue="Meadows", buy_in=300) poker.seat_players(["TAG", "Jonathan"]) poker.add_read(note="limped A4o", name="TAG") out = tools.dispatch("clear_table", {}, {}) assert "cleared" in out.lower() assert poker.session_roster() == [] # roster emptied assert poker.player_profile("TAG")["reads"] # reads kept # A live session is untouched by clearing the table. assert poker.live_session() is not None def test_seat_players_replace_swaps_to_new_table(mods): poker, tools = mods poker.start_session(venue="Meadows", buy_in=300) poker.seat_players(["TAG", "Jonathan"]) tools.dispatch("seat_players", {"players": [{"name": "Doyle"}, {"name": "Ivey"}], "replace": True}, {}) assert {r["name"] for r in poker.session_roster()} == {"Doyle", "Ivey"} def test_seat_players_accepts_plain_name_list_via_tool(mods): poker, tools = mods poker.start_session(venue="Meadows", buy_in=300) tools.dispatch("seat_players", {"players": "TAG, JD, Wheelz"}, {}) assert {r["name"] for r in poker.session_roster()} == {"TAG", "JD", "Wheelz"}