Files
Laby/docs/03_Automatisierung_CI.md
T
Lila-Kuh ea2a2f2cbf
Aegis CI / audit (push) Failing after 1m7s
Aegis CI / selfplay (push) Skipped
Aegis CI / perf (push) Failing after 3s
Aegis CI / report (push) Failing after 2s
Initial project version
2026-08-29 01:45:19 +02:00

8.1 KiB
Raw Blame History

03 Automatisierung, Self-Play, Performance & CI

Wie die Test-Suite aufgerufen wird, was selfplay.mjs und perf-test.mjs konkret messen und wie die Stufen in eine CI-Pipeline eingebunden werden.


1. Aufruf der Skripte

1.1 npm-Scripts (aus package.json)

Script Aufruf Parameter (optional)
npm run audit node tests/audit.mjs
npm run selfplay node tests/selfplay.mjs [ep] ep = Episoden (Default 10)
npm run perf node tests/perf-test.mjs [ms] [towers] ms = Dauer (Default 15000), towers = Anzahl (Default 50)
npm run report node tests/report.mjs
npm test Audit + Self-Play + Perf
npm run setup npx playwright install chromium

1.2 Direkt-Aufruf (nützlich für CI / Skripte)

node tests/selfplay.mjs 50          # 50 Episoden
node tests/perf-test.mjs 30000 80  # 30 s messen, 80 Türme
node tests/audit.mjs                # Standard-Audit
node tests/report.mjs               # Report erzeugen

1.3 Exit-Codes (Übersicht)

Skript exit 0 exit ≠ 0
audit.mjs alle Assertions grün ≥ 1 Fehler → 1, Crash → 2
selfplay.mjs (liefert immer 0)
perf-test.mjs FPS ≥ Zielwert (55) FPS < Zielwert → 1
report.mjs (liefert immer 0)

2. Self-Play (tests/selfplay.mjs)

2.1 Zweck

Deterministischer Bot spielt Level 0 (Standard) oder ein beliebiges Level durch und misst:

  • Win-Rate über N Episoden
  • Durchschnittliche Kills, Leaks, Credits, Waves
  • Stabilität (keine JS-Page-Errors, keine Freeze-Detection)

2.2 Strategie des Bots

1. Level laden (selectLevel, Default Level 0)
2. Credits setzen (setCredits → 999999)
3. Türme platzieren:
   - Alle Bau-Kacheln (grid==0) identifizieren
   - In Priorität: Sniper, Tesla, Quantum, Nano, Barrier, Holo, Grav, Mortar, Sniper
   - Je nach Credits: mehrere Runden × 5 Türme
4. Wave starten (startWave)
5. simulate() in 1/60-Schritten bis victory/defeat
6. Episoden-Log: kills, leaks, credits, waves, duration
7. Aggregation: Win-Rate, mean, std, min, max

2.3 Ausgabe (selfplay-results.json)

{
  "timestamp": "2026-08-28T19:30:00.000Z",
  "episodes": [
    {
      "placed": 40,
      "towerCount": 40,
      "maxKills": 120,
      "waveReached": 8,
      "totalUpgrades": 64,
      "finalState": "victory",
      "finalKills": 120,
      "finalLeaked": 0,
      "finalLives": 20
    }
  ],
  "summary": {
    "total": 10,
    "wins": 8,
    "losses": 2,
    "winRate": 80.0,
    "avgKills": 118,
    "avgWaves": 7.4
  }
}

2.4 Playwright-Fallstricke (Self-Play)

Problem Lösung
window.TOWER_TYPESundefined TOWER_TYPES (bare Identifier)
window.placeTowerundefined placeTower (bare)
window.buySkillundefined buySkill (bare)
window.__AEGIS_DEBUG__ window.__AEGIS_DEBUG__

selfplay.mjs nutzt ausschließlich window.__AEGIS_DEBUG__ für State-Abfragen und bare Identifier für Aktionen.


3. Performance (tests/perf-test.mjs)

3.1 Zweck

Messen der Frames per Second (FPS) und Heap-Größe unter definiertem Lastprofil:

  • Level: schweres Level (Default Level 10, 11 Waves)
  • Türme: konfigurierbare Anzahl (Default 50, empfohlen 5080)
  • Dauer: konfigurierbare Messzeit (Default 15000 ms)

3.2 Vorgehen

1. Level laden (selectLevel(10))
2. Credits setzen (setCredits → 999999)
3. Türme platzieren (N Türme auf Bau-Kacheln, Priorität: schwerste zuerst)
4. Wave starten (startWave)
5. simulate() in 1/60-Schritten
6. FPS messen:
   - requestAnimationFrame in Page (window.__fps_frames++)
   - Alle 1000 ms: fps = frames / 1.0
   - Frame-Timing: performance.now() Differenz
7. Heap messen:
   - page.evaluate(() => performance.memory)
   - usedJSHeapSize / totalJSHeapSize / jsHeapSizeLimit
8. Ergebnis: perf-result.json

3.3 FPS-Zielwert & Exit-Code

FPS Bewertung Exit-Code
≥ 55 Gut 0
4554 ⚠️ Verändert 1
< 45 Kritisch 1

3.4 Ausgabe (perf-result.json)

{
  "timestamp": "2026-08-28T19:45:00.000Z",
  "durationMs": 15000,
  "fps": 58,
  "targetFps": 55,
  "pass": true,
  "heap": { "usedMB": 48.3, "totalMB": 96.0 },
  "setup": {
    "levelIdx": 10,
    "levelName": "…",
    "placed": 50,
    "enemies": 40,
    "state": "playing"
  }
}

3.5 Bekannte Performance-Problemzonen (→ 06_Refactoring_Game.md)

Problem Wo sichtbar Empfehlung
Particles Game.particles wächst > 200 Object-Pooling + Max-Limit
Projectiles Game.projectiles > 100 Spatial-Partitioning
Holograms Game.holograms > 20 Batch-Rendering
Beam Game.beams > 10 Line-Rendering statt Circle
Enemy-Update O(n²) bei 20+ Feinden Spatial-Hash

4. CI-Workflows (empfohlen)

4.1 Stufen

Stufe Skripte Wann Dauer (ca.)
Pre-Commit npm run audit bei jedem Commit 3060 s
Nightly Audit + selfplay.mjs 50 + perf-test.mjs zeitplangetrieben 510 min
Release Audit + selfplay.mjs 500 + perf-test.mjs 30000 80 + Report Tagging 1530 min

4.2 GitHub-Actions-Beispiel (.github/workflows/ci.yml)

name: Test-Suite

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 2 * * *'   # Nightly 2:00 UTC

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install dependencies
        run: npm install

      - name: Install Playwright Chromium
        run: npx playwright install chromium

      # ⚠️ Aegis-Labyrinth.html muss als Secret/Artifact bereitgestellt werden
      - name: Download Game-Datei
        run: |
          echo "${{ secrets.AEGIS_GAME_BASE64 }}" | base64 -d > Aegis-Labyrinth.html

      - name: Run Audit
        run: npm run audit
        continue-on-error: true

      - name: Run Self-Play (10 Episoden)
        run: node tests/selfplay.mjs 10

      - name: Run Performance Test
        run: node tests/perf-test.mjs 15000 50

      - name: Generate Report
        run: node tests/report.mjs

      - name: Upload Artifacts
        uses: actions/upload-artifact@v4
        with:
          name: test-results
          path: |
            audit-result.json
            selfplay-results.json
            perf-result.json
            report.html
            audit-final.png

      # Audit muss grün sein für Main-Push
      - name: Check Audit Exit Code
        if: github.event_name == 'push' && contains(github.ref, 'refs/heads/main')
        run: |
          # audit-result.json nutzt "summary": { "pass", "fail", "total" }
          if [ ! -f audit-result.json ] || grep -q '"fail": [1-9]' audit-result.json; then
            echo "❌ Audit failed"
            exit 1
          fi

4.3 Game-Datei in CI bereitzustellen

Da Aegis-Labyrinth.html nicht im Repo liegt, muss sie in der CI als Secret oder Artifact bereitgestellt werden:

Methode Vorteil Nachteil
GitHub Secret (Base64) Einfach, sicher Base64-Konvertierung nötig
Release Artifact Versioniert Extra Schritt zum Herunterladen
Separates Private Repo Saubere Trennung Access-Token nötig

Empfehlung: GitHub Secret für einfache CI, separates Private Repo für Teams.


5. Troubleshooting

Symptom Ursache Lösung
❌ Aegis-Labyrinth.html nicht gefunden! Datei fehlt Datei in Projekt-Root kopieren
browserType.launch: Executable doesn't exist Playwright nicht installiert npx playwright install chromium
selfplay.mjs liefert immer 0 Bot verliert immer Level prüfen / mehr Credits / Türme
perf-test.mjs FPS < 45 Zu viele Entities Level/Türme reduzieren / Refactoring
report.html zeigt "no data" *-result.json fehlt Audit/Self-Play/Perf erst ausführen
Playwright evaluateundefined window.X statt X Bare Identifier verwenden (siehe 01_Debug_API.md)