Spaces:
Running
Running
| import numpy as np | |
| import pytest | |
| from engine.game.game_state import GameState, LiveCard, MemberCard | |
| class TestPerformanceWildcard: | |
| 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" | |