Spaces:
Running on Zero
Running on Zero
| """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) | |