import pytest import math from unittest.mock import MagicMock from src.classes.battle import ( get_base_strength, _combat_strength_vs, _strength_diff, calc_win_rate, _REALM_BASE_STRENGTH, _STAGE_BONUS_STRENGTH, _SUPPRESSION_POINTS ) from src.classes.cultivation import Realm, Stage from src.classes.technique import TechniqueAttribute # Helper to create a mock avatar def create_mock_avatar(level, realm=None, stage=None, effects=None, technique_attr=None): avatar = MagicMock() # Setup cultivation progress cp = MagicMock() cp.level = level # Setup Realm Enum Mock or Real Enum if realm: cp.realm = realm else: # Fallback to Qi Refinement if not specified cp.realm = Realm.Qi_Refinement if stage: cp.stage = stage else: # Fallback to Early Stage cp.stage = Stage.Early_Stage avatar.cultivation_progress = cp # Setup effects avatar.effects = effects or {} # Setup technique if technique_attr: tech = MagicMock() tech.attribute = technique_attr avatar.technique = tech else: avatar.technique = None return avatar class TestBattleStrength: def test_base_strength_qi_early_min(self): # 练气前期 1级 # Base: 10, Stage: 0 avatar = create_mock_avatar(1, Realm.Qi_Refinement, Stage.Early_Stage) strength = get_base_strength(avatar) expected = 10.0 + 0.0 assert strength == expected def test_base_strength_qi_late_max(self): # 练气后期 30级 # Base: 10, Stage: 5 avatar = create_mock_avatar(30, Realm.Qi_Refinement, Stage.Late_Stage) strength = get_base_strength(avatar) expected = 10.0 + 5.0 assert strength == pytest.approx(expected) def test_base_strength_foundation_early_min(self): # 筑基前期 31级 # Base: 20, Stage: 0 avatar = create_mock_avatar(31, Realm.Foundation_Establishment, Stage.Early_Stage) strength = get_base_strength(avatar) expected = 20.0 + 0.0 assert strength == expected def test_base_strength_nascent_middle(self): # 元婴中期 105级 # Base: 40, Stage: 2.5 avatar = create_mock_avatar(105, Realm.Nascent_Soul, Stage.Middle_Stage) strength = get_base_strength(avatar) expected = 40.0 + 2.5 assert strength == pytest.approx(expected) def test_extra_effects(self): # Test extra strength points from effects avatar = create_mock_avatar(1, Realm.Qi_Refinement, Stage.Early_Stage, effects={"extra_battle_strength_points": 5.0}) strength = get_base_strength(avatar) assert strength == 15.0 class TestCombatMechanics: def test_realm_gap_win_rate(self): # 筑基前期 vs 练气巅峰 # 筑基前期: 20.0 # 练气巅峰: 15.0 (10 + 5) # Diff: 5.0 p1 = create_mock_avatar(31, Realm.Foundation_Establishment, Stage.Early_Stage) p2 = create_mock_avatar(30, Realm.Qi_Refinement, Stage.Late_Stage) # Win rate check # p = 1 / (1 + exp(-0.15 * 5.0)) = 1 / (1 + exp(-0.75)) = 1 / (1 + 0.472) = 1 / 1.472 = 0.679 rate = calc_win_rate(p1, p2) assert rate > 0.67 assert rate < 0.69 def test_massive_gap_win_rate(self): # 元婴 vs 练气 # 元婴: 40+ # 练气: 10+ # Diff > 20 -> should be close to max win rate p1 = create_mock_avatar(91, Realm.Nascent_Soul, Stage.Early_Stage) p2 = create_mock_avatar(1, Realm.Qi_Refinement, Stage.Early_Stage) rate = calc_win_rate(p1, p2) # With cap at 0.99, but actually calculation might be slightly below 0.99 if diff isn't huge enough # Diff = 30, p = 1/(1+exp(-4.5)) = 0.989 assert rate > 0.98 def test_technique_suppression(self): # Test attribute suppression bonus (Metal > Wood) # GOLD suppresses WOOD p1 = create_mock_avatar(10, Realm.Qi_Refinement, Stage.Early_Stage, technique_attr=TechniqueAttribute.GOLD) p2 = create_mock_avatar(10, Realm.Qi_Refinement, Stage.Early_Stage, technique_attr=TechniqueAttribute.WOOD) # Base strengths are equal (same level/realm/stage) # P1 attacks P2: Gold vs Wood -> Bonus s1 = _combat_strength_vs(p2, p1) # P2 attacks P1: Wood vs Gold -> No Bonus s2 = _combat_strength_vs(p1, p2) base = get_base_strength(p1) assert s1 == base + _SUPPRESSION_POINTS assert s2 == base diff = s1 - s2 assert diff == _SUPPRESSION_POINTS def test_intra_stage_diff(self): # Test same stage same strength # Level 1 vs Level 10 (Early Stage) # Diff = 0 p1 = create_mock_avatar(10, Realm.Qi_Refinement, Stage.Early_Stage) p2 = create_mock_avatar(1, Realm.Qi_Refinement, Stage.Early_Stage) diff = _strength_diff(p1, p2) expected_diff = 0.0 assert diff == pytest.approx(expected_diff)