# 12 — Prototyp: ratatui-Durchstich Type: prototype Status: resolved Blocked by: 11 ## Question Trägt das Screen-Modell aus 11, bevor sieben Bildschirme darauf gebaut werden? Der Umbau von `print_box` auf ratatui berührt jedes bestehende Modul (`items.rs`, `player.rs`, `armor.rs`, `weapon.rs` und deren `print_values`). Vorher ein billiger Durchstich: - Hauptmenü und Dungeon-Grid, mehr nicht. Ein Popup darüber, um das Stacking zu prüfen. - Das Grid als eigenes Widget mit direktem Buffer-Zugriff — die Stelle, an der es kein fertiges Widget gibt und die Empfehlung sich beweisen muss. - Bewegung über `KeyEventKind`-gefilterte Eingabe, damit der Windows-Fallstrick real getestet ist statt nur belegt. - Prüfen, ob `game/` beim Bauen ratatui-frei bleibt oder ob es sofort leckt. Bewusst nicht im Prototyp: Kampf, Inventar, Shop, Speichern, Mods. Ergebnis: lauffähiger Durchstich plus Urteil, ob das Modell trägt oder Ticket 11 nachgebessert wird. ## Answer **Urteil: Das Modell trägt.** Ticket 11 wird an genau einer Stelle nachgebessert, und die hatte nichts mit dem Screen-Stack zu tun. Der Prototyp liegt als Primärquelle auf dem Branch **`prototyp/12-ratatui-durchstich`** (`examples/durchstich.rs`, 342 Zeilen, `cargo run --example durchstich`). Er bleibt dort und kommt nicht nach `main` — Wegwerfcode, der eine Frage beantwortet hat. ### Die fünf Zusagen | Zusage | Befund | |---|---| | `mod game` bleibt ratatui-frei | **hält.** Null Treffer, und zwar ohne dass irgendwer aufpassen musste — die Trennung ergibt sich, wenn `Command` und Spiellogik zusammenliegen | | Dungeon-Grid als eigenes `impl Widget` | **trägt.** `buf[(x, y)].set_symbol(…)` ist der ganze Trick; kein fertiges Widget wurde vermisst | | `Vec` für Screens *und* Popups | **trägt.** Overlay-Flag entscheidet Rendern *und* Auto-Schließen, wie in 11 abgeleitet | | `Response` statt Stack-Mutation im Screen | **trägt.** Kein Borrow-Konflikt, weil der Screen den Stack nie sieht | | `KeyEventKind::Press`-Filter | **gemessen statt geglaubt** — siehe unten | Popup über Grid, `x` dann `j`: Der Zug zählt hoch und das Popup schließt sich in derselben Iteration. Die Regel „ein `Response::Command` schließt alle Overlays" ist damit real und kostet die eine Zeile, die in 11 dafür veranschlagt war. ### Der Windows-Fallstrick ist jetzt eine Zahl Der Bericht aus 06 führte ihn als Faktum, aber ungemessen. Der Prototyp zählt Tasten-Events ungefiltert **und** gefiltert mit. Ergebnis unter Windows 11: ``` Tasten 14 → Press 7 → Züge 7 ``` Jeder Tastendruck erzeugt zwei `Event::Key` (Press und Release). **Ohne den Filter würde jeder Schritt zwei Züge kosten** — und weil ein Zug befristete Modifikatoren tickt, wäre das kein kosmetischer Fehler, sondern doppelte Spielzeit. Der Filter arbeitet, `Press` und `Züge` laufen synchron. ### Zwei Befunde, beide nach Ticket 11 zurückgetragen **Gegen eine Wand zu laufen kostete einen Zug.** Aufgefallen beim Spielen, nicht beim Nachdenken: Der Zugzähler lief hoch, obwohl sich nichts bewegte — ein Gift-Debuff hätte getickt, weil jemand danebengetippt hat. Die Regel und `Outcome { TurnPassed, Rejected(&str) }` stehen jetzt in [Ticket 11](11-ui-geruest-screen-modell.md). **`Command` muss `Clone` sein**, sonst kommt das Kommando nicht aus `Confirm { on_yes }` heraus, ohne den Screen zu zerstören — im Prototyp brauchte das ein `std::mem::replace`. ### Was der Prototyp nicht beantwortet Kampf, Inventar, Shop, Speichern, Mods — bewusst ausgelassen. Ebenso ungetestet: die `Registries`-neben-`Game`-Entscheidung aus 07, weil der Durchstich gar keine Registries lädt. Der Borrow-Konflikt, den sie vermeiden soll, tritt erst auf, wenn `Game::execute` beides gleichzeitig braucht — das prüft erst die Implementierung.