- Logistik: Musik-Player Playlist-Modus (5 Gruppen, 20 Tracks) - Heli: End-Screen auf .ggs-endscreen, Mission-Bilder, Voice-Lines, Briefing-Audio - Logistik: Layout-Tausch Auftraege+Fahrzeuge links, Karte rechts - Logistik: Tier-1-Staedte ausgebaut, Auto-Timescale-Badge - Logistik: Roadnet/Railnet Dijkstra-Routing, Balance-Updates - Atlas: 5 Atlas-Inbox-Nachrichten (Cards-Pattern, DALL-E-Key, Musik-Playlists) - Status-Updates Heli + Logistik Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
18 KiB
Logistik Europa — Balance-Matrix v0.2
Stand: 2026-04-20 (Phase 0 — Atlas-Review angefordert) Quellen: Pflichtenheft Kap 65 + Atlas-Kickoff-Briefing §5.1 + eigene Schätzungen (markiert)
Diese Matrix ist der verbindliche Startpunkt für alle Level-Konfigurationen. Werte werden in
game_levels.params(JSON) persistiert (siehe §6 INSERT-SQL), zur Laufzeit erreichbar überwindow.LOGISTIK_LEVELS. Einzeln änderbar von Lehrkräften über das Admin-Tool (Phase 7).Akzeptanzkorridor (Atlas-Vorgabe, hartes Kriterium):
- Level 1: ≥ 90 % der 13–15-jährigen schaffen Level beim ersten Versuch
- Level 3: ~50 % schaffen es beim ersten / ~90 % beim dritten Versuch
Messung über Headless-Runner (
headless-runner.js) mit drei Strategien (naive,greedy,optimal) plusnoopals Lebenszeichen-Strategie.
1. Level-Parameter mit Rationale
Tabelle pro Parameter. Quelle = A (Atlas-Briefing §5.1), PH (Pflichtenheft Kap), S (Schätzung Logistik-Instanz). „=A" in Rationale heißt: Atlas-Wert übernommen, keine Abweichung. Abweichungen sind ausführlich begründet.
| Parameter | L1 | L2 | L3 | Quelle | Rationale |
|---|---|---|---|---|---|
| Startbudget (€) | 8.000 | 5.000 | 2.500 | A | =A. Werte erlauben bei TRUCK_SMALL Wien→Salzburg (~472 € Fahrkosten) genau 1 Standardauftrag (1.000 €) als Tutorialgewinn auf L1; auf L3 ist jede Fehlentscheidung sichtbar. |
| Aufträge parallel (max) | 1 | 3 | 6 | A / PH 13.3 | =A. PH 13.3 nennt nur „mehrere", Atlas konkretisiert. 1/3/6 ist saubere Verdoppelung+Steigerung, didaktisch sortierbar. |
| Fahrzeuge verfügbar | 1 | 3 | 5 | A / PH 13.3 | =A bzgl. Anzahl. Typen-Mix konkretisiert: L1=TRUCK_SMALL; L2=TRUCK_SMALL+TRUCK_LARGE+TRAIN (alle Typen, je 1); L3=variabel (2× SMALL, 2× LARGE, 1× TRAIN). |
| Fristlänge-Multiplikator | × 1.5 | × 1.2 | × 1.0 | A / PH 65.6 | =A. PH 65.6 definiert nur Standardauftrag-Frist als Distanz/60 × 1.5. Atlas reduziert pro Level — gut, bleibt. |
| Hilfestufe (default) | BLINK_EXACT |
SHOW_REGION |
NONE |
A / PH 10.3 | =A. PH 10.3 nennt 5 Stufen, Atlas wählt die didaktisch sinnvollste Spreizung (volle Hilfe → mittlere → keine). SHOW_COUNTRY zwischen L1 und L2 als künftige Lehrkraft-Option. |
| Event-Wahrscheinlichkeit (Multiplikator) | 0 (deaktiviert) | 0.5 | 1.0 | S abweichend | Abweichung von A (war 2 %/5 %/10 %): Setze L1 auf 0 statt halbiert. Begründung: PH 4.2 verlangt „motivierend, nicht frustrierend". Bei einem einzigen Auftrag im Tutorial ist ein Unfall-Event statistisch zwar selten, aber wenn er trifft, ist der Effekt katastrophal (Speed×0.5 bei einer einzigen 4.5h-Fahrt = +2.25h Verspätung = 450 € Strafe = halber Erlös weg). Bei 0 % wird das Modell durchsichtig. Events kommen ab L2. |
| Verspätungsstrafe (% Wert/h) | 10 % | 20 % | 30 % | A / PH 65.2 | A weicht selbst von PH ab: PH 65.2 nennt fix „20 %", Atlas staffelt 10/20/30. Übernehme Atlas, weil pädagogisch sinnvoller (Schonung auf L1, Schmerz auf L3). Im JSON-Export als latePenaltyOverride, default bleibt ECONOMY.latePenaltyPercentPerHour = 0.2. |
| Miete Fahrzeug (€/Tag) | 0 | 200 | 500 | A | =A. PH erwähnt Miete nur lose (Kap 10.7 „Miete ab bestimmten Levels"). 0/200/500 macht Fahrzeugauswahl strategisch ab L2 (großer LKW = teurer, aber 3× Kapazität → lohnt nur bei Großauftrag). |
| Standkosten Fahrzeug (€/h) | 0 | 5 | 5 | PH 65.2 | =PH (5 €/h). L1 ausgesetzt, weil Tutorial — sonst wirkt jede Pause wie eine Strafe. Ab L2 normal aktiv. |
| Standkosten Container (€/h Hafen) | n/a | n/a | 10 | PH 65.2 | =PH (10 €/h). Erst L3 relevant, weil Häfen erst L3 aktiv. |
| Min Ziel-Erlös (€) | 3.000 | 10.000 | 20.000 | A / S | =A. Mein Sanity-Check: L3 mit 6 parallelen Aufträgen × ~1.500 € durchschnittlich × 2 Auftragsrunden in 72h = 18.000 € Brutto. 20.000 € Min-Ziel ist also „muss Bonus mitnehmen, sonst knapp". Passt zu „Profi". |
| Zeitlimit (Sim-Stunden) | 24 | 48 | 72 | A / S | =A. 24h L1 ist genug für 1 Auftrag (4.5h Wien→Salzburg + Puffer). 48h L2 für 3 Aufträge mit Disposition. 72h L3 für 6+intermodal+Events. |
| Initial timeScale | 1× | 1× | 1× | PH 65.4 | Standard. Bearbeiter:in wechselt in der UI auf 4× oder 8× nach Bedarf. Kein Auto-Skip auf höhere Levels — bewusste Entscheidung sollen sie selbst treffen. |
| Bahn aktiv? | nein | nein | ja | A / PH 13.3 | =A. PH 13.3 sagt L3 „volle Grundlogik" inkl. Bahn. L1+L2 ohne Bahn vereinfacht Routing-Entscheidung erheblich (kein Mode-Choice). |
| Häfen aktiv? | nein | nein | ja | A / PH 13.3 | =A. Begründung wie Bahn — Schiffsankünfte als Auftragsquelle braucht didaktischen Boden, der erst auf L3 da ist. |
| Intermodale Aufträge? | nein | nein | ja | A / PH 13.3 | =A. Voraussetzung: Bahn + Häfen aktiv. |
| Minigames? | nein | optional | optional | PH 11.4 | =PH (optional ab L2). L1 nichts, weil schon mit Hauptmechanik beschäftigt. „Optional" heißt: per Lehrkraft-Config schaltbar (Phase 7). |
| Sichtbare Locations | nur visible_from_level=1 (Hauptstädte) |
bis Level 2 (+ Großstädte) | alle | PH 9.1 + Seed | Seed-Daten haben pro Location ein visibleFromLevel-Feld. Filter rendert nur sichtbare Marker — reduziert kognitive Last auf L1. Ab L3 alles sichtbar. |
Begründungen je Level (Erzählebene)
Level 1 — „Erster Auftrag":
Tutorial. Ein einziger Auftrag (Wien → Salzburg, 300 km), ein einziger
TRUCK_SMALL. BLINK_EXACT zeigt Start- und Zielort blinkend auf der Karte.
Events deaktiviert (Abweichung von Atlas — siehe Tabelle, Zeile Event-Wahrsch.).
Kontingent: 8.000 € + 1.000 € Auftrag − ~472 € Fahrkosten = klarer
positiver Abschluss bei Pünktlichkeit. Bonus „pünktlich" (+10 %) und
„keine Leerfahrt" (+20 %) spürbar, aber nicht überlebenswichtig.
Ziel: Vertrauen aufbauen, Mechanik verstehen, „ich kann das"-Erlebnis.
Level 2 — „Drei Aufträge gleichzeitig":
Erste echte Disposition. 3 parallele Aufträge in Mitteleuropa, alle 3
Fahrzeugtypen verfügbar. SHOW_REGION zeigt nur die Region des Zielorts —
Bearbeiter:in muss überlegen, welcher Ort innerhalb dieser Region passt.
Events bei 50 % der PH-Werte (Unfall 2.5 %/h, Schnee 5 %/h, Hafen 4 %/h).
Knappe 5.000 € Startbudget zwingt zu Effizienz. Zwei dumme Leerfahrten
machen die Bilanz schon negativ. Ziel: Strategie-Bewusstsein,
Fahrzeug-zu-Auftrag-Match lernen.
Level 3 — „Profi-Disposition Europa":
Volles Modell. 6 parallele Aufträge, intermodale Ketten (LKW → Zug → LKW),
Häfen mit Schiffsankünften (= zusätzliche Container, neue Aufträge),
Bahn-Dijkstra über 5 Knoten. NONE — alle Orte selbst suchen.
Events bei 100 % der PH-Werte. 2.500 € Startbudget verzeiht keinen
Patzer. Ziel: Wer hier mit positivem Kontostand abschließt, hat
das Modell verstanden — auch wirtschaftlich.
2. Verbindliche Konstanten (aus Kap 65 — gleich für alle Level)
Über
LogistikEngine.VEHICLE_DEFAULTS,.ECONOMY,.EVENT_RULESbereits implementiert. Nicht überschreiben pro Level, nur via Tuning-Iteration mit Atlas-Approval.
Fahrzeuge
| Typ | Speed | Kapazität | €/km | €/h | Lade min | Entlade min |
|---|---|---|---|---|---|---|
| TRUCK_SMALL | 70 km/h | 1 Cont. | 1.20 | 25 | 5 | 5 |
| TRUCK_LARGE | 60 km/h | 3 Cont. | 2.50 | 45 | 8 | 8 |
| TRAIN | 90 km/h | 20 Cont. | 8.00 | 150 | 20/Block | 20 |
Aufträge
| Typ | Basiswert | Container | Frist (h) |
|---|---|---|---|
| Standardauftrag | 1.000 € | 1–3 | distanceKm/60 × 1.5 |
| Eilauftrag | 1.500 € | 1–2 | distanceKm/70 |
Bonus / Malus
- Pünktlich: +10 %
- Optimal (keine Leerfahrt): +20 %
- Strafe pro Stunde Verspätung: 20 % Auftragswert (per Level via
latePenaltyOverrideanpassbar)
Events (pro Stunde, Basis-Werte)
| Event | Basis-Wahrsch./h | Wirkung |
|---|---|---|
| TRAFFIC_ACCIDENT | 5 % | Speed × 0.5 für Event-Dauer |
| SNOW (Alpen) | 10 % | Speed × 0.7 |
| PORT_DELAY | 8 % | Ladezeit × 1.5 |
Pro Level multipliziert mit eventProbabilityMultiplier (0/0.5/1.0).
Zeit
- 1 Tick = 250 ms Echtzeit
- 1× = 1 Sim-Min/Echt-Sek
- 4× = 4 Sim-Min/Echt-Sek
- 8× = 8 Sim-Min/Echt-Sek (je nach Stabilität)
Bahnnetz (aus lg-railnet.json, von Atlas bestätigt 01:45)
Knoten: Wien, München, Hamburg, Rotterdam, Paris (5) Kanten:
- Wien ↔ München: 400 km
- München ↔ Hamburg: 800 km
- Hamburg ↔ Rotterdam: 500 km
- Paris ↔ Rotterdam: 520 km (durationMinutes: 347)
- Paris ↔ München: 820 km (durationMinutes: 547)
Test-Pfade für Dijkstra:
- Wien → Hamburg via München = 400 + 800 = 1.200 km
- Wien → Paris via München = 400 + 820 = 1.220 km
- Wien → Rotterdam (kürzer): via München+Hamburg = 400+800+500 = 1.700 km (vs. via Paris = 400+820+520 = 1.740 km)
3. Strategie-Profile (für Headless-Runner)
| Strategie | Verhalten | Erwartung L1 | Erwartung L2 | Erwartung L3 |
|---|---|---|---|---|
noop |
Keine Aktion. Zeit läuft ab, kein Erlös. | fail | fail | fail |
naive |
Erstes Fahrzeug, nächster Auftrag, FIFO. | muss schaffen (≥ 90 %) | knapp / fail | fail (sicher) |
greedy |
Höchster Auftragswert zuerst, größtes Fahrzeug. | schafft locker | schafft (~ 70 %) | fail / knapp |
optimal |
Orakel — perfekte Routenplanung, keine Leerfahrten. | schafft perfekt | schafft mit Reserve | muss schaffen (~ 90 %) |
noop ist Test-Strategie für Phase 0: Beweist, dass Runner-Vertrag,
Seed-RNG, Ergebnisstruktur und Loop-Steuerung funktionieren — ohne
dass engine.tick implementiert sein muss.
Akzeptanzkorridor (formal)
runLevel(1, *, 'naive') → success: true, endBalance ≥ +1.000 €
runLevel(1, *, 'greedy') → success: true, endBalance ≥ +2.000 €
runLevel(1, *, 'optimal') → success: true, endBalance ≥ +3.000 €
runLevel(2, *, 'naive') → success: false (oder negativ)
runLevel(2, *, 'greedy') → success: true, endBalance ≥ 0
runLevel(2, *, 'optimal') → success: true, endBalance ≥ +5.000 €
runLevel(3, *, 'naive') → success: false (sicher)
runLevel(3, *, 'greedy') → success: false oder knapp negativ
runLevel(3, *, 'optimal') → success: true, endBalance ≥ 0
Pro Level werden 5 Seeds (1–5) gespielt. Kriterium gilt für Median,
nicht Worst-Case (sonst kein Spielraum für Pech). Ergebnisse landen in
headless-runner.html-Tabelle.
4. Tuning-Protokoll
Wenn nach Phase 2/3 die Akzeptanz-Korridore nicht erreicht werden:
- Erst prüfen, ob die Strategie wirklich „naive"/„optimal" ist — schlechte Strategien lassen Level-Werte gut aussehen.
- Dann an einem Parameter pro Iteration drehen. Nie mehrere gleichzeitig — sonst nicht messbar.
- Jede Tuning-Änderung mit kurzem Eintrag im §8 Änderungslog dokumentieren.
- Bei Strukturveränderungen (Hilfestufe oder Fahrzeuge) Atlas-Review anfragen, weil dann Lehrplan-Anker möglicherweise nicht mehr passen.
5. JSON-Repräsentation für game_levels.params
{
"level_1": {
"startBudget": 8000,
"maxActiveContracts": 1,
"availableVehicleTypes": ["TRUCK_SMALL"],
"vehicleCount": 1,
"deadlineMultiplier": 1.5,
"defaultHintMode": "BLINK_EXACT",
"eventProbabilityMultiplier": 0,
"latePenaltyOverride": 0.10,
"rentalCostPerDay": 500,
"idleCostPerHour": 0,
"containerStandCostPerHour": 0,
"minTargetEarnings": 3000,
"timeLimitHours": 72,
"initialTimeScale": 1,
"railEnabled": false,
"portsEnabled": false,
"intermodalEnabled": false,
"minigamesEnabled": false,
"visibleLocationLevel": 1
},
"level_2": {
"startBudget": 5000,
"maxActiveContracts": 3,
"availableVehicleTypes": ["TRUCK_SMALL", "TRUCK_LARGE", "TRAIN"],
"vehicleCount": 3,
"deadlineMultiplier": 1.2,
"defaultHintMode": "SHOW_REGION",
"eventProbabilityMultiplier": 0.5,
"latePenaltyOverride": 0.20,
"rentalCostPerDay": 200,
"idleCostPerHour": 5,
"containerStandCostPerHour": 0,
"minTargetEarnings": 10000,
"timeLimitHours": 48,
"initialTimeScale": 1,
"railEnabled": false,
"portsEnabled": false,
"intermodalEnabled": false,
"minigamesEnabled": true,
"visibleLocationLevel": 2
},
"level_3": {
"startBudget": 2500,
"maxActiveContracts": 6,
"availableVehicleTypes": ["TRUCK_SMALL", "TRUCK_LARGE", "TRAIN"],
"vehicleCount": 5,
"deadlineMultiplier": 1.0,
"defaultHintMode": "NONE",
"eventProbabilityMultiplier": 1.0,
"latePenaltyOverride": 0.30,
"rentalCostPerDay": 500,
"idleCostPerHour": 5,
"containerStandCostPerHour": 10,
"minTargetEarnings": 20000,
"timeLimitHours": 72,
"initialTimeScale": 1,
"railEnabled": true,
"portsEnabled": true,
"intermodalEnabled": true,
"minigamesEnabled": true,
"visibleLocationLevel": 3
}
}
6. INSERT-SQL für game_levels
Schema (von Atlas bestätigt 01:45): id, game_id, level_name, scenario, params, sort_order, created_at, updated_at. Keine eigene
Spalte level_name_easy — die Leichte-Sprache-Variante wandert ins
params-JSON als levelNameEasy (UI liest beide Felder, pickText() wählt).
Hinweis: Atlas hat die drei Logistik-Level am 2026-04-20 01:45 bereits eingespielt. Diese SQLs dienen als Referenz / für Reset.
-- Logistik Europa — 3 Level-Konfigurationen (Phase 0)
-- Voraussetzung: module_info-Eintrag 'logistik' existiert
-- created_at / updated_at werden von MySQL-Default gesetzt
INSERT INTO `game_levels`
(`game_id`, `sort_order`, `level_name`, `scenario`, `params`)
VALUES
(
'logistik', 1, 'Lernen', 'logistik_level_1',
JSON_OBJECT(
'levelNameEasy', 'Erster Auftrag',
'startBudget', 8000,
'maxActiveContracts', 1,
'availableVehicleTypes', JSON_ARRAY('TRUCK_SMALL'),
'vehicleCount', 1,
'deadlineMultiplier', 1.5,
'defaultHintMode', 'BLINK_EXACT',
'eventProbabilityMultiplier', 0,
'latePenaltyOverride', 0.10,
'rentalCostPerDay', 0,
'idleCostPerHour', 0,
'containerStandCostPerHour', 0,
'minTargetEarnings', 3000,
'timeLimitHours', 24,
'initialTimeScale', 1,
'railEnabled', FALSE,
'portsEnabled', FALSE,
'intermodalEnabled', FALSE,
'minigamesEnabled', FALSE,
'visibleLocationLevel', 1
)
),
(
'logistik', 2, 'Übung', 'logistik_level_2',
JSON_OBJECT(
'levelNameEasy', 'Drei Aufträge',
'startBudget', 5000,
'maxActiveContracts', 3,
'availableVehicleTypes', JSON_ARRAY('TRUCK_SMALL', 'TRUCK_LARGE', 'TRAIN'),
'vehicleCount', 3,
'deadlineMultiplier', 1.2,
'defaultHintMode', 'SHOW_REGION',
'eventProbabilityMultiplier', 0.5,
'latePenaltyOverride', 0.20,
'rentalCostPerDay', 200,
'idleCostPerHour', 5,
'containerStandCostPerHour', 0,
'minTargetEarnings', 10000,
'timeLimitHours', 48,
'initialTimeScale', 1,
'railEnabled', FALSE,
'portsEnabled', FALSE,
'intermodalEnabled', FALSE,
'minigamesEnabled', TRUE,
'visibleLocationLevel', 2
)
),
(
'logistik', 3, 'Profi', 'logistik_level_3',
JSON_OBJECT(
'levelNameEasy', 'Profi-Disposition',
'startBudget', 2500,
'maxActiveContracts', 6,
'availableVehicleTypes', JSON_ARRAY('TRUCK_SMALL', 'TRUCK_LARGE', 'TRAIN'),
'vehicleCount', 5,
'deadlineMultiplier', 1.0,
'defaultHintMode', 'NONE',
'eventProbabilityMultiplier', 1.0,
'latePenaltyOverride', 0.30,
'rentalCostPerDay', 500,
'idleCostPerHour', 5,
'containerStandCostPerHour', 10,
'minTargetEarnings', 20000,
'timeLimitHours', 72,
'initialTimeScale', 1,
'railEnabled', TRUE,
'portsEnabled', TRUE,
'intermodalEnabled', TRUE,
'minigamesEnabled', TRUE,
'visibleLocationLevel', 3
)
);
7. Konsistenz-Checks (durch Atlas 01:45 abgeschlossen)
- ✅ Bahnnetz-Distanzen: Paris↔Rotterdam=520, Paris↔München=820 (bestätigt, in §2 eingearbeitet)
- ✅ Event-Wahrsch. L1=0: Genehmigt
- ✅
game_levels-Schema: Spalten geklärt, INSERT in §6 angepasst - ✅
level_name_easy: Existiert nicht, wandert ins params-JSON
8. Änderungshistorie
- 2026-04-20 01:50 v0.3 — Atlas-Doku-Korrekturen: Bahnnetz-Distanzen
Paris↔Rotterdam=520/Paris↔München=820 ergänzt, INSERT-Schema
angepasst (
scenariostattlevel_name_easy,levelNameEasyins params-JSON), Konsistenz-Checks abgehakt - 2026-04-20 v0.2 — Rationale-Spalte je Parameter, INSERT-SQLs,
Konsistenz-Checks an Atlas,
eventProbabilityMultiplierals Multiplikator umstrukturiert, Container-Standkosten ergänzt,noop- Strategie zur Strategieliste - 2026-04-20 v0.1 — Erste Fassung (Logistik)