# Level-Progression + Balance-Regel R-1

**Stand:** 2026-04-23
**Gilt fuer:** Logistik Europa (Modul 11)

Dieses Dokument erklaert (1) die neue **Innere Progression** in Level 1
und (2) die Balance-Regel **R-1 (Startbudget-Runway)** inkl. Analyse
warum Level 3 in seiner aktuellen Form dagegen verstoesst.

---

## 1. Level-1-Progression („Steigerung innerhalb eines Durchgangs")

Bisher: in L1 lief immer `Wien → Salzburg → Wien → Salzburg → ...`
immer genau 1 Auftrag parallel. Didaktisch stagniert — der Bearbeiter
lernt nach 2 Auftraegen nichts Neues.

### Neue Mechanik

Die Engine fuehrt jetzt einen Progression-Zaehler:
```js
game.progression = { round: 1, trucksBought: 0 }
```
`round` wird nach jeder abgeschlossenen Lieferung +1 erhoeht.

#### Dynamische Auftragsanzahl (nur L1)

| Runde | aktive Auftraege (max) | Idee |
|-------|------------------------|------|
| 1     | 1                      | Tutorial — 1 Option, keine Wahl |
| 2     | 2                      | Erste Wahl: direkt vs. away |
| 3     | 2                      | Einuebung |
| 4+    | 3                      | Parallel disponieren |

Implementiert in [engine.js](engine.js) via `_effectiveMaxContracts(game)`.
Der Config-Wert `cfg.maxActiveContracts` bleibt unveraendert (L1 = 1);
die Funktion ueberschreibt ihn dynamisch in L1.

#### Erweiterter Auftragspool

Bisher nur `Wien ↔ Salzburg`. Neu: Pool mit 5 Staedten (Wien,
Salzburg, Muenchen, Berlin, Hamburg — alle `visibleFromLevel=1`) und
Reward-Multiplikatoren, die Distanz-Unterschiede ausgleichen:

| Strecke              | Luftlinie | Mult | Rationale |
|----------------------|-----------|------|-----------|
| Wien ↔ Salzburg      | ~300 km   | 1.0  | Standard |
| Salzburg ↔ Muenchen  | ~150 km   | 0.8  | Kurz, weniger Wert |
| Wien ↔ Muenchen      | ~430 km   | 1.3  | Mittel |
| Muenchen ↔ Berlin    | ~620 km   | 1.5  | Weit |
| Wien ↔ Berlin        | ~680 km   | 1.7  | Sehr weit |

Der Pool wird **staffelweise** freigeschaltet:
- Runde 1-2: nur `mult ≤ 1.0` (sanfter Einstieg)
- Runde 3-4: bis `mult ≤ 1.4` (Mittelweite)
- Runde 5+: voller Pool

#### Away-Bonus: „direkt" vs. „woanders"

Bei 2-3 parallelen Auftraegen bevorzugt der Generator:
- **Erster Auftrag** aus Vehicle-Location (Bearbeiter hat sofort was zu tun)
- **Zweiter/Dritter** aus anderen Staedten (`awayBonus: true` Flag)

Der Spieler muss entscheiden: fahre direkt los (geringer Gewinn, keine
Leerfahrt) ODER akzeptiere den Away-Auftrag (hoeherer Reward-Mult,
aber Leerfahrt zum Pickup noetig). Implementiert wirtschaftlich sinnvoll
durch den Reward-Multiplikator — der entschaedigt die erwartete Leerfahrt.

#### Auto-Buy-Kleiner-LKW

L1 ist Tutorial — der Spieler lernt Flottenvergroesserung als Konzept.
Sobald `balance ≥ 15.000 €`, kauft die Engine automatisch einen weiteren
`TRUCK_SMALL` (Kosten: 5.000 €). Max 3 Fahrzeuge auf L1.

Implementiert in `_maybeAutoBuyVehicle(game)`. Feuert im Anschluss an
jede Followup-Generation.

Ab L2 wird das manuell (UI-Button, noch zu bauen) — siehe Offene Punkte.

---

## 2. Balance-Regel R-1: Startbudget-Runway

### Regel-Wortlaut

> Ein Level muss dem Bearbeiter mindestens **1.5 Tage „Luft"**
> fuer Entscheidungen geben, bevor die Fixkosten das Startbudget
> aufbrauchen. Formal:
>
> ```
> startBudget ≥ 1.5 × ( rentalCostPerDay × vehicleCount
>                    + idleCostPerHour   × vehicleCount × 12 )
> ```
>
> Der Faktor 12 beim Idle kommt von einer Annahme „im Schnitt halbtags
> ungenutzt" — konservativ, aber nachvollziehbar.

### Warum 1.5 Tage?

- Unter 1 Tag: Spieler ist in Runde 1 schon im Minus, bevor Auftrag
  abgeschlossen — reiner Frustrations-Einstieg.
- 1 Tag genau: knapp, wenn ein Event den ersten Auftrag verzoegert ist
  der Puffer weg.
- 1.5 Tage: genug Puffer fuer 1 Fehltrip + strategische Entscheidung
  (Fahrzeug stehen lassen statt fahren z.B.).
- 2+ Tage: zu bequem, fordert nicht zum wirtschaftlichen Denken auf.

1.5 ist der Kompromiss.

### Pruefung der aktuellen Levels

| Level | startBudget | vc | rental | idle | Required R-1 | Ergebnis |
|-------|-------------|----|--------|------|--------------|----------|
| L1    | 8.000       | 1  | 0      | 0    | 0            | ✅ PASSES |
| L2    | 5.000       | 3  | 200    | 5    | 1.170        | ✅ PASSES |
| L3    | **2.500**   | 5  | 500    | 5    | **4.650**    | ❌ FAILS (−2.150) |

### L3-Diagnose im Detail

Taegliche Fixkosten auf L3:
- Miete: 5 × 500 = **2.500 €/Tag**
- Idle (50 % Leerlauf): 5 × 5 × 12 = **300 €/Tag**
- Summe: **2.800 €/Tag** realistisch, **3.100 €/Tag** worst case

Runway:
- `2.500 € / 2.800 €/Tag = 0.89 Tage` (realistisch)
- `2.500 € / 3.100 €/Tag = 0.81 Tage` (worst case)

Der Bearbeiter wird in **Stunde 20** schon in den roten Bereich rutschen,
bevor der erste Profit-Auftrag (ca. Stunde 10-14) sein volles Netto
eingebracht hat — das Zeit-Profil ist feindselig.

Gleichzeitig ist das Min-Ziel 20.000 € nach 72 h erreichbar (meine
Python-Simulation: ≈ 28.000 € Endbilanz bei optimalem Spiel), aber der
Puffer ist null — jedes Event zieht die Bilanz ins Minus.

### Vorgeschlagener L3-Fix (muss von Atlas abgesegnet werden)

**Option A (minimal):** `startBudget: 2500 → 5000`
- Erfuellt R-1 knapp (benoetigt 4.650).
- Aendert didaktische Zielstellung nicht.
- +2.500 € = ~2.5 Standard-Auftraege Puffer.

**Option B (moderat):** `startBudget: 2500 → 5000 UND vehicleCount: 5 → 4`
- Reduziert Fixkosten auf 4 × 500 = 2.000/Tag + 4 × 5 × 12 = 240 → 2.240/Tag.
- R-1 fordert `1.5 × 2.240 = 3.360` — 5.000 passt locker.
- Weniger Fahrzeuge = einfacher zu ueberblicken.

**Option C (strukturell):** `rentalCostPerDay: 500 → 300`
- Fixkosten fallen auf 1.800 + 300 = 2.100/Tag.
- R-1 fordert 3.150 — 2.500 schafft's immer noch nicht.
- Kombiniert mit `startBudget 4000`: 4.000 ≥ 3.150 → ok.

**Meine Empfehlung:** Option A (nur Budget hoch auf 5.000 €). Minimal-invasiv,
aendert die „Profi"-Identitaet von L3 nicht — er bleibt knapp, aber nicht
unfair. Die anderen Hebel (Miete, Strafen, Events) spuert der Spieler
dann in Runde 2-3, nicht in Runde 1.

### Laufzeit-Check als Invariante

Vorschlag an Atlas: Beim `loadContent`-Aufruf der Engine die Regel
automatisch pruefen und einen `console.warn` ausgeben, wenn ein Level
R-1 verletzt. Optional: bei `strictMode` (Test-Lauf) hart werfen.

```js
// Vorschlag (pseudo):
function _validateLevelConfig(cfg) {
  const daily = (cfg.rentalCostPerDay || 0) * (cfg.vehicleCount || 1)
              + (cfg.idleCostPerHour  || 0) * (cfg.vehicleCount || 1) * 12;
  const needed = 1.5 * daily;
  if ((cfg.startBudget || 0) < needed) {
    console.warn(`[Logistik] Level verletzt R-1: startBudget=${cfg.startBudget}, needed≥${needed.toFixed(0)}`);
  }
}
```

---

## 3. Offene Punkte

- **UI-Anzeige der Runde** auf L1 („Runde 3 — 2 Auftraege, 1 davon woanders") —
  noch nicht gebaut. Derzeit sieht der Spieler die Steigerung nur indirekt
  (Anzahl Cards auf der Auftragsliste waechst).
- **Manueller Fahrzeugkauf** ab L2 — Buy-Button in der Fahrzeug-Liste.
  Architektur: `LogistikEngine.buyVehicle(game, mode)` Export + UI-Dialog
  mit Preisliste + Balance-Check.
- **Atlas-Review** der Regel R-1 inkl. moeglicher L3-Balance-Anpassung.
  Siehe Mail `2026-04-23-1730-logistik-l3-balance-und-rule-r1.md`.
- **Away-Bonus-Badge** in der UI: Contract-Cards mit `awayBonus: true`
  sollten visuell markiert sein (z.B. ↗ Icon + „Leerfahrt noetig"-Text).
