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