LovecaSim / engine /tests /logic /test_rules.py
trioskosmos's picture
Upload folder using huggingface_hub
bb3fbf9 verified
import numpy as np
import pytest
from engine.game.enums import Phase
from engine.game.game_state import GameState
from engine.models.ability import Ability, Effect, EffectType, TriggerType
from engine.models.card import LiveCard, MemberCard
@pytest.fixture
def game_state():
state = GameState()
# Mock DB - Reset class variables
GameState.member_db = {}
GameState.live_db = {}
return state
def test_baton_touch_cost(game_state):
"""Verify Baton Touch reduces cost correctly"""
state = game_state
# Card 1: Cost 2 (on stage)
# card_id, card_no, name, cost, hearts, blade_hearts, blades
c1 = MemberCard(1, "NO-01", "Fan1", 2, np.zeros(6), np.zeros(7), 1)
# Card 2: Cost 5 (in hand)
c2 = MemberCard(2, "NO-02", "Fan2", 5, np.zeros(6), np.zeros(7), 1)
GameState.member_db = {1: c1, 2: c2}
p = state.players[0]
p.stage[0] = 1 # Card 1 on Left
p.hand = [2] # Card 2 in hand
p.hand_added_turn = [0] * len(p.hand)
# Energy: 3 available (enough for 5-2=3, but not 5)
p.energy_zone = [100, 101, 102]
p.tapped_energy[:] = False
p.main_deck = [999] # Validation: Prevent auto-refresh from discard
state.phase = Phase.MAIN
state.current_player = 0
# Action: Play Card 2 (idx 0) on Area 0 (Left)
# Action encoding: 1 + hand_idx*3 + area = 1 + 0 + 0 = 1
# Check if legal (should be, cost 5-2=3 <= 3 energy)
legal = state.get_legal_actions()
assert legal[1], "Baton touch play should be legal"
# Execute
new_state = state.step(1)
np0 = new_state.players[0]
# Verify result
assert np0.stage[0] == 2, "Card 2 should be on stage"
assert 1 in np0.discard, "Card 1 should be in discard"
assert np0.count_untapped_energy() == 0, "Should use all 3 energy (5-2=3)"
def test_live_heart_requirements(game_state):
"""Verify Live card heart checking logic"""
state = game_state
# Req: 1 Pink, 1 Red, 1 Any
req = np.zeros(7, dtype=np.int32)
req[0] = 1 # Pink
req[1] = 1 # Red
req[6] = 1 # Any
live = LiveCard(100, "L-01", "Song", 1, req)
GameState.live_db = {100: live}
p = state.players[0]
p.live_zone = [100]
# Case 1: Exact match (Pink, Red, Blue for Any)
hearts1 = np.zeros(6, dtype=np.int32)
hearts1[0] = 1
hearts1[1] = 1
hearts1[4] = 1 # Blue
assert state._check_hearts_meet_requirement(hearts1, req), "Should match with exact + blue"
# Case 2: Insufficient Color (2 Pink, 0 Red, 1 Blue) -> Fail Red
hearts2 = np.zeros(6, dtype=np.int32)
hearts2[0] = 2
hearts2[4] = 1
assert not state._check_hearts_meet_requirement(hearts2, req), "Should fail missing red"
# Case 3: Insufficient Any (1 Pink, 1 Red) -> Fail Any
hearts3 = np.zeros(6, dtype=np.int32)
hearts3[0] = 1
hearts3[1] = 1
assert not state._check_hearts_meet_requirement(hearts3, req), "Should fail missing any"
def test_trigger_stack(game_state):
"""Verify On Play triggers add to stack"""
state = game_state
# Card with On Play logic
ability = Ability("Test", TriggerType.ON_PLAY, [Effect(EffectType.DRAW, 1)])
c1 = MemberCard(1, "NO-01", "Fan1", 1, np.zeros(6), np.zeros(7), 1, abilities=[ability])
GameState.member_db = {1: c1}
p = state.players[0]
p.hand = [1]
p.hand_added_turn = [0] * len(p.hand)
p.energy_zone = [100]
state.phase = Phase.MAIN
state.current_player = 0
# Play Card 1
# Action: 1 (Hand idx 0, Area 0)
new_state = state.step(1)
# Check trigger was processed
# Note: step() auto-resolves triggers if no choice required.
# Since DRAW 1 is auto-resolved, pending_effects should be empty.
assert len(new_state.pending_effects) == 0, "Effect should auto-resolve"
# Should have drawn a card (if deck had cards, which it doesn't by default here)
# But main point is verifying the trigger mechanism doesn't crash or hang