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: from backend import engine_rust except ImportError: print("Could not import engine_rust. Make sure it is built.") sys.exit(1) def print_gs_state(gs, p_idx=0): p = gs.get_player(p_idx) print(f"--- GS State (Phase {gs.phase}) ---") print(f" Hand: {p.hand}") print(f" Deck: {p.deck}") print(f" Discard: {p.discard}") print(f" Looked: {p.looked_cards}") mask = gs.get_legal_actions() legal = [i for i, x in enumerate(mask) if x] print(f" Legal Actions: {legal[:20]}... (Total {len(legal)})") print(" Rule Log:") for entry in gs.rule_log: print(f" {entry}") def get_play_action(gs, p_idx, target_cid): mask = gs.get_legal_actions() p = gs.get_player(p_idx) for h_idx in range(len(p.hand)): if p.hand[h_idx] == target_cid: for slot in range(3): aid = 1 + h_idx * 3 + slot if aid < len(mask) and mask[aid]: return aid print(f"ERROR: Could not find play action for cid {target_cid} in hand {p.hand}") return None def test_remainder_modes_real(): compiled_path = "data/cards_compiled.json" with open(compiled_path, "r", encoding="utf-8") as f: db = engine_rust.PyCardDatabase(f.read()) dummy = 1 # Use card ID 1 as dummy/energy (cost 1 typically) c_A, c_B, c_C = 10, 20, 30 # --- Mode 0: DISCARD (Card 467) --- print("\n\n=== Testing REMAINDER=DISCARD (Card 467) ===") gs = engine_rust.PyGameState(db) gs.silent = False # Use real energy cards or just anything valid. 467 itself? # Actually, card 467 costs 2. Let's make sure we have energy. gs.initialize_game([467]*40, [467]*40, [dummy]*10, [dummy]*10, [dummy]*3, [dummy]*3) # Process start of game gs.step(0); gs.step(0) while gs.phase != 4: gs.step(0) p0 = gs.get_player(0) p0.deck = [1, c_C, c_B, c_A] # c_A is top gs.set_player(0, p0) aid = get_play_action(gs, 0, 467) if aid is None: print_gs_state(gs) raise ValueError("No play action for 467") print(f"Playing 467 with action {aid}") gs.step(aid) if gs.phase != 10: print_gs_state(gs) raise ValueError(f"Expected phase 10 (Response), got {gs.phase}") print("Choice 0 (Put A on top)...") gs.step(550) print("Choice 3 (Done)...") gs.step(553) p = gs.get_player(0) print_gs_state(gs) assert 10 in p.deck assert 20 in p.discard and 30 in p.discard print("DISCARD passed.") # --- Mode 1: DECK_TOP (Card 566) --- print("\n\n=== Testing REMAINDER=DECK_TOP (Card 566) ===") gs = engine_rust.PyGameState(db) gs.silent = False gs.initialize_game([566]*40, [dummy]*40, [dummy]*10, [dummy]*10, [dummy]*3, [dummy]*3) gs.step(0); gs.step(0) while gs.phase != 4: gs.step(0) p0 = gs.get_player(0) p0.deck = [1, c_C, c_B, c_A] gs.set_player(0, p0) aid = get_play_action(gs, 0, 566) if aid is None: print_gs_state(gs) raise ValueError("No play action for 566") gs.step(aid) gs.step(550) gs.step(553) p = gs.get_player(0) print_gs_state(gs) assert p.deck[-3:] == [10, 20, 30] print("DECK_TOP passed.") # --- Mode 2: DECK_BOTTOM (Card 565) --- print("\n\n=== Testing REMAINDER=DECK_BOTTOM (Card 565) ===") gs = engine_rust.PyGameState(db) gs.silent = False gs.initialize_game([565]*40, [dummy]*40, [dummy]*10, [dummy]*10, [dummy]*3, [dummy]*3) gs.step(0); gs.step(0) while gs.phase != 4: gs.step(0) p0 = gs.get_player(0) p0.deck = [1, c_C, c_B, c_A] gs.set_player(0, p0) aid = get_play_action(gs, 0, 565) if aid is None: print_gs_state(gs) raise ValueError("No play action for 565") gs.step(aid) gs.step(550) gs.step(553) p = gs.get_player(0) print_gs_state(gs) assert p.deck[0] == 20 and p.deck[1] == 30 assert p.deck[-1] == 10 print("DECK_BOTTOM passed.") print("\nAll remainder modes validated!") if __name__ == "__main__": test_remainder_modes_real()