Files
geograsim/App/sims/logistik/headless-runner.html
T
Adminator 3885c83294 Atlas: Logistik-Gerüst + Heli-Fix + Waypoints-Regen + Level-Picker
- Neues Modul 'Logistik Europa': module_info-Eintrag, Landing-Card,
  PHP-Wrapper (logistik.php, modul-logistik.php), Engine-Skelett mit
  Enums + Helpern (travelCost, latePenalty, Bonus, Dijkstra,
  Polyline-Interpolation), Test-Harness, Kompetenzen-Draft,
  5 Seed-Dateien (locations, vehicle-types, cargo-types, railnet,
  contract-templates), 3 Level-Einträge in game_levels, Phase 0 durch
  die Logistik-Instanz geliefert und Atlas-Review bestanden
- Heli-Fix: heli-game.php mit BASE_PATH (production-sicher) +
  Asset-Pfad-Injection, geo_waypoints-Tabelle per Seed-Script
  auffindbar gemacht
- Waypoints-Regen-Tool: regen-waypoints-sql.php synchronisiert
  waypoints.sql aus DB (70 Einträge)
- Design-System: .ggs-level-grid / .ggs-level-card als Standard
  gepromotet (Klimas Muster, iPad-hover-safe)
- Inbox-Nachrichten: Kickoff-Briefings für Heli und Logistik,
  Asset-Map für Heli, DALL-E-Pipeline-Anleitung, Koordinations-
  nachrichten an Fluss, Klima, Glossar, Lehrplan

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 20:43:48 +02:00

142 lines
5.8 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Logistik Europa — Headless-Runner</title>
<style>
body { font-family: 'Segoe UI', system-ui, sans-serif; max-width: 1100px; margin: 24px auto; padding: 0 16px; color: #222; background: #f7f5ef; }
h1 { font-size: 1.4rem; margin-bottom: 8px; }
h2 { font-size: 1.05rem; color: #1f4b37; margin-top: 24px; }
.meta { color: #666; font-size: 0.9rem; margin-bottom: 20px; }
.panel { background: #fff; border: 1px solid #e2ddd1; border-radius: 8px; padding: 14px 18px; margin: 14px 0; }
table { border-collapse: collapse; width: 100%; font-size: 0.88rem; }
th, td { text-align: left; padding: 6px 10px; border-bottom: 1px solid #eee; }
th { background: #f0ece3; font-weight: 600; }
td.num { text-align: right; font-variant-numeric: tabular-nums; }
.ok { color: #2d6d3d; font-weight: 600; }
.fail { color: #b32d2d; font-weight: 600; }
.skip { color: #8a7a4f; font-style: italic; }
.err { color: #b32d2d; font-style: italic; }
button { background: #1f4b37; color: #fff; border: 0; border-radius: 4px; padding: 8px 14px; cursor: pointer; font-size: 0.95rem; margin-right: 8px; }
button:hover { background: #2d6d4d; }
pre { background: #2b2b2b; color: #e6e6e6; padding: 12px; border-radius: 6px; font-size: 0.85rem; overflow-x: auto; }
.legend { font-size: 0.82rem; color: #666; margin-top: 6px; }
details > summary { cursor: pointer; color: #1f4b37; font-weight: 600; }
</style>
</head>
<body>
<h1>Logistik Europa — Headless-Runner</h1>
<p class="meta">
Deterministische Level-Durchläufe für die Balance-Verifikation aus
<code>balance-matrix.md</code> §3. Logik liegt komplett in
<code>headless-runner.js</code> — diese Seite ist nur die UI-Hülle.
</p>
<div class="panel">
<p>
<button id="run-noop">Nur <code>noop</code> (3 Level × 5 Seeds, läuft)</button>
<button id="run-all">Alle Strategien (Stubs werfen)</button>
</p>
<div id="results"><p class="legend">Klick auf einen Button.</p></div>
</div>
<h2>Akzeptanzkorridor (aus balance-matrix.md §3)</h2>
<table>
<thead><tr><th>Level</th><th>noop</th><th>naive</th><th>greedy</th><th>optimal</th></tr></thead>
<tbody>
<tr><td>1</td><td>fail</td><td>success ≥+1.000 €</td><td>success ≥+2.000 €</td><td>success ≥+3.000 €</td></tr>
<tr><td>2</td><td>fail</td><td>fail / negativ</td><td>success ≥0 €</td><td>success ≥+5.000 €</td></tr>
<tr><td>3</td><td>fail</td><td>fail (sicher)</td><td>fail / knapp</td><td>success ≥0 €</td></tr>
</tbody>
</table>
<h2>Strategie-Definitionen</h2>
<ul id="strat-list"><!-- gefüllt aus LogistikRunner.STRATEGIES --></ul>
<h2>API</h2>
<pre>const r = await LogistikRunner.runLevel(1, 1, 'noop');
// { successful, endBalance, completedContracts, lateDeliveries,
// durationHours, hintUsages, log, strategy, level, seed }
const matrix = await LogistikRunner.runMatrix({
levels: [1,2,3], strategies: ['noop','naive','greedy','optimal'],
seeds: [1,2,3,4,5], tolerateErrors: true
});</pre>
<script src="engine.js"></script>
<script src="headless-runner.js"></script>
<script>
(function () {
'use strict';
// Strategie-Liste rendern
const stratList = document.getElementById('strat-list');
Object.entries(LogistikRunner.STRATEGIES).forEach(([name, def]) => {
const li = document.createElement('li');
li.innerHTML = '<strong>' + name + '</strong> — ' + def.description;
stratList.appendChild(li);
});
function renderTable(rows) {
let html = '<table><thead><tr>'
+ '<th>Lvl</th><th>Strategie</th><th>Seed</th>'
+ '<th>Status</th><th class="num">Balance</th>'
+ '<th class="num">Aufträge</th><th class="num">Verspätet</th>'
+ '<th class="num">Dauer (h)</th><th>Notiz</th></tr></thead><tbody>';
rows.forEach(r => {
let cls, label, note = '';
if (r._error) {
cls = 'err'; label = 'ERROR'; note = r._error;
} else if (r.successful === null) {
cls = 'skip'; label = 'SKIP';
} else if (r.successful) {
cls = 'ok'; label = 'success';
} else {
cls = 'fail'; label = 'fail';
}
html += '<tr>'
+ '<td>' + r.level + '</td>'
+ '<td><code>' + r.strategy + '</code></td>'
+ '<td>' + r.seed + '</td>'
+ '<td class="' + cls + '">' + label + '</td>'
+ '<td class="num">' + (r.endBalance ?? '—') + '</td>'
+ '<td class="num">' + (r.completedContracts ?? 0) + '</td>'
+ '<td class="num">' + (r.lateDeliveries ?? 0) + '</td>'
+ '<td class="num">' + (r.durationHours ?? 0) + '</td>'
+ '<td>' + note + '</td>'
+ '</tr>';
});
html += '</tbody></table>';
return html;
}
async function runNoop() {
const root = document.getElementById('results');
root.innerHTML = '<p class="legend">…läuft…</p>';
const rows = await LogistikRunner.runMatrix({
strategies: ['noop'], tolerateErrors: true,
});
root.innerHTML = renderTable(rows)
+ '<p class="legend">' + rows.length + ' Durchläufe — '
+ '<code>noop</code> ist die Phase-0-Demo-Strategie. '
+ 'Alle Level → success=false ist erwartet.</p>';
}
async function runAll() {
const root = document.getElementById('results');
root.innerHTML = '<p class="legend">…läuft…</p>';
const rows = await LogistikRunner.runMatrix({ tolerateErrors: true });
root.innerHTML = renderTable(rows)
+ '<p class="legend">' + rows.length + ' Durchläufe — '
+ '<code>naive/greedy/optimal</code> werfen kontrolliert „Phase 2", '
+ '<code>noop</code> läuft sauber durch.</p>';
}
document.getElementById('run-noop').addEventListener('click', runNoop);
document.getElementById('run-all').addEventListener('click', runAll);
})();
</script>
</body>
</html>