import json import sys from oni_api import api_get def get_game_state(): return { 'game': api_get('/api/state/game'), 'resources': api_get('/api/state/resources'), 'duplicants': api_get('/api/state/duplicants'), 'buildings': api_get('/api/state/buildings'), 'research': api_get('/api/state/research'), 'geysers': api_get('/api/state/geysers'), 'alerts': api_get('/api/state/alert'), 'critters': api_get('/api/state/critters'), 'plants': api_get('/api/state/plants'), 'rooms': api_get('/api/state/rooms'), } def as_dict(resources): if not isinstance(resources, list): return {} return {r.get('name'): r for r in resources} def buildings_by_cat(buildings): cats = {} for b in (buildings or []): cat = b.get('category', 'Other') cats.setdefault(cat, []).append(b) return cats def analyze_o2(resources, buildings): r = as_dict(resources) o2 = r.get('Oxygen', {}).get('amount', 0) algae = r.get('Algae', {}).get('amount', 0) pw = r.get('PollutedWater', {}).get('amount', 0) has_electrolyzer = any(b.get('id') == 'Electrolyzer' for b in (buildings or [])) has_diffuser = any(b.get('id') == 'OxygenDiffuser' for b in (buildings or [])) warnings = [] if o2 < 100: warnings.append(("CRITICAL", f"Oxygen critically low ({o2:.0f} kg)!")) elif o2 < 500: warnings.append(("WARN", f"Oxygen declining ({o2:.0f} kg)")) if o2 < 1000 and not has_electrolyzer and not has_diffuser: warnings.append(("CRITICAL", "No oxygen production buildings found! Build OxygenDiffuser or Electrolyzer")) if algae < 1000: warnings.append(("WARN", f"Algae running out ({algae:.0f} kg) — build electrolyzer")) elif algae < 5000 and not has_electrolyzer: warnings.append(("INFO", f"Algae moderate ({algae:.0f} kg) — plan SPOM transition")) if pw > 50000: warnings.append(("INFO", f"Polluted water abundant ({pw:.0f} kg) — use for reed fiber / pincha pepper")) return warnings def analyze_food(resources, buildings): r = as_dict(resources) cal = r.get('Calories', {}).get('amount', 0) has_farm = any(b.get('id') in ('PlanterBox', 'FarmTile') for b in (buildings or [])) has_grill = any(b.get('id') == 'ElectricGrill' for b in (buildings or [])) warnings = [] if cal < 100000: warnings.append(("CRITICAL", f"Food shortage ({cal:.0f} kcal)!")) elif cal < 500000: warnings.append(("WARN", f"Food declining ({cal:.0f} kcal)")) elif cal > 2000000: warnings.append(("INFO", f"Food surplus ({cal:.0f} kcal)")) if cal < 500000 and not has_farm: warnings.append(("WARN", "No farm plots found. Build PlanterBox and plant Mealwood")) if cal < 500000 and not has_grill: warnings.append(("INFO", "Build ElectricGrill to improve food quality")) return warnings def analyze_power(resources, buildings): r = as_dict(resources) coal = r.get('Coal', {}).get('amount', 0) hydrogen = r.get('Hydrogen', {}).get('amount', 0) natgas = r.get('NaturalGas', {}).get('amount', 0) generators = [b for b in (buildings or []) if b.get('id') in ( 'CoalGenerator', 'HydrogenGenerator', 'NaturalGasGenerator', 'ManualGenerator', 'PetroleumGenerator', 'WoodBurner' )] batteries = [b for b in (buildings or []) if b.get('id') in ('Battery', 'JumboBattery', 'SmartBattery')] warnings = [] if not generators: warnings.append(("CRITICAL", "No power generators found! Build ManualGenerator or CoalGenerator")) else: powered_on = sum(1 for g in generators if g.get('isOperational')) warnings.append(("INFO", f"Power: {len(generators)} generators ({powered_on} operational), {len(batteries)} batteries")) if coal < 5000 and any(g.get('id') == 'CoalGenerator' for g in generators): warnings.append(("WARN", f"Coal low ({coal:.0f} kg) — diversify power")) if hydrogen > 20000: warnings.append(("INFO", f"Hydrogen stockpiled ({hydrogen:.0f} kg) — add generators")) if natgas > 10000: warnings.append(("INFO", f"Natural gas abundant ({natgas:.0f} kg)")) return warnings def analyze_temp(): from oni_api import api_get data = api_get('/api/state/temperature/zones', auto_pause=False) warnings = [] if 'error' in data: return warnings avg = data.get('averageC', 0) if avg > 50: warnings.append(("CRITICAL", f"Overheating ({avg:.0f}°C)")) elif avg > 35: warnings.append(("WARN", f"High temperature ({avg:.0f}°C)")) elif avg < -5: warnings.append(("WARN", f"Too cold ({avg:.0f}°C)")) if data.get('hotSpots'): warnings.append(("WARN", f"{len(data['hotSpots'])} hot spots (>50°C)")) if data.get('coldSpots'): warnings.append(("WARN", f"{len(data['coldSpots'])} cold spots (<5°C)")) return warnings def analyze_water(resources): r = as_dict(resources) water = r.get('Water', {}).get('amount', 0) pw = r.get('PollutedWater', {}).get('amount', 0) sw = r.get('SaltWater', {}).get('amount', 0) total_water = water + pw + sw warnings = [] if total_water < 10000: warnings.append(("WARN", f"Total water low ({total_water:.0f} kg across all sources)")) elif total_water < 50000: warnings.append(("INFO", f"Water reserves moderate ({total_water:.0f} kg)")) if water < 10000 and pw > 10000: warnings.append(("INFO", f"Filter polluted water ({pw:.0f} kg available)")) return warnings def analyze_research(research): if not isinstance(research, list): return [] warnings = [] done = sum(1 for t in research if t.get('isComplete')) total = len(research) if done == 0 and total > 0: warnings.append(("WARN", "No research completed! Start with Research Station")) elif done < total * 0.3: warnings.append(("INFO", f"Research progress: {done}/{total} ({done*100//total}%)")) return warnings def suggest_actions(warnings, alerts, buildings): suggestions = [] has_electrolyzer = any(b.get('id') == 'Electrolyzer' for b in (buildings or [])) has_lavatory = any(b.get('id') == 'Lavatory' for b in (buildings or [])) has_sieve = any(b.get('id') == 'WaterSiever' for b in (buildings or [])) for sev, msg in warnings: ml = msg.lower() if 'oxygen' in ml: suggestions.append("Build SPOM: Electrolyzer + Hydrogen Generator at a water source") elif 'food' in ml and 'shortage' in ml: suggestions.append("Build PlanterBoxes x5, plant Mealwood (no irrigation needed)") elif 'food' in ml and 'declining' in ml: suggestions.append("Expand farm or start hatch ranching (Hatch eats Sedimentary Rock)") elif 'power' in ml and 'generator' in ml: suggestions.append("Build ManualGenerator (early) or CoalGenerator (durable)") elif 'coal' in ml: suggestions.append("Diversify power: build HydrogenGenerator + SmartBattery") elif 'hydrogen' in ml: suggestions.append("Connect HydrogenGenerator to your hydrogen vent/SPOM") elif 'water' in ml and 'low' in ml: suggestions.append("Dig to find water geyser or filter polluted water") elif 'heat' in ml or 'overheat' in ml: suggestions.append("Build insulated tiles around heat sources; add cooling loop") if not has_lavatory: suggestions.append("Build Lavatory + Water Sieve for renewable water loop") if not has_electrolyzer: suggestions.append("Plan SPOM once Algae < 5t or you have renewable water") if not has_sieve and has_lavatory: suggestions.append("Build Water Sieve to close the bathroom loop") return list(dict.fromkeys(suggestions)) def print_report(state): g = state.get('game', {}) if 'error' in g: print(f"[!] Cannot connect to game: {g['error']}") return False resources = state.get('resources', []) buildings = state.get('buildings', []) alerts = state.get('alerts', []) all_warnings = [] all_warnings += analyze_o2(resources, buildings) all_warnings += analyze_food(resources, buildings) all_warnings += analyze_power(resources, buildings) all_warnings += analyze_temp() all_warnings += analyze_water(resources) all_warnings += analyze_research(state.get('research', [])) critical = [w for w in all_warnings if w[0] == 'CRITICAL'] warns = [w for w in all_warnings if w[0] == 'WARN'] infos = [w for w in all_warnings if w[0] == 'INFO'] suggestions = suggest_actions(all_warnings, alerts, buildings) print("=" * 56) print(" ONI Analysis Report") print("=" * 56) print(f" Cycle: {g.get('cycle', '?')}") print(f" Duplicants: {g.get('duplicantCount', '?')}") print(f" World: {g.get('worldName', '?')}") print(f" Grid: {g.get('gridWidth', '?')} x {g.get('gridHeight', '?')}") print(f" Buildings: {len(buildings or [])}") print(f" Critters: {len(state.get('critters', []) or [])}") print(f" Geysers: {len(state.get('geysers', []) or [])}") print(f" Plants: {len(state.get('plants', []) or [])}") print(f" Rooms: {len(state.get('rooms', []) or [])}") print(f" Research done: {sum(1 for t in (state.get('research') or []) if t.get('isComplete'))}/{len(state.get('research', []) or [])}") print() if critical: print(f" [CRITICAL] {len(critical)} issues — act immediately!") for _, msg in critical: print(f" ! {msg}") print() if warns: print(f" [WARN] {len(warns)} issues") for _, msg in warns: print(f" * {msg}") print() if infos: print(f" [INFO] {len(infos)} notes") for _, msg in infos: print(f" i {msg}") print() if not all_warnings: print(" Status: All stable") print() if suggestions: print(f" Suggestions ({len(suggestions)}):") for s in suggestions: print(f" -> {s}") print() print(f" In-game alerts: {len(alerts) if isinstance(alerts, list) else 0}") if isinstance(alerts, list): for a in alerts: print(f" [{a.get('severity', '?')}] {a.get('title', '?')}") print("=" * 56) return True if __name__ == '__main__': state = get_game_state() if not print_report(state): sys.exit(1)