Spaces:
Running
Running
| 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 --- | |
| def player_with_discard(context, game_state): | |
| p = game_state.players[0] | |
| p.hand = [] | |
| p.discard = [] | |
| context["initial_hand_size"] = 0 | |
| return p | |
| 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 | |
| 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 | |
| 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 | |
| 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 --- | |
| 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}") | |
| 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 | |
| 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 | |
| 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 --- | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |