| |
| |
| |
| from __future__ import annotations |
|
|
| import math |
| import random |
| from typing import Any |
|
|
| from game_config import ( |
| BASE_REWARD, |
| BASE_TRAIN_COST, |
| CHAT_TEMP_MAX, |
| CHAT_TEMP_MIN, |
| CLOUD_BASE_REVENUE, |
| CLOUD_QUALITY_MULT, |
| CLOUD_UNLOCK_COST, |
| MAX_QUALITY_REWARD_MULT, |
| QUALITY_EXPONENT, |
| REWARD_NOISE_PCT, |
| STARTING_MONEY, |
| TRAIN_COST_QUALITY_SCALE, |
| UPGRADES, |
| ) |
|
|
| |
|
|
|
|
| def _max_quality() -> float: |
| return sum(upg["levels"][-1]["quality_score"] for upg in UPGRADES.values()) |
|
|
|
|
| def _quality_ratio(state: dict) -> float: |
| total = sum( |
| UPGRADES[k]["levels"][state["upgrades"][k]]["quality_score"] for k in UPGRADES |
| ) |
| mx = _max_quality() |
| return total / mx if mx > 0 else 0.0 |
|
|
|
|
| def _total_quality_score(state: dict) -> int: |
| return sum( |
| UPGRADES[k]["levels"][state["upgrades"][k]]["quality_score"] for k in UPGRADES |
| ) |
|
|
|
|
| |
|
|
|
|
| def new_game() -> dict: |
| return { |
| "money": STARTING_MONEY, |
| "upgrades": {k: 0 for k in UPGRADES}, |
| "trained": False, |
| "cloud_unlocked": False, |
| "cloud_active": False, |
| "runs": 0, |
| "total_earned": 0.0, |
| "log": [], |
| } |
|
|
|
|
| |
|
|
|
|
| def buy_upgrade(state: dict, upgrade_key: str) -> tuple[dict, str]: |
| """Buy the next tier of an upgrade. Returns (new_state, message).""" |
| state = _copy(state) |
| upg = UPGRADES[upgrade_key] |
| current = state["upgrades"][upgrade_key] |
| max_tier = len(upg["levels"]) - 1 |
|
|
| if current >= max_tier: |
| return state, f"[WARN] {upg['label']} is already maxed." |
|
|
| next_tier = current + 1 |
| cost = upg["levels"][next_tier]["cost"] |
|
|
| if state["money"] < cost: |
| return ( |
| state, |
| f"[FAIL] Not enough funds. Need ${cost:.0f}, have ${state['money']:.2f}.", |
| ) |
|
|
| state["money"] -= cost |
| state["upgrades"][upgrade_key] = next_tier |
| tier_name = upg["levels"][next_tier]["name"] |
| msg = f"[OK] {upg['label']} β {tier_name} (β${cost:.0f})" |
| state["log"].append(msg) |
| return state, msg |
|
|
|
|
| def train_model(state: dict) -> tuple[dict, str]: |
| """Run one training pass. Returns (new_state, multiline log).""" |
| state = _copy(state) |
| qr = _quality_ratio(state) |
| qs = _total_quality_score(state) |
|
|
| |
| cost = BASE_TRAIN_COST + TRAIN_COST_QUALITY_SCALE * qs |
| if state["money"] < cost: |
| msg = f"[FAIL] Insufficient funds for training run. Need ${cost:.2f}, have ${state['money']:.2f}." |
| state["log"].append(msg) |
| return state, msg |
|
|
| |
| raw_mult = 1.0 + (MAX_QUALITY_REWARD_MULT - 1.0) * (qr**QUALITY_EXPONENT) |
| noise = 1.0 + random.uniform(-REWARD_NOISE_PCT, REWARD_NOISE_PCT) |
| reward = BASE_REWARD * raw_mult * noise |
|
|
| state["money"] += reward - cost |
| state["runs"] += 1 |
| state["total_earned"] += reward |
| state["trained"] = True |
|
|
| lines = [ |
| f"[RUN #{state['runs']:04d}] ββββββββββββββββββββββ", |
| f" Cost: β${cost:.2f}", |
| f" Reward: +${reward:.2f}", |
| f" Net: ${reward - cost:+.2f}", |
| f" Balance: ${state['money']:.2f}", |
| f" Quality: {qs}/{_max_quality():.0f} ({qr * 100:.1f}%)", |
| ] |
| for l in lines: |
| state["log"].append(l) |
| return state, "\n".join(lines) |
|
|
|
|
| def unlock_cloud(state: dict) -> tuple[dict, str]: |
| state = _copy(state) |
| if state["cloud_unlocked"]: |
| return state, "[WARN] Cloud Inference already unlocked." |
| if state["money"] < CLOUD_UNLOCK_COST: |
| return ( |
| state, |
| f"[FAIL] Need ${CLOUD_UNLOCK_COST:.0f} to unlock Cloud Inference. Have ${state['money']:.2f}.", |
| ) |
| state["money"] -= CLOUD_UNLOCK_COST |
| state["cloud_unlocked"] = True |
| state["cloud_active"] = True |
| msg = f"[OK] Cloud Inference unlocked & active. (β${CLOUD_UNLOCK_COST:.0f})" |
| state["log"].append(msg) |
| return state, msg |
|
|
|
|
| def cloud_tick(state: dict) -> tuple[dict, float]: |
| """Called periodically when cloud is active. Returns (new_state, revenue_this_tick).""" |
| if not state["cloud_active"]: |
| return state, 0.0 |
| state = _copy(state) |
| qs = _total_quality_score(state) |
| revenue = CLOUD_BASE_REVENUE + CLOUD_QUALITY_MULT * qs |
| state["money"] += revenue |
| state["total_earned"] += revenue |
| return state, revenue |
|
|
|
|
| def toggle_cloud(state: dict) -> tuple[dict, str]: |
| state = _copy(state) |
| if not state["cloud_unlocked"]: |
| return state, "[FAIL] Cloud Inference not unlocked yet." |
| state["cloud_active"] = not state["cloud_active"] |
| status = "ACTIVE" if state["cloud_active"] else "PAUSED" |
| msg = f"[OK] Cloud Inference β {status}" |
| state["log"].append(msg) |
| return state, msg |
|
|
|
|
| |
|
|
|
|
| def chat_temperature(state: dict) -> float: |
| qr = _quality_ratio(state) |
| return CHAT_TEMP_MAX + (CHAT_TEMP_MIN - CHAT_TEMP_MAX) * qr |
|
|
|
|
| def quality_ratio(state: dict) -> float: |
| return _quality_ratio(state) |
|
|
|
|
| def status_lines(state: dict) -> str: |
| qs = _total_quality_score(state) |
| mx = int(_max_quality()) |
| qr = _quality_ratio(state) |
| temp = chat_temperature(state) |
| lines = [ |
| f" balance : ${state['money']:.2f}", |
| f" quality : {qs}/{mx} ({qr * 100:.1f}%)", |
| f" temperature: {temp:.2f}", |
| f" runs : {state['runs']}", |
| f" cloud : {'ACTIVE' if state['cloud_active'] else ('UNLOCKED/PAUSED' if state['cloud_unlocked'] else 'LOCKED')}", |
| ] |
| return "\n".join(lines) |
|
|
|
|
| def upgrade_status(state: dict) -> dict[str, dict]: |
| """Returns per-upgrade display info.""" |
| result = {} |
| for k, upg in UPGRADES.items(): |
| cur = state["upgrades"][k] |
| max_tier = len(upg["levels"]) - 1 |
| next_cost = upg["levels"][cur + 1]["cost"] if cur < max_tier else None |
| result[k] = { |
| "label": upg["label"], |
| "icon": upg["icon"], |
| "description": upg["description"], |
| "current_tier": cur, |
| "current_name": upg["levels"][cur]["name"], |
| "max_tier": max_tier, |
| "next_cost": next_cost, |
| "maxed": cur >= max_tier, |
| } |
| return result |
|
|
|
|
| |
|
|
|
|
| def _copy(state: dict) -> dict: |
| import copy |
|
|
| return copy.deepcopy(state) |