diff --git a/src/sim/new_avatar.py b/src/sim/new_avatar.py index 1e5ad07..caaeb5d 100644 --- a/src/sim/new_avatar.py +++ b/src/sim/new_avatar.py @@ -507,6 +507,15 @@ class AvatarFactory: if levels[a] < levels[b] + MASTER_LEVEL_MIN_DIFF: levels[a] = min(LEVEL_MAX, levels[b] + MASTER_LEVEL_MIN_DIFF + random.randint(0, MASTER_LEVEL_EXTRA_MAX)) + # 确保年龄不超过境界寿命上限,避免角色一出生就老死 + # 放在所有关系调整之后,因为关系调整可能会修改年龄和等级 + for i in range(n): + realm = CultivationProgress(levels[i]).realm + max_lifespan = Age.REALM_LIFESPAN.get(realm, 100) + if ages[i] >= max_lifespan: + # 将年龄限制为寿命上限的 80%-95%,保留一定的生存空间 + ages[i] = int(max_lifespan * random.uniform(0.8, 0.95)) + avatars_by_index: list[Avatar] = [None] * n # type: ignore avatars_by_id: dict[str, Avatar] = {} diff --git a/tests/test_new_avatar.py b/tests/test_new_avatar.py new file mode 100644 index 0000000..ed8d2b5 --- /dev/null +++ b/tests/test_new_avatar.py @@ -0,0 +1,84 @@ +"""测试 new_avatar 模块的角色创建逻辑.""" +import pytest +from src.sim.new_avatar import make_avatars, AvatarFactory, PopulationPlanner +from src.classes.age import Age +from src.classes.cultivation import CultivationProgress + + +class TestAgeLifespanConstraint: + """测试角色创建时年龄不超过寿命上限的约束.""" + + def test_batch_creation_age_within_lifespan(self, base_world): + """批量创建角色时,年龄应不超过境界寿命上限. + + 注意:只有当随机生成的年龄 >= 寿命上限时才会触发调整, + 调整后的年龄会在 80%-95% 区间内。正常随机生成的年龄 + (如 77 岁 / 80 寿命)不会被调整,所以可能接近但不超过上限。 + """ + # 创建大量角色以增加覆盖率 + avatars = make_avatars(base_world, count=100) + + for avatar in avatars.values(): + max_lifespan = Age.REALM_LIFESPAN.get( + avatar.cultivation_progress.realm, 100 + ) + assert avatar.age.age < max_lifespan, ( + f"角色 {avatar.name} 年龄 {avatar.age.age} 超过了" + f"境界 {avatar.cultivation_progress.realm} 的寿命上限 {max_lifespan}" + ) + + def test_batch_creation_no_immediate_death(self, base_world): + """批量创建的角色不应该一出生就处于老死状态.""" + avatars = make_avatars(base_world, count=100) + + for avatar in avatars.values(): + # 不应该有老死概率 + death_prob = avatar.age.get_death_probability() + assert death_prob == 0.0, ( + f"角色 {avatar.name} 年龄 {avatar.age.age}/" + f"{avatar.age.max_lifespan} 有老死概率 {death_prob}" + ) + + def test_multiple_batch_creations_consistent(self, base_world): + """多次批量创建应该都满足年龄约束.""" + for _ in range(5): + avatars = make_avatars(base_world, count=50) + for avatar in avatars.values(): + max_lifespan = Age.REALM_LIFESPAN.get( + avatar.cultivation_progress.realm, 100 + ) + assert avatar.age.age < max_lifespan + + +class TestRealmLifespanMapping: + """测试各境界的寿命上限映射.""" + + def test_qi_refinement_lifespan(self, base_world): + """练气期角色年龄应不超过80岁.""" + from src.classes.cultivation import Realm + + avatars = make_avatars(base_world, count=100) + qi_refinement_avatars = [ + av for av in avatars.values() + if av.cultivation_progress.realm == Realm.Qi_Refinement + ] + + for avatar in qi_refinement_avatars: + assert avatar.age.age < 80, ( + f"练气期角色 {avatar.name} 年龄 {avatar.age.age} 超过 80" + ) + + def test_foundation_establishment_lifespan(self, base_world): + """筑基期角色年龄应不超过120岁.""" + from src.classes.cultivation import Realm + + avatars = make_avatars(base_world, count=100) + fe_avatars = [ + av for av in avatars.values() + if av.cultivation_progress.realm == Realm.Foundation_Establishment + ] + + for avatar in fe_avatars: + assert avatar.age.age < 120, ( + f"筑基期角色 {avatar.name} 年龄 {avatar.age.age} 超过 120" + )