Martico2432's picture
Create state.py
3ebe92d verified
Raw
History Blame Contribute Delete
7.22 kB
# ─────────────────────────────────────────────
# state.py – pure game logic, no Gradio
# ─────────────────────────────────────────────
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,
)
# ── Helpers ──────────────────────────────────
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
)
# ── State factory ────────────────────────────
def new_game() -> dict:
return {
"money": STARTING_MONEY,
"upgrades": {k: 0 for k in UPGRADES}, # current tier index per upgrade
"trained": False, # has trained at least once
"cloud_unlocked": False,
"cloud_active": False,
"runs": 0,
"total_earned": 0.0,
"log": [], # list of log-line strings
}
# ── Actions ──────────────────────────────────
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
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
# Reward
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
# ── Chat helpers ─────────────────────────────
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
# ── Internal ─────────────────────────────────
def _copy(state: dict) -> dict:
import copy
return copy.deepcopy(state)