Scrypt / tests /test_director.py
IMJONEZZ's picture
SCRYPT: initial commit — game, sandbox, Warden, Space web layer
9fca766
Raw
History Blame Contribute Delete
4 kB
"""Director tests: dominance assessment, bounded interventions, mercy."""
import random
import pytest
from scrypt.data import load_content
from scrypt.engine.cards import CardInstance, make_card
from scrypt.engine.combat import CombatState, IllegalMove
from scrypt.inference.backend import ScriptedBackend
from scrypt.warden.director import CRUSHED, DOMINATING, EVEN, Director, assess
TITAN = make_card("titan", power=6, health=6)
GRUNT = make_card("grunt", power=2, health=2)
def fight(turn: int = 3, scale: int = 0) -> CombatState:
state = CombatState(main_deck=[GRUNT] * 4, side_deck=[], script=[[]], seed=1)
state.turn = turn
state.scale = scale
return state
def make_director(backend=None, seed: int = 0) -> Director:
return Director(content=load_content(), rng=random.Random(seed), backend=backend)
# ----------------------------------------------------------------- assess
def test_assess_reads_dominance_and_collapse():
state = fight(scale=3)
state.player_row[0] = CardInstance(spec=TITAN)
assert assess(state) is DOMINATING
crushed = fight(scale=-3)
crushed.foe_row[0] = CardInstance(spec=TITAN)
crushed.hand.clear()
assert assess(crushed) is CRUSHED
assert assess(fight(scale=0)) is EVEN
# ----------------------------------------------------------- intervention
async def test_no_intervention_in_opening_turns():
state = fight(turn=0, scale=5)
state.player_row[0] = CardInstance(spec=TITAN)
assert await make_director().consider(state) is None
async def test_dominating_triggers_one_bounded_intervention():
director = make_director()
state = fight(scale=3)
state.player_row[0] = CardInstance(spec=TITAN)
first = await director.consider(state)
assert first is not None and first.action in ("throttle", "reinforce")
kinds = {e.kind for e in state.events}
assert kinds & {"throttled", "reinforced"}
# The per-fight budget refuses a second act of cruelty.
assert await director.consider(state) is None
director.new_fight()
assert await director.consider(state) is not None
async def test_crushed_gets_secret_mercy():
director = make_director()
state = fight(scale=-4)
state.foe_row[0] = CardInstance(spec=TITAN)
state.foe_queue[1] = CardInstance(spec=TITAN)
state.hand.clear()
mercy = await director.consider(state)
assert mercy is not None and mercy.action == "withdraw"
assert state.foe_queue[1] is None
assert any(e.kind == "withdrawn" for e in state.events)
async def test_backend_picks_from_menu():
backend = ScriptedBackend(
playbook={"Choose one intervention": '{"tool": "intervene", "args": {"action": "reinforce"}}'}
)
director = make_director(backend=backend)
state = fight(scale=3)
state.player_row[0] = CardInstance(spec=TITAN)
result = await director.consider(state)
assert result is not None and result.action == "reinforce"
assert any(e.kind == "reinforced" for e in state.events)
async def test_nonsense_backend_falls_back_to_rng():
backend = ScriptedBackend(default="I refuse to use tools.")
director = make_director(backend=backend, seed=4)
state = fight(scale=4)
state.player_row[0] = CardInstance(spec=TITAN)
result = await director.consider(state)
assert result is not None and result.action in ("throttle", "reinforce")
# ------------------------------------------------------------ engine hooks
def test_throttle_floors_at_zero_power():
state = fight()
weak = CardInstance(spec=make_card("weak", power=1, health=1))
state.player_row[0] = weak
state.throttle_player_card(0)
state.throttle_player_card(0)
assert weak.power == 0 # never negative
def test_reinforce_requires_empty_lane():
state = fight()
state.foe_queue[0] = CardInstance(spec=GRUNT)
with pytest.raises(IllegalMove):
state.reinforce_queue(0, TITAN)
with pytest.raises(IllegalMove):
state.withdraw_queue(2)