import numpy as np import pytest from engine.game.game_state import GameState, LiveCard, MemberCard class TestPerformanceWildcard: @pytest.fixture(autouse=True) def setup(self): self.game = GameState() # Ensure clean DBs GameState.member_db = {} GameState.live_db = {} self.p0 = self.game.players[0] def test_check_hearts_exact_match(self): """Test specific colors match requirements exactly.""" # Need: 1 Pink (0), 1 Blue (4) need = np.array([1, 0, 0, 0, 1, 0, 0], dtype=np.int32) # Have: 1 Pink, 1 Blue have = np.array([1, 0, 0, 0, 1, 0, 0], dtype=np.int32) assert self.game._check_hearts_meet_requirement(have, need) def test_check_hearts_insufficient_specific(self): """Test failure when specific colors are missing.""" need = np.array([1, 0, 0, 0, 0, 0, 0], dtype=np.int32) have = np.array([0, 0, 0, 0, 0, 0, 0], dtype=np.int32) assert not self.game._check_hearts_meet_requirement(have, need) def test_check_hearts_wildcard_for_specific(self): """Test Wildcard (index 6) filling specific color deficit.""" # Need: 2 Pink need = np.array([2, 0, 0, 0, 0, 0, 0], dtype=np.int32) # Have: 0 Pink, 2 Wildcards have = np.array([0, 0, 0, 0, 0, 0, 2], dtype=np.int32) assert self.game._check_hearts_meet_requirement(have, need) def test_check_hearts_wildcard_partial_specific(self): """Test Wildcard filling partial specific deficit.""" # Need: 3 Pink need = np.array([3, 0, 0, 0, 0, 0, 0], dtype=np.int32) # Have: 1 Pink, 2 Wildcards have = np.array([1, 0, 0, 0, 0, 0, 2], dtype=np.int32) assert self.game._check_hearts_meet_requirement(have, need) def test_check_hearts_any_requirement_surplus(self): """Test Surplus specific colors filling 'Any' requirement.""" # Need: 1 Any (index 6) need = np.array([0, 0, 0, 0, 0, 0, 1], dtype=np.int32) # Have: 1 Pink (Surplus since 0 Pink needed) have = np.array([1, 0, 0, 0, 0, 0, 0], dtype=np.int32) assert self.game._check_hearts_meet_requirement(have, need) def test_check_hearts_any_requirement_wildcard(self): """Test Wildcard filling 'Any' requirement.""" # Need: 1 Any need = np.array([0, 0, 0, 0, 0, 0, 1], dtype=np.int32) # Have: 1 Wildcard have = np.array([0, 0, 0, 0, 0, 0, 1], dtype=np.int32) assert self.game._check_hearts_meet_requirement(have, need) def test_check_hearts_complex_mix(self): """Test simultaneous use of Wildcards for specific deficits and 'Any' requirements.""" # Need: 2 Pink, 1 Any need = np.array([2, 0, 0, 0, 0, 0, 1], dtype=np.int32) # Scenario A: 1 Pink, 2 Wildcards # 1 Wildcard checks Pink deficit (1), Remaining 1 Wildcard checks Any (1) -> Pass have_a = np.array([1, 0, 0, 0, 0, 0, 2], dtype=np.int32) assert self.game._check_hearts_meet_requirement(have_a, need) # Scenario B: 3 Pink, 0 Wildcards # 2 Pink satisfy specific, 1 Pink surplus satisfies Any -> Pass have_b = np.array([3, 0, 0, 0, 0, 0, 0], dtype=np.int32) assert self.game._check_hearts_meet_requirement(have_b, need) # Scenario C: 1 Pink, 1 Wildcard # 1 Pink used. Deficit 1 Pink. 1 Wildcard used. Deficit 0. # Any need 1. Remaining surplus/wildcard 0. -> Fail have_c = np.array([1, 0, 0, 0, 0, 0, 1], dtype=np.int32) assert not self.game._check_hearts_meet_requirement(have_c, need) def test_consume_hearts_logic(self): """Test that _consume_hearts correctly updates the 'have' array in-place.""" # Need: 1 Pink, 1 Any need = np.array([1, 0, 0, 0, 0, 0, 1], dtype=np.int32) # Have: 3 Pink have = np.array([3, 0, 0, 0, 0, 0, 0], dtype=np.int32) # Check first assert self.game._check_hearts_meet_requirement(have, need) # Consume self.game._consume_hearts(have, need) # Expect: 1 Pink consumed for specific, 1 Pink consumed for Any. Total 2 Pink consumed. # Remaining: 1 Pink expected = np.array([1, 0, 0, 0, 0, 0, 0], dtype=np.int32) np.testing.assert_array_equal(have, expected) def test_consume_hearts_wildcard_preference(self): """ Verify consumption order: 1. Specific Need -> Specific Color 2. Specific Need -> Wildcard (if specific missing) 3. Any Need -> Surplus Specific 4. Any Need -> Wildcard """ # Need: 1 Pink, 1 Any need = np.array([1, 0, 0, 0, 0, 0, 1], dtype=np.int32) # Have: 0 Pink, 1 Blue (Surplus), 2 Wildcards # 1 Wildcard needed for Pink. # For Any: Surplus Blue available? Yes. Should use Blue or Wildcard? # Logic says: "First consume surplus specific colors" then "consume Wildcards" have = np.array([0, 0, 0, 0, 1, 0, 2], dtype=np.int32) self.game._consume_hearts(have, need) # Expect: # Pink deficit covered by 1 Wildcard. (Wildcards: 2->1) # Any covered by 1 Blue (Surplus). (Blue: 1->0) # Remaining: 1 Wildcard expected = np.array([0, 0, 0, 0, 0, 0, 1], dtype=np.int32) np.testing.assert_array_equal(have, expected) def test_real_data_flow(self): """ Integration test using real mock objects to simulate a full performance check. Using a Member Card providing hearts and a Live Card with requirements. """ # Create a Mock Member: "Honoka" (Pink) # Provides 1 Pink Heart member = MemberCard( card_id=101, card_no="test", name="Honoka", cost=1, hearts=np.array([1, 0, 0, 0, 0, 0, 0], dtype=np.int32), # 1 Pink blade_hearts=np.array([0, 0, 0, 0, 0, 0, 0], dtype=np.int32), blades=1, ) GameState.member_db[101] = member # Create a Mock Live: "Snow Halation" # Require: 1 Any heart (using index 6 for test even if real card uses strict colors) req = np.zeros(7, dtype=np.int32) req[6] = 1 # 1 Any live = LiveCard(card_id=201, card_no="test-live", name="Test Live", score=10, required_hearts=req) GameState.live_db[201] = live # Setup Player self.p0.stage[0] = 101 # Place Honoka self.p0.live_zone = [201] # Place Live self.p0 = self.game.players[0] # We need to simulate the heart gathering part of _do_performance manually # or mock the method calls? # Actually, let's just reuse the underlying calculation logic which is what we care about. # 1. Gather Hearts total_hearts = np.zeros(7, dtype=np.int32) hraw = self.p0.get_effective_hearts(0, GameState.member_db) # Should be length 7 now [1,0,0,0,0,0,0] total_hearts += hraw # 2. Check Requirement # Have: [1, 0, ...], Need: [0, ... 1 (Any)] success = self.game._check_hearts_meet_requirement(total_hearts, live.required_hearts) assert success, "Pink heart should satisfy 'Any' requirement in integration flow"