LovecaSim / engine /tests /scenarios /test_new_patterns.py
trioskosmos's picture
Upload folder using huggingface_hub
bb3fbf9 verified
import numpy as np
import pytest
from engine.game.game_state import GameState, Phase
from engine.models.ability import Effect, EffectType, TargetType
from engine.models.card import LiveCard, MemberCard
@pytest.fixture
def base_state():
state = GameState()
state.phase = Phase.MAIN
state.current_player = 0
state.players[0].energy_zone = [1000, 1001, 1002, 1003, 1004] # 5 energy
state.players[0].hand = [1, 2, 3]
# Mock some basic cards
m1 = MemberCard(
card_id=1,
card_no="M1",
name="Member 1",
cost=2,
blades=2,
hearts=np.array([2, 0, 0, 0, 0, 0, 0]),
blade_hearts=np.zeros(7),
)
m2 = MemberCard(
card_id=2,
card_no="M2",
name="Member 2",
cost=2,
blades=2,
hearts=np.array([0, 2, 0, 0, 0, 0, 0]),
blade_hearts=np.zeros(7),
)
m3 = MemberCard(
card_id=3,
card_no="M3",
name="Member 3",
cost=2,
blades=2,
hearts=np.array([0, 0, 2, 0, 0, 0, 0]),
blade_hearts=np.zeros(7),
)
state.member_db[1] = m1
state.member_db[2] = m2
state.member_db[3] = m3
return state
def test_baton_touch_limit_extension():
state = GameState()
state.phase = Phase.MAIN
state.players[0].energy_zone = [1000] * 20 # Lots of energy
state.member_db[1] = MemberCard(
card_id=1, card_no="M1", name="M1", cost=1, blades=1, hearts=[0] * 7, blade_hearts=[0] * 7
)
state.member_db[2] = MemberCard(
card_id=2, card_no="M2", name="M2", cost=1, blades=1, hearts=[0] * 7, blade_hearts=[0] * 7
)
state.member_db[3] = MemberCard(
card_id=3, card_no="M3", name="M3", cost=1, blades=1, hearts=[0] * 7, blade_hearts=[0] * 7
)
state.member_db[4] = MemberCard(
card_id=4, card_no="M4", name="M4", cost=1, blades=1, hearts=[0] * 7, blade_hearts=[0] * 7
)
state.member_db[5] = MemberCard(
card_id=5, card_no="M5", name="M5", cost=1, blades=1, hearts=[0] * 7, blade_hearts=[0] * 7
)
p = state.players[0]
p.hand = [1, 2, 3, 4]
# 1. Fill stage without baton touches
p.stage[0] = 1
p.stage[1] = 2
p.stage[2] = 3
p.hand = [4, 5]
# Now slots are [1, 2, 3]. Baton count = 0, Limit = 1.
# 2. Perform 1st baton touch on Slot 0
# Hand is [4, 5]. Action for Hand 0 to Slot 0 is 1 + 0*3 + 0 = 1.
legal = state.get_legal_actions()
assert legal[1], "Baton touch 1 (Hand 0 to Slot 0) should be legal"
state = state.step(1)
p = state.players[0] # Update p to the new state's player
assert p.baton_touch_count == 1
assert p.stage[0] == 4
# 3. Try to perform 2nd baton touch (Should be illegal by default limit=1)
# Hand is [5]. Action for Hand 0 to Slot 1 is 1 + 0*3 + 1 = 2.
legal = state.get_legal_actions()
assert not legal[2], "Baton touch 2 (Hand 0 to Slot 1) should be illegal (limit 1 reached)"
# 4. Apply Modifier (+1 baton touch)
p.continuous_effects.append(
{"effect": Effect(EffectType.BATON_TOUCH_MOD, 1, TargetType.SELF), "expiry": "TURN_END"}
)
legal = state.get_legal_actions()
assert legal[2], "Baton touch 2 should be legal after BATON_TOUCH_MOD"
def test_heart_requirement_reduction(base_state):
p = base_state.players[0]
# Card 1 is in Slot 0 (2 Pink hearts)
p.stage[0] = 1
p.hand = [2, 3]
# Mock Live Card: Needs 3 Pink hearts
l1 = LiveCard(card_id=101, card_no="L1", name="Live 1", score=2, required_hearts=np.array([3, 0, 0, 0, 0, 0, 0]))
base_state.live_db[101] = l1
p.live_zone = [101]
# 1. Normal Performance: Should fail (2 < 3)
guide = p.get_performance_guide(base_state.live_db, base_state.member_db)
assert guide["lives"][0]["passed"] == False, "Should fail without reduction"
# 2. Add REDUCE_HEART_REQ (-1 Pink)
p.continuous_effects.append(
{"effect": Effect(EffectType.REDUCE_HEART_REQ, 1, TargetType.SELF, {"color": 1}), "expiry": "LIVE_END"}
)
# 3. Now should pass (2 >= 3-1)
guide = p.get_performance_guide(base_state.live_db, base_state.member_db)
assert guide["lives"][0]["passed"] == True, (
f"Should pass with reduction. Req: {guide['lives'][0]['req']}, Have: {guide['total_hearts']}"
)
def test_color_transform(base_state):
p = base_state.players[0]
# Card 2 has 2 Red hearts. Place in Slot 0.
p.stage[0] = 2
# Mock Live Card: Needs 2 Pink hearts
l1 = LiveCard(
card_id=101, card_no="L101", name="Live 101", score=2, required_hearts=np.array([2, 0, 0, 0, 0, 0, 0])
)
base_state.live_db[101] = l1
p.live_zone = [101]
# 1. Normal: Fails (Needs pink, has red)
guide = p.get_performance_guide(base_state.live_db, base_state.member_db)
assert guide["lives"][0]["passed"] == False, "Should fail (Needs Pink, has Red)"
# 2. Add TRANSFORM_COLOR (Red -> Pink)
p.continuous_effects.append(
{
"effect": Effect(EffectType.TRANSFORM_COLOR, 0, TargetType.SELF, {"from_color": 2, "to_color": 1}),
"expiry": "LIVE_END",
}
)
# 3. Now should pass
guide = p.get_performance_guide(base_state.live_db, base_state.member_db)
assert guide["total_hearts"][0] == 2, f"Should have 2 Pink hearts. Got {guide['total_hearts']}"
assert guide["total_hearts"][1] == 0, f"Should have 0 Red hearts. Got {guide['total_hearts']}"
assert guide["lives"][0]["passed"] == True, "Should pass after transform"