Spaces:
Running on Zero
Running on Zero
File size: 3,997 Bytes
9fca766 | 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 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | """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)
|