Stand 2026-04-13: PHP/MySQL Infrastruktur, Flussmanagement, Stadt-Prototyp
- PHP/MySQL Backend (XAMPP + Produktionsserver) - Front-Controller, API-Endpunkte, Session-Management - Flussmanagement-Simulation (Echtzeit, Punkt-basierter Fluss) - Stadt & Raumplanung (Prototyp, Top-Down Kachelsystem) - Klimawaechter 3D: Deiche kleiner, Baeume kippen, Budget angepasst - persistence.ts: Dualer Speicher (localStorage + Server-API) - 6 Unit-Test-Dateien fuer bestehende Simulationen Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Klimawächter — Mechanik-Trace
|
||||
*
|
||||
* Spielt das Klimawächter-Spiel rein rechnerisch über alle 75 Jahre für
|
||||
* verschiedene Strategien durch und gibt die Schlüssel-Variablen als
|
||||
* ASCII-Tabellen aus. Dient zur Analyse der Spielmechanik:
|
||||
* - Steigt CO₂ ohne Maßnahmen?
|
||||
* - Wie reagiert die Temperatur auf verschiedene Strategien?
|
||||
* - Wann wird die Stadt überflutet?
|
||||
* - Sind die Maßnahmen sinnvoll dimensioniert?
|
||||
*
|
||||
* Wird mit `npx vitest run sim-05-trace` aufgerufen.
|
||||
* Test schlägt nie fehl — er druckt nur.
|
||||
*/
|
||||
|
||||
import { describe, it } from 'vitest'
|
||||
import { KlimawaechterGame, MEASURES } from '../../src/sims/sim-05-treibhaus/game'
|
||||
|
||||
interface TraceRow {
|
||||
tick: number
|
||||
co2: number
|
||||
temp: number
|
||||
sea: number
|
||||
flooded: number
|
||||
budget: number
|
||||
pop: number
|
||||
measures: Record<string, number>
|
||||
}
|
||||
|
||||
interface Strategy {
|
||||
name: string
|
||||
buy: (g: KlimawaechterGame, tick: number) => void
|
||||
}
|
||||
|
||||
function runStrategy(strat: Strategy): TraceRow[] {
|
||||
const game = new KlimawaechterGame()
|
||||
// Tutorial überspringen, in 'playing' wechseln
|
||||
;(game as any).state = 'playing'
|
||||
;(game as any).tutorialStep = (game as any).tutorialSteps.length
|
||||
const rows: TraceRow[] = []
|
||||
|
||||
// Startzustand auch erfassen
|
||||
const snapshot0 = (): TraceRow => ({
|
||||
tick: 0,
|
||||
co2: game.getResource('co2'),
|
||||
temp: game.getResource('temperature'),
|
||||
sea: game.getResource('sealevel'),
|
||||
flooded: game.getResource('flooded'),
|
||||
budget: game.getResource('budget'),
|
||||
pop: game.getResource('population'),
|
||||
measures: countMeasures(game),
|
||||
})
|
||||
rows.push(snapshot0())
|
||||
|
||||
// 75 Ticks (= 75 Jahre)
|
||||
for (let i = 1; i <= 75; i++) {
|
||||
strat.buy(game, i)
|
||||
// simulateTick ist protected → cast
|
||||
;(game as any).simulateTick()
|
||||
;(game as any).tick = i
|
||||
rows.push({
|
||||
tick: i,
|
||||
co2: game.getResource('co2'),
|
||||
temp: game.getResource('temperature'),
|
||||
sea: game.getResource('sealevel'),
|
||||
flooded: game.getResource('flooded'),
|
||||
budget: game.getResource('budget'),
|
||||
pop: game.getResource('population'),
|
||||
measures: countMeasures(game),
|
||||
})
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
function countMeasures(game: KlimawaechterGame): Record<string, number> {
|
||||
const result: Record<string, number> = {}
|
||||
for (const m of MEASURES) {
|
||||
result[m.id] = game.getMeasureCount(m.id)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function fmtRow(r: TraceRow): string {
|
||||
const m = Object.entries(r.measures).filter(([_, c]) => c > 0).map(([k, c]) => `${k}:${c}`).join(',') || '-'
|
||||
return [
|
||||
String(2025 + r.tick).padStart(4),
|
||||
r.co2.toFixed(0).padStart(5),
|
||||
r.temp.toFixed(2).padStart(6),
|
||||
r.sea.toFixed(1).padStart(6),
|
||||
r.flooded.toFixed(0).padStart(4),
|
||||
r.budget.toFixed(0).padStart(6),
|
||||
String(r.pop).padStart(6),
|
||||
m.padStart(20),
|
||||
].join(' │ ')
|
||||
}
|
||||
|
||||
function printTrace(name: string, rows: TraceRow[]) {
|
||||
console.log('\n═══════════════════════════════════════════════════════════════════════════════════')
|
||||
console.log(`STRATEGIE: ${name}`)
|
||||
console.log('═══════════════════════════════════════════════════════════════════════════════════')
|
||||
console.log(' Jahr │ CO₂ │ Temp │ Meer │ Flut │ Budget │ Bev │ Maßnahmen')
|
||||
console.log('──────┼───────┼────────┼───────┼──────┼────────┼────────┼──────────────────────')
|
||||
// Alle 5 Jahre + erstes + letztes
|
||||
for (const r of rows) {
|
||||
if (r.tick === 0 || r.tick === 75 || r.tick % 5 === 0) {
|
||||
console.log(fmtRow(r))
|
||||
}
|
||||
}
|
||||
// Endbewertung
|
||||
const last = rows[rows.length - 1]
|
||||
const won =
|
||||
last.tick >= 75 &&
|
||||
last.temp < 17 &&
|
||||
last.budget > 0 &&
|
||||
last.flooded < 30
|
||||
console.log('──────┴───────┴────────┴───────┴──────┴────────┴────────┴──────────────────────')
|
||||
console.log(` Status: ${won ? '✅ GEWONNEN' : '❌ VERLOREN'}`)
|
||||
}
|
||||
|
||||
// === Strategien ===
|
||||
const strategies: Strategy[] = [
|
||||
{
|
||||
name: 'Nichts tun (Baseline)',
|
||||
buy: () => {},
|
||||
},
|
||||
{
|
||||
name: 'Nur Wälder (alle 3 Jahre einer)',
|
||||
buy: (g, t) => { if (t % 3 === 0) g.buyMeasure('forest') },
|
||||
},
|
||||
{
|
||||
name: 'Nur Solar (sobald leistbar)',
|
||||
buy: (g) => {
|
||||
while (g.getResource('budget') >= 200) {
|
||||
if (!g.buyMeasure('solar')) break
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Nur Wind (sobald leistbar)',
|
||||
buy: (g) => {
|
||||
while (g.getResource('budget') >= 400) {
|
||||
if (!g.buyMeasure('wind')) break
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Nur Deiche (gegen Überflutung)',
|
||||
buy: (g) => {
|
||||
while (g.getResource('budget') >= 300) {
|
||||
if (!g.buyMeasure('dike')) break
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Wälder + Solar gemischt',
|
||||
buy: (g, t) => {
|
||||
if (t % 4 === 0 && g.getResource('budget') >= 200) g.buyMeasure('solar')
|
||||
else if (g.getResource('budget') >= 50) g.buyMeasure('forest')
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Optimal: Solar + Wind + 1 Deich',
|
||||
buy: (g, t) => {
|
||||
if (t === 5 && g.getResource('budget') >= 300) g.buyMeasure('dike')
|
||||
if (g.getResource('budget') >= 400) g.buyMeasure('wind')
|
||||
else if (g.getResource('budget') >= 200) g.buyMeasure('solar')
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
describe('Klimawächter — Mechanik-Trace über 75 Jahre', () => {
|
||||
for (const strat of strategies) {
|
||||
it(`Trace: ${strat.name}`, () => {
|
||||
const rows = runStrategy(strat)
|
||||
printTrace(strat.name, rows)
|
||||
})
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user