LovecaSim / engine /tests /logic /test_order_deck_fix.py
trioskosmos's picture
Upload folder using huggingface_hub
bb3fbf9 verified
import os
import sys
# Add project root to path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../")))
import os
from engine.game.data_loader import CardDataLoader
from engine.game.enums import Phase
from engine.game.game_state import GameState
# Mock DB loader if needed, or use real one
def get_test_db():
base_path = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.abspath(os.path.join(base_path, "../../../"))
cards_path = os.path.join(project_root, "data/cards.json")
loader = CardDataLoader(cards_path)
return loader.load()
def test_order_deck_logic():
m, l, e = get_test_db()
# Initialize DB for GameState
GameState.initialize_class_db(m, l)
GameState.energy_db = e
GameState._init_jit_arrays()
gs = GameState()
p0 = gs.players[0]
# Find PL!N-bp1-002-P (Nakasuv Kasumi)
# Trigger: ON_PLAY -> LOOK_AND_CHOOSE_ORDER(3) -> DECK_TOP
target_card_no = "PL!N-bp1-002-P"
target_card_id = -1
for cid, card in m.items():
if card.card_no == target_card_no:
target_card_id = int(cid)
break
assert target_card_id != -1, f"Card {target_card_no} not found in DB"
# Isolate card in hand, clear deck to controllable state
p0.hand = [target_card_id]
p0.energy_zone = [20000, 20000] # Enough energy
# Fix: Ensure tapped_energy is numpy array
import numpy as np
p0.tapped_energy = np.zeros(100, dtype=bool)
p0.tapped_energy[0] = False
p0.tapped_energy[1] = False
# Setup Deck: [C, B, A] -> Top is A
# We want known cards to verify ordering
# Let's use some dummy IDs if possible or just other members
deck_cards = [1001, 1002, 1003, 1004, 1005] # Assumptions
p0.deck = deck_cards[:]
# Setup Phase
gs.phase = Phase.MAIN
gs.current_player = 0
gs.turn = 1
print("\n--- Playing Card ---\n")
# Play to Center (Slot 1). Action ID: 1 + 0*3 + 1 = 2
gs.step(2)
print("DEBUG RULE LOG:", gs.rule_log)
# 1. Verify we paused for ORDER_DECK
assert gs.phase == Phase.RESPONSE, f"Expected Response phase, got {gs.phase}"
assert gs.pending_choice_type == "ORDER_DECK", f"Expected ORDER_DECK choice, got {gs.pending_choice_type}"
# 2. Verify looked_cards populated
looked = p0.looked_cards
print(f"Looked Cards: {looked}")
assert len(looked) == 3, f"Expected 3 looked cards, got {len(looked)}"
# Should be the popped cards from deck. Since deck pops from end:
# Deck was [..., 1003, 1004, 1005]
# Pop 1: 1005, Pop 2: 1004, Pop 3: 1003
# Looked should be [1005, 1004, 1003] (or visible order)
# 3. Check Legal Actions
# Should offer choices 0, 1, 2 (corresponding to the 3 cards)
# Action ID range: 550 + area_idx*100 + ab_idx*10 + c
# area_idx = 1 (Center)
# ab_idx for OnPlay usually -1 -> mapped to 0 safely in our new logic?
# Let's check get_legal_actions output mask
legal_mask = gs.get_legal_actions()
legal_indices = [i for i, v in enumerate(legal_mask) if v]
print(f"Legal Actions: {legal_indices}")
# Calculate Expected IDs
# area=1, ab=0 (safe_ab), c=0..2
# 550 + 100 + 0 + 0 = 650
# 550 + 100 + 0 + 1 = 651
# 550 + 100 + 0 + 2 = 652
expected_ids = [650, 651, 652]
for eid in expected_ids:
assert eid in legal_indices, f"Expected Action ID {eid} not found in {legal_indices}"
print("\n--- Selecting First Card to Top ---\n")
# Choose Index 1 (Card 1004) to put on TOP
gs.step(651)
# 4. Verify Card Moved
# Deck should now have 1004 on top (end of list)
assert p0.deck[-1] == looked[1], "Chosen card not on top of deck"
# 5. Verify Loop (Still in Response, fewer cards)
assert gs.phase == Phase.RESPONSE, "Should still be in Response phase for next card"
assert len(p0.looked_cards) == 2, "Should have 2 cards left in looked_cards"
print("\n--- Discarding Rest ---\n")
# If we want to test "Done"/Discard logic, we need to know what ID triggers that.
# In my implementation plan, I said "Invalid choice implies discard rest".
# But get_legal_actions only gives valid choices [0, 1].
# Wait, the logic handles "Invalid choice" but UI prevents it usually.
# The logic says: `if choice >= 0 && choice < looked_len: ... else: discard all`
# So we need to send an action that maps to a choice >= looked_len?
# Or maybe we rely on the fact that we can just pick all cards one by one.
# Let's pick another card. Index 0 of REMAINING list.
# Remaining: [1005, 1003] (Original 0 and 2)
# We pick 1005 (Current Index 0)
gs.step(650) # Same ID 650 is valid for index 0
assert p0.deck[-1] == looked[0], "Next chosen card not on top"
assert len(p0.looked_cards) == 1
# Pick last card
gs.step(650)
assert p0.deck[-1] == looked[2], "Last card not on top"
assert len(p0.looked_cards) == 0
# 6. Verify Phase Transition
assert gs.phase == Phase.MAIN, "Should return to Main phase after cards depleted"
print("Test Passed: O_ORDER_DECK logic verified.")
if __name__ == "__main__":
test_order_deck_logic()