Spaces:
Running
Running
| import numpy as np | |
| import pytest | |
| from engine.game.enums import Phase | |
| from engine.game.game_state import GameState, LiveCard, MemberCard | |
| from engine.models.ability import Effect, EffectType | |
| class TestPerformanceLogs: | |
| def setup(self): | |
| self.game = GameState(verbose=True) | |
| self.p0 = self.game.players[0] | |
| # Pink:0, Red:1, Yellow:2, Green:3, Blue:4, Purple:5, Any:6 | |
| self.game.member_db[1] = MemberCard( | |
| card_id=1, | |
| card_no="M1", | |
| name="Member 1", | |
| cost=1, | |
| hearts=np.array([1, 0, 0, 0, 0, 0, 0]), | |
| blade_hearts=np.array([0, 0, 0, 0, 0, 0, 0]), | |
| blades=1, | |
| abilities=[], | |
| img_path="m1.png", | |
| ) | |
| # Red Member | |
| self.game.member_db[2] = MemberCard( | |
| card_id=2, | |
| card_no="M2", | |
| name="Member 2", | |
| cost=1, | |
| hearts=np.array([0, 1, 0, 0, 0, 0, 0]), | |
| blade_hearts=np.array([0, 0, 0, 0, 0, 0, 0]), | |
| blades=1, | |
| abilities=[], | |
| img_path="m2.png", | |
| ) | |
| self.game.live_db[100] = LiveCard( | |
| card_id=100, | |
| card_no="L1", | |
| name="Live 1", | |
| score=1, | |
| required_hearts=np.array([1, 1, 0, 0, 0, 0, 0]), | |
| abilities=[], | |
| img_path="l1.png", | |
| ) | |
| def test_performance_requirement_reduction_log(self): | |
| """Verify that heart requirement reductions are logged in performance results.""" | |
| self.p0.stage[0] = 1 # Member 1 (Pink) | |
| # We need 1 Pink and 1 Red. We have 1 Pink. | |
| # Add a continuous effect that reduces Red requirement by 1. | |
| self.p0.continuous_effects.append({ | |
| "source_name": "Reduction Effect", | |
| "effect": Effect( | |
| effect_type=EffectType.REDUCE_HEART_REQ, | |
| value=1, | |
| params={"color": 2} # Red is 2 in 1-6 mapping | |
| ) | |
| }) | |
| self.p0.live_zone = [100] | |
| self.game.phase = Phase.PERFORMANCE_P1 | |
| self.game.current_player = 0 | |
| self.game._do_performance(0) | |
| res = self.game.performance_results[0] | |
| assert res["success"] is True | |
| # Check breakdown | |
| assert "requirements" in res["breakdown"] | |
| req_logs = res["breakdown"]["requirements"] | |
| assert len(req_logs) > 0 | |
| # One of them should be our reduction | |
| reduction_found = False | |
| for log in req_logs: | |
| if log["source"] == "Reduction Effect": | |
| reduction_found = True | |
| assert log["type"] == "req_mod" | |
| # Value should be negative vector: Pink:0, Red:-1 | |
| assert log["value"][1] == -1 | |
| assert reduction_found | |
| def test_performance_transform_color_log(self): | |
| """Verify that color transforms are logged in performance results.""" | |
| self.p0.stage[0] = 1 # Member 1 (Pink) | |
| # We need 1 Red. We have 1 Pink. | |
| self.game.live_db[101] = LiveCard( | |
| card_id=101, | |
| card_no="L2", | |
| name="Live 2", | |
| score=1, | |
| required_hearts=np.array([0, 1, 0, 0, 0, 0, 0]), | |
| abilities=[], | |
| img_path="l2.png", | |
| ) | |
| self.p0.live_zone = [101] | |
| # Add a transform effect: Pink (1) -> Red (2) | |
| self.p0.continuous_effects.append({ | |
| "source_name": "Transform Effect", | |
| "effect": Effect( | |
| effect_type=EffectType.TRANSFORM_COLOR, | |
| value=0, | |
| params={"from_color": 1, "to_color": 2} | |
| ) | |
| }) | |
| self.game.phase = Phase.PERFORMANCE_P1 | |
| self.game.current_player = 0 | |
| self.game._do_performance(0) | |
| res = self.game.performance_results[0] | |
| assert res["success"] is True | |
| # Check breakdown | |
| assert "transforms" in res["breakdown"] | |
| trans_logs = res["breakdown"]["transforms"] | |
| assert len(trans_logs) > 0 | |
| # Check for transform log | |
| transform_found = False | |
| for log in trans_logs: | |
| if log["source"] == "Transform Effect": | |
| transform_found = True | |
| assert log["type"] == "transform" | |
| assert "Transform 1 Yells to 2" in log["desc"] | |
| assert transform_found | |
| # Also check heart_breakdown (it should still have it for legacy UI too) | |
| heart_found = False | |
| for log in res["breakdown"]["hearts"]: | |
| if log.get("source") == "Transform Effect" and log.get("type") == "transform": | |
| heart_found = True | |
| assert heart_found | |
| def test_performance_score_modifier_log(self): | |
| """Verify that ability score modifiers are logged in performance results.""" | |
| self.p0.stage[0] = 1 # Member 1 (Pink) | |
| self.p0.live_zone = [100] | |
| # Add a score modifier effect | |
| from engine.models.ability import Effect, EffectType | |
| self.p0.continuous_effects.append({ | |
| "source_name": "Score Booster", | |
| "source_id": 999, | |
| "effect": Effect( | |
| effect_type=EffectType.MODIFY_SCORE_RULE, | |
| value=5, | |
| params={} | |
| ) | |
| }) | |
| self.game.phase = Phase.PERFORMANCE_P1 | |
| self.game.current_player = 0 | |
| self.game._do_performance(0) # This populates results | |
| # _do_performance calls _advance_performance -> _do_live_result | |
| # But _do_performance itself doesn't call _do_live_result if it returns to process triggers. | |
| # However, PhaseMixin calls _do_live_result at the end of the performance cycle. | |
| # In this simple test, we need to ensure _do_live_result is called to populate the modifiers. | |
| self.game._do_live_result() | |
| # performance_results is cleared at the end of _do_live_result, but copied to last_performance_results | |
| assert 0 in self.game.last_performance_results | |
| res = self.game.last_performance_results[0] | |
| assert "score_modifiers" in res["breakdown"] | |
| mods = res["breakdown"]["score_modifiers"] | |
| assert len(mods) > 0 | |
| found_mod = False | |
| for m in mods: | |
| if m["source"] == "Score Booster": | |
| found_mod = True | |
| assert m["value"] == 5 | |
| assert m["source_id"] == 999 | |
| assert found_mod | |