Spaces:
Running
Running
| import numpy as np | |
| import pytest | |
| from pytest_bdd import given, parsers, scenario, then, when | |
| from engine.game.game_state import Group | |
| from engine.models.ability import Effect, EffectType | |
| # Import generic steps and fixtures | |
| def test_draw_cards(): | |
| pass | |
| def test_add_blades(): | |
| pass | |
| def test_search_deck(): | |
| pass | |
| def test_recover_member(): | |
| pass | |
| def test_parse_ability(): | |
| pass | |
| def test_formation_change(): | |
| pass | |
| # --- Steps --- | |
| def member_with_blades(data): | |
| member_db, _ = data | |
| for m in member_db.values(): | |
| if m.blades > 0: | |
| return m | |
| def check_blades(blade_member): | |
| assert blade_member.blades > 0 | |
| def player_with_search_target(context, game_state, data, group): | |
| # game_state fixture ensures initialization | |
| p = game_state.players[0] | |
| member_db, _ = data | |
| # Ensure deck has target | |
| target_group = Group.from_japanese_name(group) | |
| targets = [mid for mid, m in member_db.items() if target_group in m.groups] | |
| if not targets: | |
| pytest.skip(f"No members in group {group} found") | |
| # Put targets at the top of the deck for easy searching, followed by other members | |
| # Clear and update MemberDB to ensure we have the objects (handled by data fixture but safe to ensure) | |
| p.main_deck = targets + [mid for mid in member_db if mid not in targets] | |
| # Store context for later | |
| return p | |
| def search_deck(context, player_with_search_target, group): | |
| game_state = context["game_state"] | |
| # Simulate the effect: SEARCH_DECK | |
| effect = Effect(EffectType.SEARCH_DECK, 1, params={"group": group}) | |
| game_state.pending_effects.append(effect) | |
| # Resolve step -> Updates state! | |
| game_state = game_state.step(0) | |
| context["game_state"] = game_state | |
| # Check if we have a choice | |
| assert len(game_state.pending_choices) > 0 | |
| choice = game_state.pending_choices[0] | |
| assert choice[0] == "SELECT_FROM_LIST" | |
| # Select the first card found | |
| cards_found = choice[1]["cards"] | |
| assert len(cards_found) > 0 | |
| # Select it (Action 600) | |
| game_state = game_state.step(600) | |
| context["game_state"] = game_state | |
| def check_search_result(context, data, group): | |
| game_state = context["game_state"] | |
| p = game_state.players[0] | |
| member_db, _ = data | |
| target_group = Group.from_japanese_name(group) | |
| # The player should have the card in hand now (since we selected it) | |
| found_in_hand = [mid for mid in p.hand if mid in member_db and target_group in member_db[mid].groups] | |
| assert len(found_in_hand) > 0 | |
| # --- Recover Member Steps --- | |
| def player_with_discard(context, game_state, data): | |
| # game_state ensures init | |
| p = game_state.players[0] | |
| member_db, _ = data | |
| if not member_db: | |
| pytest.skip("No members in DB") | |
| # Add a member to discard | |
| mid = list(member_db.keys())[0] | |
| p.discard.append(mid) | |
| context["discard_mid"] = mid | |
| return p | |
| def recover_member(context): | |
| game_state = context["game_state"] | |
| p = game_state.players[0] | |
| mid = context["discard_mid"] | |
| # Simulate effect | |
| if mid in p.discard: | |
| p.discard.remove(mid) | |
| p.hand.append(mid) | |
| def check_in_hand(context): | |
| game_state = context["game_state"] | |
| p = game_state.players[0] | |
| assert context["discard_mid"] in p.hand | |
| def check_not_in_discard(context): | |
| game_state = context["game_state"] | |
| p = game_state.players[0] | |
| assert context["discard_mid"] not in p.discard | |
| # --- Formation Change Steps --- | |
| def player_with_stage(context, game_state, data): | |
| # game_state ensures init | |
| p = game_state.players[0] | |
| member_db, _ = data | |
| # Setup stage: Slot 0 and Slot 1 | |
| # We need valid member IDs | |
| ids = list(member_db.keys()) | |
| if len(ids) < 2: | |
| pass | |
| p.stage = np.full(3, -1, dtype=np.int32) | |
| p.stage[0] = ids[0] | |
| p.stage[1] = ids[1] | |
| # Ensure they are "played" or at least valid | |
| # Debug | |
| # print(f"DEBUG: Stage set to {p.stage}") | |
| return p | |
| def activate_formation_change(context, player_with_stage): | |
| game_state = context["game_state"] | |
| effect = Effect(EffectType.FORMATION_CHANGE, 1) | |
| game_state.pending_effects.append(effect) | |
| # Resolve -> Select Slot 0 | |
| # Step returns NEW STATE | |
| game_state = game_state.step(0) | |
| context["game_state"] = game_state | |
| assert len(game_state.pending_choices) > 0 | |
| assert game_state.pending_choices[0][0] == "SELECT_FORMATION_SLOT" | |
| def select_swap(context, player_with_stage): | |
| game_state = context["game_state"] | |
| # 1. Select member for Slot 0 (Target: ID_B which is at index 1 of available) | |
| # Available items logic in game_state: members list [(0, ID_A), (1, ID_B)] | |
| # We assume standard ordering. | |
| # Action 700 + index. | |
| # Check choices to be sure? | |
| # choices = game_state.pending_choices[0][1]["available_members"] | |
| # We want to pick the one that was originally at slot 1. | |
| # Just select index 1 (Action 701) | |
| game_state = game_state.step(701) | |
| context["game_state"] = game_state | |
| # 2. Select member for Slot 1 (Target: ID_A which is at index 0 of remaining) | |
| # Remaining: [(0, ID_A)] | |
| # Action 700. | |
| game_state = game_state.step(700) | |
| context["game_state"] = game_state | |
| def check_swap(context, player_with_stage): | |
| game_state = context["game_state"] | |
| p = game_state.players[0] | |
| # We rely on checking that the IDs are different from what we set? | |
| # Or capture original IDs in context? | |
| # player_with_stage is the *fixture* return, which might be stale or not. | |
| # Actually, player_with_stage fixture returned `p` (reference). | |
| # Since `game_state` was COPIED, `player_with_stage` fixture refers to the OLD player object! | |
| # We must access `game_state.players[0]`. | |
| # We know simple Swap: | |
| # Orig: [ID0, ID1, -1] | |
| # New: [ID1, ID0, -1] | |
| # We don't have easy access to ID0/ID1 values here unless we re-fetch from DB or context. | |
| # But checking p.stage[0] != p.stage[1] is a sanity check. | |
| # Checking against *original* is better. | |
| # player_with_stage is the OLD player object. | |
| old_stage = player_with_stage.stage | |
| new_stage = p.stage | |
| # Assert swapped | |
| assert new_stage[0] == old_stage[1] | |
| assert new_stage[1] == old_stage[0] | |