Spaces:
Sleeping
Sleeping
File size: 8,256 Bytes
bb3fbf9 |
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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
import numpy as np
from pytest_bdd import given, parsers, scenarios, then, when
from engine.game.game_state import GameState
from engine.models.ability import Effect, EffectType
from engine.models.card import LiveCard, MemberCard
from engine.models.enums import Group
scenarios("../features/recovery.feature")
# --- Fixtures & Givens ---
@given("a player with a discard pile", target_fixture="player_with_discard")
def player_with_discard(context, game_state):
p = game_state.players[0]
p.hand = []
p.discard = []
context["initial_hand_size"] = 0
return p
@given(parsers.parse('the discard pile contains a live card "{name}" with ID {cid:d}'))
def add_live_to_discard(context, game_state, data, name, cid):
p = game_state.players[0]
_, live_db = data
# Create simple live card if not in DB
if cid not in live_db:
# Create a mock/real live card
# We need to insert it into the DB fixture?
# The data fixture loads from JSON. We can monkeypatch into the generic DB.
# Use real LiveCard
live_db[cid] = LiveCard(
card_id=cid,
card_no=f"LIVE-{cid}",
name=name,
score=1000,
required_hearts=np.zeros(7),
abilities=[],
groups=[],
units=[],
img_path="",
)
p.discard.append(cid)
context[f"card_id_{name}"] = cid
@given(parsers.parse('the discard pile contains a member card "{name}" with ID {cid:d}'))
def add_member_to_discard(context, game_state, data, name, cid):
p = game_state.players[0]
member_db, _ = data
# Create mock/real member
if cid not in member_db:
member_db[cid] = MemberCard(
card_id=cid,
card_no=f"TEST-{cid}",
name=name,
cost=1,
groups=[],
hearts=np.zeros(6),
blade_hearts=np.zeros(7),
blades=1,
)
p.discard.append(cid)
context[f"card_id_{name}"] = cid
@given("the discard pile contains the following members:")
def add_members_table(context, game_state, data, datatable):
# p = game_state.players[0]
member_db, _ = data
# datatable is a list of rows (if supported by pytest-bdd table parser)?
# Actually pytest-bdd "datatable" usually comes from Examples or manual parsing if step uses "datatable" param name?
# No, generic steps don't support table automatically unless parsed.
# We should use `scenarios` and `parsers` carefully or manual table parsing.
# For now, we assume simple step implementation parsing context logic manually or using simple loop if possible?
# Actually, allow `datatable` or similar logic.
# Wait, BDD standard step doesn't pass a table object unless specifically configured.
# I'll manually parse headings if needed, but `pytest-bdd` doesn't pass table in step args automatically like Behave.
# I'll implement it by parsing row by row or assume the step handles setup manually?
# Ah, pytest-bdd uses `target_fixture` or we access `request`?
# Actually, simpler: just iterate in the test code? No.
# I will replace the datatable step with individual "And" steps in the feature file if I can't easily parse table.
# BUT, I want to learn. Let's use simple multiple "And" steps or parse distinct lines.
# Actually, let's keep it simple for now and use specific step setups in the feature, or handle dict-like parsing?
# I'll use a simplified implementation where I define specific members in the python code matching the scenario.
# Let's pivot: The step definition will just setup specific hardcoded data for this scenario "Recover a member with filters".
# Or I can use `parsers.parse`? No.
# I'll implement "the discard pile contains the following members:" but the datatable handling is tricky.
# I'll rewrite the feature to be explicit step-by-step to avoid table parsing complexity in `pytest-bdd`.
pass
# Simplified step for the specific scenario
@given("the discard pile contains the test members for filtering")
def add_test_members_filtering(context, game_state, data):
p = game_state.players[0]
member_db, _ = data
members = [
(400, "Kotori", "μ's", 3),
(401, "Honoka", "μ's", 5),
(402, "Chika", "Aqours", 2),
]
for cid, name, group, cost in members:
member_db[cid] = MemberCard(
card_id=cid,
card_no=f"TEST-{cid}",
name=name,
cost=cost,
groups=[Group.from_japanese_name(group)],
hearts=np.zeros(6),
blade_hearts=np.zeros(7),
blades=1,
)
p.discard.append(cid)
context[f"card_id_{name}"] = cid
# --- Whhen steps ---
@when("the player activates an effect to recover a live card")
def activate_recover_live(context, game_state):
p = game_state.players[0]
print(f"DEBUG: Discard before recovery: {p.discard}")
print(f"DEBUG: LiveDB keys: {list(GameState.live_db.keys())}")
print(f"DEBUG: Checking discard: {[cid in GameState.live_db for cid in p.discard]}")
effect = Effect(EffectType.RECOVER_LIVE, 1)
game_state.pending_effects.append(effect)
# Trigger effect resolution
game_state = game_state.step(0)
context["game_state"] = game_state
# Check pending
print(f"DEBUG: Pending effects: {game_state.pending_effects}")
print(f"DEBUG: Post-step pending choices: {game_state.pending_choices}")
@when("the player activates an effect to recover a member card")
def activate_recover_member(context, game_state):
effect = Effect(EffectType.RECOVER_MEMBER, 1)
game_state.pending_effects.append(effect)
game_state = game_state.step(0)
context["game_state"] = game_state
@when(parsers.parse('the player activates an effect to recover a member with group "{group}" and max cost {cost:d}'))
def activate_recover_filtered(context, game_state, group, cost):
effect = Effect(EffectType.RECOVER_MEMBER, 1, params={"group": group, "cost_max": cost})
game_state.pending_effects.append(effect)
game_state = game_state.step(0)
context["game_state"] = game_state
@when(parsers.parse('the player selects "{name}" from the recovery choices'))
def select_card_by_name(context, game_state, name):
current_state = context["game_state"]
cid = context[f"card_id_{name}"]
# Validate choice exists
assert len(current_state.pending_choices) > 0
choice = current_state.pending_choices[0]
assert choice[0] == "SELECT_FROM_DISCARD"
available = choice[1]["cards"]
assert cid in available, f"Card {name} ({cid}) not in available choices {available}"
idx = available.index(cid)
# Action 660 + index
current_state = current_state.step(660 + idx)
context["game_state"] = current_state
# --- Then steps ---
@then(parsers.parse('"{name}" should be in the player\'s hand'))
def check_in_hand_named(context, game_state, name):
current_state = context["game_state"]
p = current_state.players[0]
cid = context[f"card_id_{name}"]
assert cid in p.hand
@then(parsers.parse('"{name}" should not be in the player\'s discard'))
def check_not_in_discard_named(context, game_state, name):
current_state = context["game_state"]
p = current_state.players[0]
cid = context[f"card_id_{name}"]
assert cid not in p.discard
@then(parsers.parse('the recovery choices should include "{name}"'))
def check_choice_includes(context, game_state, name):
current_state = context["game_state"]
cid = context[f"card_id_{name}"]
choice = current_state.pending_choices[0]
available = choice[1]["cards"]
assert cid in available
@then(parsers.parse('the recovery choices should not include "{name}"'))
def check_choice_excludes(context, game_state, name):
current_state = context["game_state"]
cid = context[f"card_id_{name}"]
choice = current_state.pending_choices[0]
available = choice[1]["cards"]
assert cid not in available
|