LovecaSim / engine /tests /cards /batches /test_reported_issues.py
trioskosmos's picture
Upload folder using huggingface_hub
bb3fbf9 verified
import numpy as np
from engine.game.game_state import GameState
from engine.models.enums import Group
# Issue IDs
ID_ISSUE_541 = 541 # PL!S-pb1-001-R (Cost 13, Opp Hand > My Hand + 2 -> Add Live from Discard)
ID_ISSUE_269 = 269 # PL!N-bp1-012-R+ (3+ Lives & 1+ Niji -> Heart becomes ALL - wait, it gains 2 ALL hearts)
ID_ISSUE_177 = 177 # PL!HS-bp1-007-P (Cost 2E -> Draw 1)
ID_ISSUE_603 = 603 # PL!SP-bp1-010-R (Cost 1E + 1 Discard -> Look 5 Add 1)
def setup_hand(p, cards):
"""Refills hand and syncs hand_added_turn for validation."""
p.hand = list(cards)
p.hand_added_turn = [0] * len(p.hand)
def setup_live_zone(p, cards):
"""Refills live zone and syncs revealed for validation."""
p.live_zone = list(cards)
p.live_zone_revealed = [True] * len(p.live_zone)
def test_issue_541_condition_check(validated_game_state):
"""
Issue 541: Condition 'Opponent hand > My hand' check.
Card: PL!S-pb1-001-R (Cost 13, Condition: Opponent > Self + 2)
"""
gs = validated_game_state
p1 = gs.players[0]
p2 = gs.players[1]
# Give plenty of energy
p1.energy_zone = [0] * 20 # Use valid ID 0
p1.tapped_energy = np.zeros(100, dtype=bool)
# Setup Hands
# Use real IDs for dummy cards to ensure they are handled by engine logic if needed
member_ids = list(GameState.member_db.keys())
dummies = [member_ids[0], member_ids[1], member_ids[2], member_ids[3]]
# Condition: Opponent >= Self + 2
# Case: Opponent (0) < Self (5). Should FAIL.
setup_hand(p1, [ID_ISSUE_541] + dummies) # 5 cards w/ 541 at Index 0
setup_hand(p2, []) # 0 cards
# Add a live card to discard to be targetable
live_ids = list(GameState.live_db.keys())
p1.discard = [live_ids[0]]
# Debug legal actions
legal = gs.get_legal_actions()
print(f"DEBUG: Legal actions in Phase.MAIN: {np.where(legal)[0]}")
# Action 1: Play Index 0 to Slot 0 (Main Phase ID mapping: 1 + i*3 + slot)
# (0*3 + 0) + 1 = 1
gs = gs.step(1)
p1 = gs.players[0]
# Analysis:
# Cost: 13 energy tapped. Hand -> 4.
# Effect should NOT trigger.
assert len(p1.hand) == 4, f"Hand size is {len(p1.hand)}. Action 1 might have failed if it is 5."
assert live_ids[0] not in p1.hand, "Effect triggered incorrectly! Live card added to hand."
assert np.count_nonzero(p1.tapped_energy[:20]) == 13, "Cost was not paid correctly (Energy Tapping)"
def test_issue_269_constant_effect(validated_game_state):
"""
Issue 269: Constant effect 'If 3+ live cards & 1+ Niji -> Heart becomes ALL (Gains 2 Any)'
"""
gs = validated_game_state
p1 = gs.players[0]
# Find a Nijigasaki Live Card
live_db = GameState.live_db
niji_live_id = next((cid for cid, c in live_db.items() if Group.NIJIGASAKI in c.groups), None)
assert niji_live_id is not None, "Need Niji Live card for test"
# Find dummy lives
dummy_lives = [cid for cid in live_db.keys() if cid != niji_live_id][:2]
# Setup Live Zone (3 cards, 1 Niji) -> Condition Met
setup_live_zone(p1, [niji_live_id] + dummy_lives)
# Setup Stage
p1.stage[0] = ID_ISSUE_269
# Force rule check to apply constants
gs._process_rule_checks()
# Check Hearts
hearts = p1.get_effective_hearts(0, GameState.member_db)
# Index 6 is "Any/All".
# Expected: member's base hearts + 2 from effect
assert hearts[6] >= 2, f"Constant effect failed: Heart total at index 6 is {hearts[6]}. Hearts: {hearts}"
def test_issue_177_activated_draw(validated_game_state):
"""
Issue 177: PL!HS-bp1-007-P Activation (Cost 2E -> Draw 1)
"""
gs = validated_game_state
p1 = gs.players[0]
# Setup Stage
p1.stage[0] = ID_ISSUE_177
p1.energy_zone = [0] * 5
p1.tapped_energy = np.zeros(100, dtype=bool)
member_ids = list(GameState.member_db.keys())
setup_hand(p1, [member_ids[0], member_ids[1]])
initial_hand = len(p1.hand)
# Action 200: Activate Ability Slot 0
gs = gs.step(200)
p1 = gs.players[0]
# Verify results
assert np.count_nonzero(p1.tapped_energy[:5]) == 2, "Cost not paid (Energy not tapped)"
assert len(p1.hand) == initial_hand + 1, "Draw effect failed"
def test_issue_603_ui_payload(validated_game_state):
"""
Issue 603: Activated 'Look 5 add 1'. UI issue (cards not shown).
Verify engine populates looked_cards and handles choice.
"""
gs = validated_game_state
p1 = gs.players[0]
# Setup Stage
p1.stage[0] = ID_ISSUE_603
p1.energy_zone = [0] * 5
p1.tapped_energy = np.zeros(100, dtype=bool)
# Need 1 card in hand for discard cost
member_ids = list(GameState.member_db.keys())
setup_hand(p1, [member_ids[0], member_ids[1]])
# Action 200: Use Ability Slot 0
gs = gs.step(200)
p1 = gs.players[0]
# Cost: 1 Discard. Choice should be pending.
assert gs.pending_choices, "No choice generated for discard cost"
assert gs.pending_choices[0][0] == "TARGET_HAND"
# Action 500: Discard first card in hand (Resolves Target Hand COST)
gs = gs.step(500)
p1 = gs.players[0]
# Now Effect 2 should trigger (Look 5)
assert len(gs.looked_cards) == 5, f"Engine failed to look at 5 cards, got {len(gs.looked_cards)}"
# Verify follow-up choice
assert gs.pending_choices, "No choice generated for Look & Choose"
assert gs.pending_choices[0][0] == "SELECT_FROM_LIST", f"Unexpected choice {gs.pending_choices[0][0]}"
# Action 600: Select first looked card
gs = gs.step(600)
assert len(gs.players[0].hand) == 2, f"Expected 2 cards (2 - 1 cost + 1 drawn), got {len(gs.players[0].hand)}"