# classes.py import random BARD = "Bard" BARBARIAN = "Barbarian" FIGHTER = "Fighter" WIZARD = "Wizard" DRUID = "Druid" CLERIC = "Cleric" WARLOCK = "Warlock" MONK = "Monk" PALADIN = "Paladin" ROGUE = "Rogue" RANGER = "Ranger" SORCERER = "Sorcerer" COMMONER = "Commoner" CLASSES = [BARD, BARBARIAN, FIGHTER, WIZARD, DRUID, CLERIC, WARLOCK, MONK, PALADIN, ROGUE, RANGER, SORCERER] CLASSES_CHANCE = [2, 8, 26, 7, 4, 18, 2, 2, 6, 17, 6, 2] DEFAULT_COMMONER_CHANCE = 0.90 # 90% мира — простолюдины def get_random_class( allowed_classes: list[str] | None = None, commoner_chance: float = DEFAULT_COMMONER_CHANCE, ) -> str: """ Двухуровневый выбор класса. allowed_classes: None или [] → все классы, включая Commoner ["Fighter", "Rogue"] → только боевые из списка, Commoner исключён ["Commoner"] → всегда Commoner ["Fighter", "Commoner"] → Fighter или Commoner commoner_chance: Вероятность выпадения Commoner (если он допустим). Default 0.90. """ all_allowed = not allowed_classes # [] или None → все commoner_allowed = all_allowed or (COMMONER in allowed_classes) combat_pool = ( {cls: chance for cls, chance in zip(CLASSES, CLASSES_CHANCE)} if all_allowed else {cls: chance for cls, chance in zip(CLASSES, CLASSES_CHANCE) if cls in allowed_classes} ) has_combat = bool(combat_pool) has_commoner = commoner_allowed # Крайние случаи if has_commoner and not has_combat: return COMMONER if has_combat and not has_commoner: return _pick_from_combat(combat_pool) # Общий случай: оба варианта допустимы if random.random() < commoner_chance: return COMMONER return _pick_from_combat(combat_pool) def _pick_from_combat(pool: dict[str, float]) -> str: """Взвешенный выбор из пула боевых классов.""" total = sum(pool.values()) r = random.random() * total cumulative = 0.0 for cls, chance in pool.items(): cumulative += chance if r < cumulative: return cls return list(pool.keys())[-1]