Spaces:
Running
Running
File size: 7,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 |
import os
import sys
# Add project root to path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../")))
try:
import engine_rust
except ImportError:
# Try importing from backend if not in root
try:
from backend import engine_rust
except ImportError:
print("Could not import engine_rust. Make sure it is built.")
sys.exit(1)
print(f"DEBUG engine_rust file: {getattr(engine_rust, '__file__', 'No __file__')}")
def test_order_deck_logic_rust():
# Load RUST_DB
compiled_path = "data/cards_compiled.json"
if not os.path.exists(compiled_path):
print(f"Compiled data not found: {compiled_path}")
return
with open(compiled_path, "r", encoding="utf-8") as f:
json_data = f.read()
db = engine_rust.PyCardDatabase(json_data)
gs = engine_rust.PyGameState(db)
# Setup Logic relying on PyGameState bindings
# We need to construct deck with known IDs.
# Card 467 is PL!N-bp1-002-P
target_id = 467
# We can't directly set fields on PyGameState easily unless exposed.
# But init_game takes decks!
# p0_deck, p1_deck, p0_energy, p1_energy, p0_lives, p1_lives
# We want target_id in HAND.
# init_game draws 6 cards.
# So if we put target_id at end of deck (top), it will be drawn?
# No, deck is popped from end.
# Let's create a deck where target_id is at index 0..5 (drawn)
# And known cards at 6..N (remaining in deck).
# IDs for dummy cards? 1001..1005 might not exist in RUST_DB.
# We should use valid IDs.
# Let's just use 467 for everything to be safe, or some other valid ID.
# Let's use 467 for hand, and maybe 40000 (Action/Energy? No)
# Let's find another valid member ID.
# ID 1 is usually valid?
other_id = 1
# Deck construction:
# Drawn (6): [467, other, other, other, other, other]
# Remaining (Top->Bottom for drawing purposes? Or strictly array?)
# Rust `initialize_game`:
# p.main_deck = deck; (cloned)
# draw 6: p.hand.push(p.main_deck.pop())
# So if we want 467 in hand, we put it at END of the list we pass.
# We also want specific cards in DECK to check ordering.
# Let's use 1, 2, 3 (assuming they exist).
# If not sure, use 467 with different instance IDs?
# Rust engine handles instance IDs internally if we pass base IDs?
# Actually PyGameState::initialize_game takes List[u32].
# It converts to creating unique IDs internally?
# Let's check logic.rs/py_bindings.rs.
# Assuming it takes base IDs and makes them unique or takes UIDs.
# Usually it takes UIDs if passed directly to `deck`.
# server.py does `convert_deck_strings_to_ids` which calls `create_uid`.
# So we should pass UIDs.
BASE_ID_MASK = 0xFFFFF
def mk_uid(base, idx):
return base | (idx << 20)
p0_main = []
# 3 known cards for deck (will be at bottom of our list, so deep in deck? No)
# We want them at TOP of deck (end of list).
# 1. Hand cards (will be popped LAST, so push them FIRST?)
# Wait, `pop` takes from END.
# So if deck is [A, B, C], pop gives C.
# We want 467 to be drawn. So 467 should be at END of `p0_main`.
# Remaining Deck: [Card C, Card B, Card A] <- Top (End)
# Drawn Hand: [..., Target]
# So list structure: [Card C, Card B, Card A, ..., Target]
# Let's use IDs 10, 20, 30 for cards (assuming valid).
# If they are invalid, they might work as dummy cards if DB lookup returns None (or crash).
# Safest is to use valid ID 467 and just use UID distinctions.
c_A = mk_uid(target_id, 10)
c_B = mk_uid(target_id, 11)
c_C = mk_uid(target_id, 12)
c_D = mk_uid(target_id, 13) # Extra card for Draw Phase
p0_main = [c_D, c_C, c_B, c_A] # A on top, then B, C, D.
# Draw phase takes A.
# LookDeck takes B, C, D (3 cards).
# No, we draw 6 cards.
# So we need at least 6 cards ON TOP of these.
hand_uids = [mk_uid(target_id, i) for i in range(6)] # 0..5
p0_main.extend(hand_uids) # [C, B, A, H0, H1, ... H5]
# Last is H5.
# Pop 6 times -> Hand gets H5..H0.
# Remaining deck: [C, B, A]. Top is A.
p1_main = [mk_uid(target_id, 100 + i) for i in range(40)]
# Energy
p0_energy = [mk_uid(40000, i) for i in range(10)]
p1_energy = [mk_uid(40000, 10 + i) for i in range(10)]
# Lives
p0_lives = [mk_uid(target_id, 200 + i) for i in range(3)]
p1_lives = [mk_uid(target_id, 210 + i) for i in range(3)]
gs.initialize_game(p0_main, p1_main, p0_energy, p1_energy, p0_lives, p1_lives)
# Force turn to 1, Phase Main.
# After init, it's MULLIGAN_P1.
# We need to skip mulligan.
# gs.do_action(0) # Pass/End mulligan?
# Action 0 in Mulligan is "End Mulligan"? Or "Check/Pass"?
# `game_state.py` says `a==0`: "【確認】マリガンを実行" (Execute Mulligan).
gs.step(0) # P1 Mulligan
gs.step(0) # P2 Mulligan
# Now Phase.ACTIVE -> Energy -> Draw -> Main?
# We need to reach Main Phase.
# Skip phases.
# In Rust `get_legal_actions` handles phase transitions often?
# No, we must perform actions.
# Active -> Energy (if no start effects).
# Energy: Charge or Pass(0).
# Auto-advance to Main Phase
max_steps = 10
steps = 0
while gs.phase != 4 and steps < max_steps: # 4 = MAIN
print(f"Current Phase: {gs.phase}, stepping 0...")
gs.step(0)
steps += 1
assert gs.phase == 4, f"Failed to reach Main phase. Current: {gs.phase}"
print(f"Reached Main Phase: {gs.phase}")
# Check if we have 467 in hand.
# Hand should be populated.
# Play card from Hand Index 0 (latest drawn) to Center (Area 1).
# Action: 1 + 0*3 + 1 = 2.
print("Playing card...")
gs.step(2)
print(f"Phase after play: {gs.phase}")
print(f"Engine Rust File: {engine_rust.__file__}")
print(f"GS Attributes: {dir(gs)}")
print(f"Rule Log: {gs.rule_log}")
# print(f"Looked Cards: {gs.looked_cards}")
# print(f"Deck Count: {gs.deck_count}")
# CHECK 1: Phase should be RESPONSE (10)
assert gs.phase == 10, f"Expected Phase 10 (Response), got {gs.phase}"
# CHECK 2: pending_choice_type
# PyGameState exposes pending_choice_type (added in my fix? No, was there? check bindings)
# The snippet of logic.rs showed self.pending_choice_type setting.
# Does PyGameState binding expose it?
# If not, we check get_legal_actions.
# CHECK 3: Legal Actions
mask = gs.get_legal_actions() # list of bools
legal_indices = [i for i, x in enumerate(mask) if x]
print(f"Legal Actions: {legal_indices}")
# We expect 3 choices (for 3 cards looked) PLUS Done option.
# 550 + 100 + 0 + 0 = 650
# 650, 651, 652 (Cards)
# 653 (Done)
expected = [650, 651, 652, 653]
for e in expected:
assert e in legal_indices, f"Expected action {e} not found"
print("O_ORDER_DECK logic validated!")
if __name__ == "__main__":
test_order_deck_logic_rust()
|