/** * SIM-08: Energiewende-Planer*in — Spielbare Energiemix-Simulation * * Du übernimmst 2025 als Energieplaner*in eine Region mit ca. 200.000 * Einwohnern. Aktuell kommt der Strom größtenteils aus fossilen Quellen. * Bis 2050 (25 Jahre) musst du den Energiemix umbauen: * * - Die Nachfrage steigt (E-Autos, Wärmepumpen, Digitalisierung). * - CO₂ muss runter — Klimaziel. * - Blackouts dürfen nicht passieren — Versorgungssicherheit. * - Du hast ein knappes Budget. * * Fachlich korrekt: * - Kapazitätsfaktoren echt (Wind ~25%, Solar ~12%, Wasser ~45%, Kernkraft ~90%) * - CO₂-Emissionen in gCO₂/kWh basieren auf IPCC-Median-Werten * - Erneuerbare brauchen Speicher oder backup-fähige Partner (Gas) * * Kernbotschaft: Es gibt keinen Königsweg. Jede Technologie hat Stärken und * Schwächen. Die Transformation ist ein Balance-Akt zwischen drei Zielen: * CO₂, Kosten und Versorgungssicherheit. */ import { GameEngine, type GameMeta } from '@core/game-engine' const META: GameMeta = { id: 'sim-08', title: 'Energiewende-Planer*in', description: 'Führe deine Region bis 2050 in eine CO₂-neutrale, sichere Stromversorgung.', msPerTick: 4000, tickUnit: 'Jahr', maxTicks: 25, tutorialSteps: 4, } const START_YEAR = 2025 export interface PlantType { id: string name: string emoji: string description: string cost: number // Baukosten in Mio. € capacityMW: number // Nennleistung capacityFactor: number // Anteil der Nennleistung im Jahresmittel (0..1) co2PerKWh: number // gCO₂/kWh (IPCC Median) upkeep: number // Mio. €/Jahr renewable: boolean flexible: boolean // Kann bei Bedarf hochgefahren werden (für Backup) storage?: number // MW Speicherkapazität (reduziert Blackout-Risiko) } export const PLANT_TYPES: PlantType[] = [ { id: 'coal', name: 'Kohlekraftwerk', emoji: '🏭', description: 'Billig, zuverlässig, aber höchste CO₂-Emission. Gesellschaftlich umstritten.', cost: 80, capacityMW: 300, capacityFactor: 0.75, co2PerKWh: 820, upkeep: 8, renewable: false, flexible: true, }, { id: 'gas', name: 'Gaskraftwerk', emoji: '⛽', description: 'Flexibler Backup — schnell regelbar. Halbe CO₂-Emission wie Kohle.', cost: 110, capacityMW: 250, capacityFactor: 0.55, co2PerKWh: 490, upkeep: 9, renewable: false, flexible: true, }, { id: 'hydro', name: 'Wasserkraftwerk', emoji: '💧', description: 'CO₂-frei und zuverlässig. Standortgebunden, bei Dürre weniger Leistung.', cost: 280, capacityMW: 180, capacityFactor: 0.45, co2PerKWh: 24, upkeep: 4, renewable: true, flexible: true, }, { id: 'wind', name: 'Windpark', emoji: '💨', description: 'Günstig, CO₂-arm. Wetterabhängig: liefert nur bei Wind (~25% der Zeit).', cost: 120, capacityMW: 200, capacityFactor: 0.28, co2PerKWh: 11, upkeep: 5, renewable: true, flexible: false, }, { id: 'solar', name: 'Solarpark', emoji: '☀️', description: 'Sehr günstig, CO₂-arm. Nur tagsüber, bei Bewölkung weniger.', cost: 70, capacityMW: 150, capacityFactor: 0.13, co2PerKWh: 48, upkeep: 3, renewable: true, flexible: false, }, { id: 'biomass', name: 'Biomasse-Heizkraftwerk', emoji: '🌿', description: 'Aus Holz und Agrarresten. Flexibel, nahezu CO₂-neutral.', cost: 140, capacityMW: 80, capacityFactor: 0.65, co2PerKWh: 230, upkeep: 7, renewable: true, flexible: true, }, { id: 'nuclear', name: 'Kernkraftwerk', emoji: '☢️', description: 'CO₂-frei, extrem hohe Grundlast-Leistung. Hohe Baukosten, politisch umstritten.', cost: 600, capacityMW: 1000, capacityFactor: 0.90, co2PerKWh: 12, upkeep: 18, renewable: false, flexible: false, }, { id: 'battery', name: 'Batteriespeicher', emoji: '🔋', description: 'Erzeugt keinen Strom, glättet aber Schwankungen und verhindert Blackouts.', cost: 150, capacityMW: 0, capacityFactor: 0, co2PerKWh: 0, upkeep: 4, renewable: true, flexible: true, storage: 100, }, ] interface OwnedPlant { typeId: string count: number builtTick: number } export class EnergiemixGame extends GameEngine { private plants: OwnedPlant[] = [] private firedEvents = new Set() // Wetter-Modifikator dieses Jahres (ca. 0.7..1.2) private weatherWindFactor = 1.0 private weatherSolarFactor = 1.0 private weatherHydroFactor = 1.0 constructor() { super(META) this.setupResources() this.setupGoals() this.setupTutorial() this.setupStartingPlants() this.recalc() } private setupResources(): void { this.addResource({ id: 'budget', name: 'Budget', icon: '💰', initial: 300, unit: 'Mio €', format: (v) => `${Math.round(v)} Mio €`, }) this.addResource({ id: 'demand', name: 'Strombedarf', icon: '🔌', initial: 700, unit: 'MW', format: (v) => `${Math.round(v)} MW`, }) this.addResource({ id: 'supply', name: 'Erzeugung (Ø)', icon: '⚡', initial: 0, unit: 'MW', format: (v) => `${Math.round(v)} MW`, }) this.addResource({ id: 'co2', name: 'CO₂-Ausstoß', icon: '🌫', initial: 0, unit: 'kt/J', format: (v) => `${Math.round(v)} kt/J`, }) this.addResource({ id: 'renewable', name: 'Erneuerbar', icon: '🌱', initial: 0, unit: '%', format: (v) => `${Math.round(v)} %`, }) this.addResource({ id: 'blackouts', name: 'Blackouts', icon: '🕯', initial: 0, unit: '', format: (v) => `${Math.round(v)}`, }) } private setupGoals(): void { this.addGoal({ id: 'survive', title: 'Bis 2050 planen', description: '25 Jahre Energiewende begleiten.', check: (g) => (g as EnergiemixGame).tick >= 25, progress: (g) => Math.min(100, ((g as EnergiemixGame).tick / 25) * 100), required: true, }) this.addGoal({ id: 'renewable', title: '80 % Erneuerbare Energie', description: 'Mindestens 80 % des Strombedarfs aus erneuerbaren Quellen.', check: (g) => g.getResource('renewable') >= 80, progress: (g) => Math.min(100, (g.getResource('renewable') / 80) * 100), required: true, }) this.addGoal({ id: 'co2', title: 'CO₂ unter 400 kt/Jahr', description: 'Die Emissionen müssen deutlich sinken.', check: (g) => g.getResource('co2') < 400, progress: (g) => { const co2 = g.getResource('co2') return Math.max(0, Math.min(100, 100 - ((co2 - 400) / 10))) }, required: true, }) this.addGoal({ id: 'reliable', title: 'Maximal 3 Blackouts', description: 'Die Versorgung muss sicher bleiben.', check: (g) => g.getResource('blackouts') <= 3, progress: (g) => Math.max(0, Math.min(100, 100 - g.getResource('blackouts') * 25)), required: true, }) this.addGoal({ id: 'budget', title: 'Nicht pleite gehen', description: 'Budget muss positiv bleiben.', check: (g) => g.getResource('budget') > 0, required: true, }) } private setupTutorial(): void { this.setTutorial([ { triggerTick: 0, title: 'Willkommen, Energieplaner*in!', text: '2025. Du übernimmst die Energieplanung für eine Region mit ca. 200.000 Einwohner*innen.\n\nDein Auftrag: Bis 2050 (in 25 Jahren) muss der Strom CO₂-neutral, bezahlbar und zuverlässig sein.\n\nAktuell dominiert noch fossiler Strom. Der Umbau kostet Geld — aber nichts zu tun kostet das Klima.', unlocks: ['budget'], }, { triggerTick: 0, title: 'Der Energie-Mix', text: 'Es gibt keine Lösung, die alles kann:\n\n🏭 Kohle: billig, aber hoher CO₂-Ausstoß\n⛽ Gas: flexibel, mittleres CO₂\n💧 Wasser: sauber, aber abhängig vom Niederschlag\n💨 Wind: günstig, nur bei Wind\n☀️ Solar: sehr günstig, nur bei Sonne\n🌿 Biomasse: klimaneutral, begrenzte Verfügbarkeit\n☢️ Kernkraft: CO₂-frei, teuer, umstritten\n🔋 Speicher: glättet Schwankungen', unlocks: ['shop'], }, { triggerTick: 0, title: 'Versorgungssicherheit', text: 'Wind und Sonne liefern nicht immer.\n\nDer sogenannte Kapazitätsfaktor beschreibt, wieviel % der Nennleistung im Jahresmittel wirklich anfällt:\n- Wind ≈ 28 %\n- Solar ≈ 13 %\n- Wasser ≈ 45 %\n- Kernkraft ≈ 90 %\n\nFehlt Strom, drohen Blackouts. Backup-Kraftwerke (Gas, Biomasse) oder Speicher helfen, Lücken zu überbrücken.', unlocks: ['supply'], }, { triggerTick: 0, title: 'Der Zielkonflikt', text: 'Du musst drei Ziele unter einen Hut bringen:\n\n🌱 CO₂: auf unter 400 kt/Jahr senken\n⚡ Versorgung: maximal 3 Blackouts bis 2050\n💰 Budget: nicht pleite gehen\n\nPro Jahr bekommst du Einnahmen aus Stromverkauf. Bau klug aus — Kraftwerke brauchen mehrere Jahre, bis sie sich rechnen.\n\nViel Erfolg! ⚡', unlocks: ['controls'], }, ]) } private setupStartingPlants(): void { // Startzustand: fossil dominiert this.plants = [ { typeId: 'coal', count: 2, builtTick: -10 }, { typeId: 'gas', count: 1, builtTick: -5 }, { typeId: 'hydro', count: 1, builtTick: -20 }, ] } private recalc(): void { let totalMW = 0 let renewableMW = 0 let co2 = 0 let upkeep = 0 let storageMW = 0 let flexibleMW = 0 for (const p of this.plants) { const t = PLANT_TYPES.find(x => x.id === p.typeId) if (!t) continue // Wetter-Modifikator anwenden let cf = t.capacityFactor if (t.id === 'wind') cf *= this.weatherWindFactor else if (t.id === 'solar') cf *= this.weatherSolarFactor else if (t.id === 'hydro') cf *= this.weatherHydroFactor const avgMW = t.capacityMW * cf * p.count totalMW += avgMW if (t.renewable) renewableMW += avgMW // CO₂: gCO₂/kWh × MW × 8760h/Jahr = gCO₂/J → kt/J co2 += (avgMW * 8760 * t.co2PerKWh) / 1e9 upkeep += t.upkeep * p.count if (t.storage) storageMW += t.storage * p.count if (t.flexible) flexibleMW += t.capacityMW * p.count } this.setResource('supply', totalMW) this.setResource('co2', co2) const demand = this.getResource('demand') this.setResource('renewable', demand > 0 ? Math.min(100, (renewableMW / demand) * 100) : 0) this.setVariable('upkeepTotal', upkeep) this.setVariable('storageMW', storageMW) this.setVariable('flexibleMW', flexibleMW) } buyPlant(typeId: string): boolean { const t = PLANT_TYPES.find(x => x.id === typeId) if (!t) return false const budget = this.getResource('budget') if (budget < t.cost) { this.addEvent('error', `Nicht genug Budget für ${t.name}`, 'warning') return false } this.changeResource('budget', -t.cost) const existing = this.plants.find(p => p.typeId === typeId) if (existing) existing.count++ else this.plants.push({ typeId, count: 1, builtTick: this.tick }) this.recalc() this.addEvent('build', `${t.emoji} ${t.name} gebaut (-${t.cost} Mio €)`, 'success') this.notify() return true } getOwnedPlants(): OwnedPlant[] { return this.plants } getPlantCount(id: string): number { return this.plants.find(p => p.typeId === id)?.count ?? 0 } getStartYear(): number { return START_YEAR } getWeatherFactors() { return { wind: this.weatherWindFactor, solar: this.weatherSolarFactor, hydro: this.weatherHydroFactor, } } protected simulateTick(): void { // 1. Nachfrage wächst (E-Mobilität, Wärmepumpen, Digitalisierung) // Ca. 1.8 % pro Jahr const demand = this.getResource('demand') this.setResource('demand', demand * 1.018) // 2. Wetter des Jahres ziehen this.weatherWindFactor = 0.75 + Math.random() * 0.5 // 0.75..1.25 this.weatherSolarFactor = 0.85 + Math.random() * 0.3 // 0.85..1.15 this.weatherHydroFactor = 0.70 + Math.random() * 0.55 // 0.70..1.25 // 3. Neu berechnen mit aktuellem Wetter this.recalc() // 4. Versorgungs-Check const supply = this.getResource('supply') const newDemand = this.getResource('demand') const storageMW = this.getVariable('storageMW') const flexibleMW = this.getVariable('flexibleMW') // Spitzenlast ist höher als Durchschnitt (Faktor ~1.35) const peakDemand = newDemand * 1.35 // Gesicherte Leistung = flexible Kraftwerke + Speicher const firmCapacity = flexibleMW + storageMW if (supply < newDemand * 0.9) { // Unterversorgung im Jahresmittel this.changeResource('blackouts', 1) this.addEvent('blackout', `🕯 Blackout! Stromangebot (${Math.round(supply)} MW) deckt die Nachfrage nicht.`, 'danger') } else if (firmCapacity < peakDemand * 0.7) { // Nicht genug gesicherte Leistung für Spitzenlast if (Math.random() < 0.35) { this.changeResource('blackouts', 1) this.addEvent('peak-blackout', `⚠️ Spitzenlast-Blackout: zu wenig steuerbare Leistung im Netz.`, 'warning') } } // 5. Einnahmen aus Stromverkauf (nur was wirklich verkauft wird) const soldMW = Math.min(supply, newDemand) const revenue = Math.round(soldMW * 0.08) // ~0.08 Mio €/MW/Jahr this.changeResource('budget', revenue) // 6. Wartungskosten const upkeep = this.getVariable('upkeepTotal') this.changeResource('budget', -upkeep) // 7. CO₂-Strafe ab 2030 (EU-ETS-Preise steigen) if (this.tick >= 5) { const co2 = this.getResource('co2') const penalty = Math.round(co2 * 0.03 * Math.min(3, (this.tick - 4) / 5)) this.changeResource('budget', -penalty) if (penalty > 0 && this.tick === 5 && !this.firedEvent('ets-start')) { this.addEvent('ets', `📜 EU-CO₂-Bepreisung greift: Emissionen kosten jetzt Geld.`, 'warning') } } // 8. Events if (this.weatherWindFactor < 0.85 && this.weatherSolarFactor < 0.95 && this.tick > 2) { this.addEvent('dunkelflaute', `🌫 Dunkelflaute: wenig Wind, wenig Sonne. Versorgung knapp.`, 'warning') } if (this.weatherHydroFactor < 0.8) { this.addEvent('drought', `☀️ Trockenes Jahr: Wasserkraft liefert weniger.`, 'info') } if (this.tick === 3 && !this.firedEvent('ev-boom')) { this.addEvent('ev-boom', `🔋 E-Mobilitäts-Boom: Strombedarf steigt schneller als erwartet.`, 'info') } if (this.tick === 10 && !this.firedEvent('heat-pumps')) { this.addEvent('heat-pumps', `🔥 Wärmepumpen-Förderung: Heizen wird elektrisch.`, 'info') } if (this.tick === 15 && !this.firedEvent('coal-exit')) { const coalCount = this.getPlantCount('coal') if (coalCount > 0) { this.addEvent('coal-exit', `📢 Kohleausstieg beschlossen — alte Kohlekraftwerke werden unrentabel.`, 'warning') } } if (this.getResource('renewable') >= 50 && !this.firedEvent('milestone-50')) { this.addEvent('milestone-50', `🌱 Meilenstein: 50 % erneuerbare Energie erreicht!`, 'success') } if (this.getResource('renewable') >= 80 && !this.firedEvent('milestone-80')) { this.addEvent('milestone-80', `🎉 80 % erneuerbar — Klimaziel erreicht!`, 'success') } } private firedEvent(id: string): boolean { if (this.firedEvents.has(id)) return true this.firedEvents.add(id) return false } protected checkLossCondition(): boolean { if (this.getResource('budget') < -200) return true if (this.getResource('blackouts') > 8) return true return false } }