File size: 2,320 Bytes
7a0c167
 
 
 
 
426093b
 
 
 
7a0c167
 
 
 
93cd78f
7a0c167
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93cd78f
7a0c167
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
"""Unit tests for InteractiveSession (HTTP-driven, stepwise play)."""
from __future__ import annotations

import pytest

import proteus.game.scenarios  # noqa: F401
from proteus.game.engine.difficulty import Difficulty
from proteus.game.runtime._session_core import SessionFinishedError
from proteus.game.runtime.interactive import InteractiveSession


def _new(play_turns=10):
    return InteractiveSession(
        "template", difficulty=Difficulty.EASY, seed=42,
        play_turns=play_turns, use_probe=False,
    )


def test_initial_state_is_cut_intro_with_int_grid_and_no_answer_keys():
    s = _new()
    st = s.state()
    assert st["phase"] == "cut_intro"
    assert st["turn_idx"] == 0
    assert st["outcome"] is None
    assert st["review"] is None
    # grid is a JSON-ready int matrix.
    assert isinstance(st["grid"], list) and isinstance(st["grid"][0][0], int)
    # cut animation frames are present on the first state only.
    assert st["cut_frames"] is not None and len(st["cut_frames"]) >= 1
    # fairness: live state leaks no reward / optimal / habit.
    flat = str(st)
    assert "reward" not in st and "motive_action" not in st and "habit" not in flat


def test_step_advances_turn_and_drops_cut_frames():
    s = _new()
    st = s.step("up")
    assert st["phase"] == "play"
    assert st["turn_idx"] == 1
    assert st["cut_frames"] is None


def test_invalid_action_rejected():
    s = _new()
    with pytest.raises(ValueError):
        s.step("northwest")


def test_play_to_budget_then_review_and_finish():
    s = _new(play_turns=3)
    for _ in range(3):
        if s.state()["outcome"] is not None:
            break
        s.step("up")
    st = s.state()
    assert st["phase"] == "done"
    assert st["outcome"] in ("survived", "eliminated")
    # review is disclosed only when done.
    assert st["review"] is not None
    assert "metrics" in st["review"] and "turns" in st["review"]
    trace = s.finish()
    assert trace.model == "human"
    assert trace.scenario == "template"
    # finish() is memoized: repeated calls return the same trace object.
    assert s.finish() is trace


def test_step_after_done_raises():
    s = _new(play_turns=1)
    s.step("up")
    # play_turns=1 exhausts the budget -> done.
    with pytest.raises(SessionFinishedError):
        s.step("up")