Files

65 lines
2.9 KiB
Python

from __future__ import annotations
import random
from collections import Counter
from .plants import POOL, plant_rarity
from .types import ALL_TYPES
PACK_TYPES = ("lesser", "tower", "general") + ALL_TYPES # lesser, tower, general + one per type
REWARD_PACK_TYPES = ("general",) + ALL_TYPES # good packs only (win rewards)
RARITY_WEIGHTS = {"common": 50, "rare": 35, "super_rare": 5, "super_super_rare": 2, "ultra_rare": 1}
LESSER_RARITY_WEIGHTS = {"common": 70, "rare": 25, "super_rare": 2, "super_super_rare": 0, "ultra_rare": 0}
TOWER_RARITY_WEIGHTS = {"common": 2, "rare": 15, "super_rare": 25, "super_super_rare": 15, "ultra_rare": 5}
def _weighted(names, weights_map=None) -> str:
weights_map = weights_map or RARITY_WEIGHTS
weights = [weights_map.get(plant_rarity(n), 20) for n in names]
return random.choices(names, weights=weights, k=1)[0]
def open_pack(pack_type: str) -> str:
"""Return a random plant name for the given pack type."""
if pack_type == "lesser":
return _weighted(list(POOL.keys()), LESSER_RARITY_WEIGHTS)
if pack_type == "tower":
return _weighted(list(POOL.keys()), TOWER_RARITY_WEIGHTS)
if pack_type == "general":
return _weighted(list(POOL.keys()))
if pack_type in ALL_TYPES:
# type pack: roll the rarity first (with the general ladder), then pick a
# plant of that rarity from the type; fall back to the whole pool only if
# that rarity isn't present in the type.
dist = _rarity_odds(list(POOL.keys()), RARITY_WEIGHTS)
rarity = random.choices(list(dist.keys()),
weights=[dist[r] for r in dist], k=1)[0]
candidates = [n for n in POOL
if POOL[n]["type"] == pack_type and plant_rarity(n) == rarity]
if not candidates:
candidates = [n for n in POOL if plant_rarity(n) == rarity]
return random.choice(candidates)
return _weighted(list(POOL.keys()))
def _rarity_odds(pool, weights_map) -> dict:
"""Chance (0..1) of each rarity when rolling from `pool` with `weights_map`."""
counts = Counter(plant_rarity(n) for n in pool)
total = sum(counts.get(r, 0) * w for r, w in weights_map.items())
if total <= 0:
return {r: 0.0 for r in weights_map}
return {r: counts.get(r, 0) * weights_map[r] / total for r in weights_map}
def pack_odds(pack_type: str) -> dict:
"""Chance (0..1) of each rarity for the given pack type."""
whole = list(POOL.keys())
if pack_type == "lesser":
return _rarity_odds(whole, LESSER_RARITY_WEIGHTS)
if pack_type == "tower":
return _rarity_odds(whole, TOWER_RARITY_WEIGHTS)
if pack_type == "general":
return _rarity_odds(whole, RARITY_WEIGHTS)
# type packs roll the rarity first with the general ladder, so their rarity
# odds match the general pack (the plant then comes from the type when possible).
return _rarity_odds(whole, RARITY_WEIGHTS)