Altstand nach old_version/ eingefroren (untracked). Monolith ersetzt durch main.py + Paket loader_game/. Neu: run.sh/run.command mit venv+requirements, .gitignore, docs/ANALYSE.md, 35 Tests. Secrets entfernt. Gelöscht: loader_game.py, qr_scan*.py, INSTALL_README.txt.

This commit is contained in:
Project Bridge committed 2026-11-01 10:25:13 +00:00
1 parent 6a2c891c3e
commit de550e1d4d
24 files changed
+2269 -1008

No files matched your search

+84
View File
@@ -0,0 +1,84 @@
# Analyse Altstand (01.11.2026)
Grundlage: `old_version/loader_game.py` (656 Zeilen), `old_version/qr_scan.py`
(188 Zeilen), `old_version/qr_scan_old.py` (160 Zeilen),
`old_version/case_sequences.txt` (6 Sequenzen).
## 1. Wie der Altstand funktioniert hat
```
qr_scan.py ──► last_user.txt ──► subprocess: python loader_game.py
│ │
└ Kamera + pyzbar + HTTP-API └ pygame-Spiel, ein Durchgang, dann sys.exit()
```
* **`qr_scan.py`**: öffnet die Webcam, liest QR-Codes, fragt eine externe API
(`hag.putschgi.ch/.../qr.php?action=verify_hash`) nach Gültigkeit und Name,
schreibt `mail`/`name` in `last_user.txt` und startet das Spiel als
Unterprozess. Danach wieder Scanner – so kam der nächste Spieler dran.
* **`loader_game.py`**: ein Durchgang. Ein Koffer läuft von rechts ein; der
Spieler kann ihn an die Barriere snappen (Space/A), drehen (←/X) oder auf
einen anderen Koffer stapeln (↑/Y). Kollidiert er ungebremst, wird er mit
einer Lücke eingereiht. Ist die Barriere hinter dem Heck, ist Schluss.
Punktzahl = belegte Fläche im Auflieger, wird per `curl` an
`submit_points.php` geschickt.
* **`case_sequences.txt`**: 6 Koffersequenzen, Zeilen `breite,höhe,rotiert`,
Sequenzen durch Leerzeilen getrennt. Das Spiel zieht sich zufällig eine.
## 2. Gefundene Fehler und Risiken
| # | Fund | Wirkung | Status im Neustand |
| --- | --- | --- | --- |
| 1 | **Secrets im Quellcode**: `API_SECRET = "ddlK…"` in `qr_scan.py`, `api_key=hag-trailer-8051` in `loader_game.py` | Zugangsdaten liegen in der Git-Historie | entfernt; Endpunkt + Schlüssel nur noch per `HAG_SCORE_URL` / `HAG_SCORE_API_KEY` aus der Umgebung → **alte Schlüssel rotieren** |
| 2 | `pygame.event.set_grab(True)` | Maus/Tastatur werden gefangen; auf macOS unangenehm, Esc kommt nicht mehr durch | entfernt |
| 3 | Feste 1920×1080-Vollbildannahme | MacBook (Retina/1440×900) → Fenster grösser als der Bildschirm | virtuelle Auflösung + `pygame.SCALED`, Fenster/`--fullscreen`, F11 |
| 4 | Windows-Annahmen: `subprocess.run(["python", …])`, `pygetwindow`/`pyautogui`, `python.exe` in der Install-Doku | läuft so nicht auf macOS | kein Subprozess mehr, Scanner gestrichen |
| 5 | **Doppelte Drehlogik**: `Case.rotated` **und** getauschte Masse; `draw()` skaliert bei `rotated` auf `(height, width)` und dreht zusätzlich um 90° | verzerrte/falsch gedrehte Koffer, schwer nachvollziehbar | nur noch `units_w`/`units_h`; Grafik wird einmalig passend erzeugt |
| 6 | Zufällige Zusatzdrehung in `Case.__init__` (`random.choice([True, False])`) | die „Next"-Vorschau zeigt etwas anderes als der Koffer, der wirklich kommt | Zufallsdrehung entfernt, Vorschau stimmt jetzt |
| 7 | `can_place_on_top()` und `animate_place_on_top()` prüfen **unterschiedliche** Regeln (Barriere-Ausrichtung fehlt in der Animation) | „OnTop möglich!" wird angekündigt, dann landet der Koffer woanders oder gar nicht | eine einzige Regel: `Trailer.find_platform()` |
| 8 | Zwei verschiedene Fehlschlag-Pfade (`fail_snap` einmal mit `sound_place`, einmal mit `sound_fail`) | inkonsistentes Feedback, verwirrende Punktevergabe | ein Pfad (`_stack_with_gap`), definierter Sound + Shake |
| 9 | Blockierende Animationsschleifen (`for` + `pygame.time.delay` mitten im Event-Loop) | Events stauen sich, keine `dt`-Basis, Ruckler; 120-fps-Ziel war so nicht erreichbar | Animationen laufen über `elapsed/duration` im Update, 60 fps |
| 10 | `Case.move()` startet den Roll-Sound und kehrt zurück; Sound hängt an `Channel(1)` | erster Frame ohne Bewegung; Kanal-Kollisionen mit anderen Sounds | Roll-Sound beim Spawn, Stopp beim Platzieren, eigener Kanal |
| 11 | Blankes `except:` an 6 Stellen (Bilder, Sequenzen, Punkte-Upload) | Fehler bleiben unsichtbar, z. B. Tippfehler in `case_sequences.txt` fallen still auf die Notfall-Sequenz zurück | gezielte Exceptions + Warnungen beim Start |
| 12 | `last_user.txt` wird mit `utf-8-sig` geschrieben und mit `utf-8` gelesen | BOM landet im Spielernamen; Datei liegt im Programmordner | `runtime/last_player.json`, Ordner ist in `.gitignore` |
| 13 | `calculate_load_percent()` summiert die Fläche **aller** Koffer, auch der aufgesetzten | Beladung ist „gefühlt" zu hoch; `min(100, …)` verdeckt das nur | bewusst beibehalten (Spielbalance), aber dokumentiert und getestet |
| 14 | Toter/doppelter Code: `global current_case` zweimal in `draw_game`, `can_use_on_top` in `spawn_case`, `transition_counter`, `state`/`PLAYING`, `entry_angle` (immer 0), `frame_color`, `game_started` | unnötige Verwirrung | entfernt |
| 15 | Kein Neustart möglich: Spiel endet mit `sys.exit()` | jeder Spieler braucht einen neuen Prozess (Aufgabe des Scanners) | Spiel ist eine Klasse; Aufrufer entscheidet (Neustart/Beenden) |
| 16 | `cap = cv2.VideoCapture(0)` in der Endlosschleife, `cv2.namedWindow()` pro Frame | Kamerahandle-Leak, Flackern | mit dem QR-Scanner entfallen (pyzbar/opencv nicht mehr nötig) |
| 17 | Keine `requirements.txt`, kein venv, Installation nur als Windows-Befehlskette | auf dem Mac nicht reproduzierbar | `requirements.txt` + `run.sh` (venv + Auto-Install) |
| 18 | Spieler kam nur über die API, ohne Server kein Spielstart | Abhängigkeit von einem fremden Server | Name kommt künftig vom Controller (Schritt 2) |
## 3. Was der Neustand anders macht
* **Struktur statt Monolith**: `main.py` + Paket `loader_game/` mit je einem
Modul pro Aufgabe (siehe `loader_game/__init__.py`).
* **Logik ohne pygame**: `case.py`, `trailer.py`, `sequences.py` lassen sich
ohne Fenster testen (35 Tests).
* **Zeitbasiert**: alles rechnet in Sekunden (`dt`), nicht in Frames.
* **Eine Wahrheit für Regeln**: Stapelregeln leben ausschliesslich in
`Trailer`, Zeichnen ausschliesslich in `scene.py`.
* **Konfiguration an einer Stelle**: `config.py` (Masse, Tempi, Farben, Texte).
* **Eingabe abstrahiert**: `input_map.py` bildet Tastatur **und** Controller auf
`Action.PRIMARY/LEFT/RIGHT/UP/DOWN/BACK` ab – die Basis für die
Controller-Namenseingabe.
* **Nur noch eine Abhängigkeit**: `pygame` (kein numpy, kein opencv, kein
pyzbar, kein requests, kein pyautogui).
## 4. Bewusst *nicht* geändert
* Spielmechanik und Balance (Tempo 720/240 px/s entspricht den alten 6/2 px pro
Frame bei 120 fps; Snap-Schwelle, Lücken-Logik, Beladungsformel).
* `case_sequences.txt` (Datenbestand bleibt unverändert).
* Grafiken, Sounds, Fonts.
* Der Punkte-Upload existiert weiter, ist aber ohne konfigurierten Endpunkt
stumm geschaltet.
## 5. Offene Punkte (nächste Schritte)
1. **Controller-Namenseingabe**: Buchstabe für Buchstabe per D-Pad/Stick,
alternative Eingabe über die Tastatur. Ersetzt `last_user.txt`/QR-Scanner.
2. **Mehrere Durchgänge ohne Prozess-Neustart** (Rückkehr zur Namenseingabe
statt Beenden).
3. Entscheiden, ob der Punkte-Upload wieder aktiv genutzt wird – und wohin.
4. `images/*.png` haben ein defektes sRGB-Profil (libpng-Warnung beim Laden,
rein kosmetisch).