# retoor from __future__ import annotations import hashlib import math from dataclasses import dataclass STARTING_COINS = 50 STARTING_PLOTS = 4 MAX_PLOTS = 12 MAX_LEVEL = 20 PLOT_BASE_COST = 100 WATER_BONUS_PCT = 0.08 MAX_WATERS_PER_PLOT = 3 WATER_REWARD_COINS = 6 WATER_REWARD_XP = 3 STEAL_GRACE_SECONDS = 60 STEAL_FRACTION = 0.5 STEAL_COOLDOWN_SECONDS = 3600 GOLDEN_CHANCE = 0.05 GOLDEN_MULTIPLIER = 5 @dataclass(frozen=True) class Crop: key: str name: str icon: str cost: int grow_seconds: int reward_coins: int reward_xp: int min_level: int CROPS: tuple[Crop, ...] = ( Crop("shell", "Shell Script", "\U0001f41a", 5, 30, 11, 2, 1), Crop("python", "Python Script", "\U0001f40d", 15, 120, 36, 5, 1), Crop("webapp", "Web App", "\U0001f4dc", 40, 300, 98, 12, 2), Crop("api", "Go Service", "\U0001f439", 90, 600, 224, 25, 3), Crop("rust", "Rust Engine", "\U0001f980", 200, 1800, 520, 60, 4), Crop("haskell", "Compiler", "λ", 500, 3600, 1380, 150, 6), Crop("kernel", "Kernel", "⚙️", 1200, 7200, 3600, 400, 8), ) CROP_BY_KEY = {crop.key: crop for crop in CROPS} @dataclass(frozen=True) class CiTier: tier: int label: str speed: float upgrade_cost: int CI_TIERS: tuple[CiTier, ...] = ( CiTier(1, "Local Build", 1.0, 0), CiTier(2, "Shared Runner", 1.25, 150), CiTier(3, "Fast Runner", 1.6, 400), CiTier(4, "Parallel Matrix", 2.0, 1000), CiTier(5, "Distributed Cache", 2.5, 2600), ) CI_BY_TIER = {tier.tier: tier for tier in CI_TIERS} MAX_CI_TIER = CI_TIERS[-1].tier def crop_for(key: str) -> Crop | None: return CROP_BY_KEY.get(key) def ci_speed(tier: int) -> float: entry = CI_BY_TIER.get(tier) return entry.speed if entry else 1.0 def next_ci_tier(tier: int) -> CiTier | None: return CI_BY_TIER.get(tier + 1) def farm_speed(ci_tier: int, growth_level: int = 0, legacy_speed_level: int = 0) -> float: return ( ci_speed(ci_tier) * (1 + PERK_BY_KEY["growth"].step * growth_level) * (1 + LEGACY_SPEED_STEP * legacy_speed_level) ) def grow_seconds_for( crop: Crop, ci_tier: int, growth_level: int = 0, legacy_speed_level: int = 0 ) -> int: return max( 1, round(crop.grow_seconds / farm_speed(ci_tier, growth_level, legacy_speed_level)) ) def water_bonus_seconds( crop: Crop, ci_tier: int, growth_level: int = 0, legacy_speed_level: int = 0 ) -> int: return max( 1, round( grow_seconds_for(crop, ci_tier, growth_level, legacy_speed_level) * WATER_BONUS_PCT ), ) def plot_cost(current_plot_count: int) -> int: extra = current_plot_count - STARTING_PLOTS return PLOT_BASE_COST * (2 ** max(0, extra)) def xp_threshold(level: int) -> int: return 50 * (level - 1) * (level - 1) def level_for_xp(xp: int) -> int: level = 1 while level < MAX_LEVEL and xp >= xp_threshold(level + 1): level += 1 return level def level_progress(xp: int) -> dict: level = level_for_xp(xp) floor_xp = xp_threshold(level) if level >= MAX_LEVEL: return { "level": level, "xp": xp, "into_level": xp - floor_xp, "span": 0, "next_level_xp": floor_xp, "is_max": True, } ceil_xp = xp_threshold(level + 1) return { "level": level, "xp": xp, "into_level": xp - floor_xp, "span": ceil_xp - floor_xp, "next_level_xp": ceil_xp, "is_max": False, } SCORE_PRESTIGE = 5000 SCORE_HARVEST = 10 SCORE_COIN_DIVISOR = 20 SCORE_CI = 250 SCORE_PLOT = 200 SCORE_PERK = 120 SCORE_STREAK = 15 SCORE_STREAK_CAP = 30 def farm_score(farm: dict) -> int: def value(key: str, default: int = 0) -> int: raw = farm.get(key) return int(raw) if raw is not None else default perk_total = sum(value(f"perk_{perk.key}") for perk in PERKS) return ( value("xp") + value("prestige") * SCORE_PRESTIGE + value("total_harvests") * SCORE_HARVEST + value("coins") // SCORE_COIN_DIVISOR + (value("ci_tier", 1) - 1) * SCORE_CI + (value("plot_count", STARTING_PLOTS) - STARTING_PLOTS) * SCORE_PLOT + perk_total * SCORE_PERK + min(value("streak"), SCORE_STREAK_CAP) * SCORE_STREAK ) def unlocked_crops(level: int) -> list[Crop]: return [crop for crop in CROPS if crop.min_level <= level] def crop_payload( crop: Crop, ci_tier: int, level: int, growth_level: int = 0, discount_level: int = 0, yield_level: int = 0, prestige: int = 0, legacy_mult_level: int = 0, legacy_speed_level: int = 0, ) -> dict: return { "key": crop.key, "name": crop.name, "icon": crop.icon, "cost": effective_plant_cost(crop, discount_level), "reward_coins": effective_reward_coins( crop, yield_level, prestige, legacy_mult_level ), "reward_xp": crop.reward_xp, "min_level": crop.min_level, "grow_seconds": grow_seconds_for(crop, ci_tier, growth_level, legacy_speed_level), "locked": crop.min_level > level, } @dataclass(frozen=True) class Perk: key: str name: str icon: str description: str max_level: int base_cost: int cost_growth: float step: float PERKS: tuple[Perk, ...] = ( Perk("yield", "Optimizer", "📈", "+5% harvest coins per level", 10, 120, 1.6, 0.05), Perk("growth", "Build Cache", "⚡", "+4% build speed per level", 10, 150, 1.7, 0.04), Perk("discount", "Bulk Licenses", "🏷️", "-3% planting cost per level", 8, 100, 1.7, 0.03), Perk("xp", "Mentorship", "🎓", "+5% harvest XP per level", 10, 140, 1.6, 0.05), ) PERK_BY_KEY = {perk.key: perk for perk in PERKS} PRESTIGE_MIN_LEVEL = 10 PRESTIGE_BONUS = 0.25 STAR_BASE = 1 LEGACY_SPEED_STEP = 0.05 LEGACY_MULT_STEP = 0.10 LEGACY_DEFENSE_GRACE = 30 LEGACY_DEFENSE_FRACTION = 0.05 @dataclass(frozen=True) class LegacyUpgrade: key: str name: str icon: str description: str max_level: int base_cost: int cost_growth: float LEGACY_UPGRADES: tuple[LegacyUpgrade, ...] = ( LegacyUpgrade( "autoharvest", "CI Bot", "🤖", "Auto-collect ready builds when you open your farm", 1, 3, 1.0, ), LegacyUpgrade( "multiplier", "Tech Debt Payoff", "💎", "+10% coins per level, stacks with prestige", 10, 1, 1.6, ), LegacyUpgrade( "speed", "Bare-Metal", "🏎️", "+5% base build speed per level", 8, 1, 1.7 ), LegacyUpgrade( "plots", "Monorepo", "🗂️", "+1 starting plot after refactor per level", 4, 3, 2.0 ), LegacyUpgrade( "defense", "Branch Protection", "🛡️", "+30s steal grace and -5% steal loss per level", 5, 2, 1.8, ), ) LEGACY_BY_KEY = {up.key: up for up in LEGACY_UPGRADES} def legacy_for(key: str) -> LegacyUpgrade | None: return LEGACY_BY_KEY.get(key) def legacy_cost(up: LegacyUpgrade, level: int) -> int: return round(up.base_cost * (up.cost_growth ** level)) def legacy_multiplier(level: int) -> float: return 1 + LEGACY_MULT_STEP * max(0, level) def stars_for_refactor(level: int, prestige: int) -> int: return STAR_BASE + level // 5 + max(0, prestige) def effective_steal_grace(defense_level: int = 0) -> int: return STEAL_GRACE_SECONDS + LEGACY_DEFENSE_GRACE * max(0, defense_level) def effective_steal_fraction(defense_level: int = 0) -> float: return max(0.1, STEAL_FRACTION - LEGACY_DEFENSE_FRACTION * max(0, defense_level)) def prestige_base_plots(legacy_plots_level: int = 0) -> int: return STARTING_PLOTS + max(0, legacy_plots_level) def legacy_value_text(up: LegacyUpgrade, level: int) -> str: if up.key == "autoharvest": return "Active" if level > 0 else "Inactive" if up.key == "multiplier": return f"+{round(LEGACY_MULT_STEP * level * 100)}% coins" if up.key == "speed": return f"+{round(LEGACY_SPEED_STEP * level * 100)}% build speed" if up.key == "plots": return f"+{level} starting plots" grace = LEGACY_DEFENSE_GRACE * level loss = round(LEGACY_DEFENSE_FRACTION * level * 100) return f"+{grace}s grace, -{loss}% steal loss" def is_golden(plot_uid: str, planted_at: str) -> bool: if not plot_uid or not planted_at: return False digest = hashlib.sha256(f"{plot_uid}:{planted_at}".encode()).hexdigest() return (int(digest, 16) % 1000) < round(GOLDEN_CHANCE * 1000) DAILY_BASE = 20 DAILY_STREAK_STEP = 12 DAILY_STREAK_CAP = 7 FERTILIZE_FRACTION = 0.5 FERTILIZE_TAX = 1.05 def perk_for(key: str) -> Perk | None: return PERK_BY_KEY.get(key) def perk_cost(perk: Perk, current_level: int) -> int: return round(perk.base_cost * (perk.cost_growth ** current_level)) def perk_value_text(perk: Perk, level: int) -> str: if perk.key == "discount": return f"-{round(perk.step * level * 100)}% planting cost" if perk.key == "growth": return f"+{round(perk.step * level * 100)}% build speed" if perk.key == "yield": return f"+{round(perk.step * level * 100)}% harvest coins" return f"+{round(perk.step * level * 100)}% harvest XP" def prestige_multiplier(prestige: int) -> float: return 1 + PRESTIGE_BONUS * max(0, prestige) def effective_plant_cost(crop: Crop, discount_level: int = 0) -> int: factor = max(0.0, 1 - PERK_BY_KEY["discount"].step * discount_level) return max(1, round(crop.cost * factor)) def effective_reward_coins( crop: Crop, yield_level: int = 0, prestige: int = 0, legacy_mult_level: int = 0 ) -> int: factor = ( (1 + PERK_BY_KEY["yield"].step * yield_level) * prestige_multiplier(prestige) * legacy_multiplier(legacy_mult_level) ) return round(crop.reward_coins * factor) def effective_reward_xp(crop: Crop, xp_level: int = 0) -> int: return round(crop.reward_xp * (1 + PERK_BY_KEY["xp"].step * xp_level)) def steal_reward_coins( crop: Crop, yield_level: int = 0, prestige: int = 0, legacy_mult_level: int = 0, defense_level: int = 0, ) -> int: fraction = effective_steal_fraction(defense_level) return max( 1, round( effective_reward_coins(crop, yield_level, prestige, legacy_mult_level) * fraction ), ) def daily_reward(streak: int) -> int: effective = min(max(streak, 1), DAILY_STREAK_CAP) return DAILY_BASE + DAILY_STREAK_STEP * (effective - 1) def fertilize_click_cost( effective_reward_coins: int, reduce_seconds: int, full_grow_seconds: int ) -> int: if reduce_seconds < 1 or full_grow_seconds < 1: return 0 return max( 1, math.ceil( effective_reward_coins * reduce_seconds / full_grow_seconds * FERTILIZE_TAX ), ) @dataclass(frozen=True) class QuestDef: kind: str label: str goal_min: int goal_max: int coin: int xp: int QUEST_DEFS: dict[str, QuestDef] = { "plant": QuestDef("plant", "Plant {goal} crops", 3, 6, 8, 2), "harvest": QuestDef("harvest", "Harvest {goal} builds", 3, 5, 14, 4), "water": QuestDef("water", "Water {goal} neighbour builds", 2, 4, 16, 5), "earn": QuestDef("earn", "Earn {goal} coins harvesting", 150, 400, 0, 0), } QUEST_KINDS = tuple(QUEST_DEFS.keys()) DAILY_QUEST_COUNT = 3 def daily_quests(user_uid: str, day: str) -> list[dict]: import hashlib seed = int(hashlib.sha256(f"{user_uid}:{day}".encode()).hexdigest(), 16) start = seed % len(QUEST_KINDS) quests = [] for index in range(DAILY_QUEST_COUNT): kind = QUEST_KINDS[(start + index) % len(QUEST_KINDS)] definition = QUEST_DEFS[kind] span = definition.goal_max - definition.goal_min + 1 goal = definition.goal_min + ((seed >> (index * 7)) % span) if kind == "earn": goal = max(definition.goal_min, (goal // 10) * 10) reward_coins = round(goal * 0.15) reward_xp = max(1, round(goal / 30)) else: reward_coins = goal * definition.coin reward_xp = goal * definition.xp quests.append( { "kind": kind, "label": definition.label.format(goal=goal), "goal": goal, "reward_coins": reward_coins, "reward_xp": reward_xp, } ) return quests