user://tests liegt im echten Benutzerordner des Spiels, neben Spielstand,
Schlüssel und Aufzeichnungen des Spielers. TestFiles.remove_dir löschte
jeden Pfad, den es bekam; ein falsches Argument hätte echte Spielerdateien
gelöscht. Tests, die früh mit einem Laufzeitfehler abbrachen, ließen ihren
Ordner zurück (es lagen 15 solche Ordner im Benutzerordner).
- remove_dir vereinfacht den Pfad (simplify_path) und verweigert alles,
was nicht user://tests oder darunter liegt, mit push_error und ohne zu
löschen. Die Wurzel steht in der Konstante TestFiles.ROOT, die auch
temporary_dir benutzt.
- run_tests.gd entfernt user://tests zu Beginn von _run_all, vor dem
Fehlerzähler, aber nur wenn der Ordner existiert, weil Godot beim
Auflisten eines fehlenden Ordners einen Fehler meldet.
- CLAUDE.md sagt jetzt, dass die Tests unter user://tests schreiben und
nie die echten Dateien des Spielers anfassen.
Das Verweigern wurde einmal von Hand geprüft, mit einem Wegwerfskript auf
selbst angelegten Ordnern: Ohne Schutz löschte remove_dir sie, mit Schutz
bleiben sie samt Datei stehen und es erscheint ein Fehler, auch für einen
Geschwisterordner user://tests_probe und für user://tests/../…. Danach
waren die 15 Altlasten verschwunden und die Ausgabe fehlerfrei, auch ohne
vorhandenen Ordner user://tests.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
quit(1 if failed > 0 else 0) beendete einen Lauf mit "0 passed, 0 failed"
mit Code 0, zum Beispiel nachdem der Testordner umbenannt wurde oder alle
Tests falsch benannt waren. Die Prüfliste sah dann grün aus, ohne etwas
geprüft zu haben.
Der Läufer endet jetzt auch mit Code 1, wenn kein Test bestanden wurde.
Der Doc-Kommentar sagt das. Einmal von Hand geprüft: Mit einem Dateifilter,
den keine Testdatei erfüllt, meldete der alte Läufer 0 passed, 0 failed
mit Code 0, der neue endet mit Code 1.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Recording::from_bytes leitet start_tick aus der gespeicherten Startwelt
ab. Kein Test prüfte das: Setzte ein Fehler start_tick beim Dekodieren
auf 0, blieb er unbemerkt, und eine so dekodierte Aufzeichnung würde
beim erneuten Kodieren oder Verlängern abstürzen.
Der Test a_recording_can_start_in_the_middle_of_a_session kodiert die
Aufzeichnung jetzt, dekodiert sie wieder und prüft, dass sie beim
selben Takt endet und der ursprünglichen gleicht. Mit start_tick = 0
beim Dekodieren schlägt er fehl (Endtakt 70 statt 120).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Eine Welt kennt bis zu 16 Spieler, von denen höchstens vier online sind.
Kein Test lud eine Welt mit mehr als vier bekannten Spielern; ein Fehler,
der in check_restorable alle bekannten statt der Online-Spieler zählt,
hätte gültige Spielstände zurückgewiesen und blieb unbemerkt. Mit dem
Koop-Spiel in M1c sind mehr als vier bekannte Spieler der Normalfall.
Der neue Test baut die Welt über Befehle auf: 16 Schlüssel treten in
Vierergruppen bei, jede Gruppe außer der letzten geht wieder, bevor die
nächste kommt. from_snapshot muss dieselbe Welt liefern, load alle Spieler
offline. Mit dem Fehler (alle Spieler gezählt) schlägt der Test mit
Invalid("online players") fehl.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Mit `use std::{fs, process};` und anschließendem `fs::remove_file(…)`
kam bisher beides durch: Der Textwächter suchte `std::fs` wörtlich, und
Clippy kennt nur die aufgelisteten Funktionen. Globaler veränderlicher
Zustand über `static` mit Mutex, RwLock, OnceLock oder LazyLock wurde
auch nicht erkannt.
Neu in FORBIDDEN: `std::{` (gruppierte Importe verbergen dem Text die
Modulnamen) sowie Mutex, RwLock, OnceLock und LazyLock. Der Kern in
core/src nutzt nichts davon. Der Kommentar in core/clippy.toml behauptet
jetzt nur noch, was Clippy leistet, und CLAUDE.md nennt die Regel, dass
jeder std-Pfad in einer eigenen use-Zeile steht.
Der bestehende Selbsttest every_forbidden_term_is_detected deckt die
neuen Einträge ab.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
SaveStore.write_save verschob die geprüfte Temporärdatei direkt auf
world.grw. Godot löscht unter Windows ein vorhandenes Ziel, bevor es die
Datei verschiebt. Scheiterte die Verschiebung, zum Beispiel weil ein
Virenscanner world.grw.tmp festhielt, war der vorige Spielstand schon weg.
Jetzt wird der vorige Stand zuerst nach world.grw.bak verschoben, dann
rückt die Temporärdatei nach. Scheitert das, kommt der Stand aus der
Sicherung zurück; nach Erfolg wird die Sicherung gelöscht. Bleibt nach
einem Absturz zwischen den beiden Verschiebungen nur world.grw.bak übrig,
holt SaveStore sie beim Öffnen des Ordners zurück.
Neue Tests: ein offenes Handle auf world.grw.tmp, eine blockierte
Sicherung, die Wiederherstellung aus der Sicherung und dass eine
Sicherung einen vorhandenen Stand nie ersetzt.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
play.bat baut die Brücke und startet das Spiel. CLAUDE.md beschreibt
jetzt Kern, Brücke und Hülle mit allen Prüfbefehlen, die Versionsregel
für festgeschriebene Werte und die Zuschreibung in Commits. Ein
Fragebogen begleitet den ersten Probelauf.
Der erste Godot-Import in einem frischen Checkout stürzt beim Beenden
ab; play.bat wiederholt ihn deshalb einmal.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Lässt sich ein unbrauchbarer Spielstand nicht beiseitelegen, bleibt das
Speichern gesperrt, bis ein Stand erfolgreich geladen wird; so wird er
nie überschrieben. Ein gescheitertes automatisches Speichern zeigt
SAVE_FAILED, und jedes gescheiterte Speichern wird protokolliert.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Die Hauptszene verbindet Kern, Karte, Figur, Anzeige und Dateien:
fester Takt, Laufen, Stein schlagen in Reichweite, Speichern mit F5,
Laden mit F9, Pause mit Esc, automatisches Speichern alle zwei Minuten
und beim Schließen. Die Karte erscheint in Platzhalterfarben, bis in
M1d ein Stil gewählt ist. Ein Werkzeug macht Bildschirmfotos ohne
Handarbeit.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Ein Speicherstand ersetzt den vorigen nur noch, wenn er vollständig
geschrieben und zurückgelesen wurde. Ein nicht gespeicherter
Spielerschlüssel wird gemeldet, und set_aside_save meldet, wenn nichts
verschoben wurde.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Alle Texte stehen in einer Tabelle mit Deutsch und Englisch, die das
Spiel zur Laufzeit liest. Die Tasten werden in Code angelegt, nach
physischer Position. SaveStore verwaltet den Spielerschlüssel dieser
Installation, schreibt den Speicherstand über eine temporäre Datei,
legt unbrauchbare Stände beiseite und behält die zehn neuesten
Aufzeichnungen.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
TickClock rechnet Bildzeit in Takte um und holt nach einem Ruckler
höchstens fünf Takte auf. GridMotion bewegt einen Körper über das
Kachelraster und lässt ihn an Wänden entlanggleiten. Die Figur läuft
fünf Felder pro Sekunde, außerhalb des Gleichschritts.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Die GDExtension grovewright-bridge gibt den Kern als Klasse GwSession
an GDScript weiter: Befehle einreihen, einen Takt ausführen, Karte und
Inventar lesen, speichern, laden, aufzeichnen. Eine Panik im Kern macht
die Sitzung defekt, statt einen guten Speicherstand zu überschreiben.
Das Godot-Projekt bekommt einen Testläufer, der auch Laufzeitfehler als
Fehlschlag zählt.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Recording::step führt einen Takt aus und zeichnet ihn auf. Andere
Befehle als die ausgeführten können so nicht in eine Aufzeichnung
geraten. Aufzeichnung und Welt prüfen gegenseitig ihren Takt, und ein
beschädigter Startzustand fällt schon beim Einlesen auf.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Snapshots tragen jetzt eine Prüfsumme (Format 2). Beim Wiederherstellen
werden Zustände abgelehnt, die die Regeln nie erzeugen: zu große Takte,
mehr als vier Spieler online, doppelte Schlüssel, unmögliche
Wartezeiten. World::load ersetzt den öffentlichen Aufruf von
set_all_offline. Die festgeschriebenen Werte sind bewusst angepasst und
um einen Offline-Spieler und das Kartenformat ergänzt.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Clippy sperrt im Kern Hash-Tabellen, Uhr, Dateien, Threads und
Umgebung über aufgelöste Pfade, auch bei Aliasen und gruppierten
Importen. Der Textwächter kennt globalen veränderlichen Zustand und
Prozesse, prüft sich selbst, und ein neuer Test hält die Abhängigkeiten
des Kerns leer.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Zweiter von vier Plänen für Meilenstein 1. Acht Tasks härten den Kern
(Wächter, Speicherstand mit Prüfsumme und Prüfung beim Laden,
Aufzeichnung am Weltschritt) und bauen die GDExtension-Brücke sowie die
Godot-Hülle mit Takt, Figur, Karte, Anzeige, Texten, Dateien und
Startskript. Der Code im Plan ist vorab geprüft, jede Task-Grenze
einzeln.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deterministischer Rust-Kern mit Karte, Spielern, Inventar, Befehlen,
Speicherstand, Prüfsumme sowie Aufzeichnung und Wiederholung. 91 Tests
in Debug und Release, jede Task und der ganze Branch geprüft.
Closes#1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CLAUDE.md: Die Regeln für den Kern gelten für den Quelltext in
core/src/, nicht für die Tests. World::set_all_offline ist als einzige
Ausnahme von der Regel genannt, dass sich die Welt nur durch Befehle
ändert. Die Überlaufregel sagt jetzt, dass Taktzähler normal addiert
werden und Werte aus geladenen Dateien beim Laden geprüft werden. Ein
neuer Absatz erklärt, was bei einem Fehler in golden.rs zu tun ist.
KONZEPT.md: Das Konzept ist bestätigt. Hochladen, Branches, Issues und
Pull Requests sind freigegeben, und die veraltete Angabe, das Archiv
sei noch leer, ist entfernt. Die Reihenfolge der Rückkehrer steht
jetzt bei den Festlegungen statt bei den offenen Punkten.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Die neuen Tests in golden.rs vergleichen gegen einmal aufgezeichnete
Werte statt gegen eine zweite Berechnung im selben Build. So fällt
über Builds hinweg auf, wenn sich Regeln oder Formate unbeabsichtigt
ändern: der Startzustand der Welt, das Ende einer festen Befehlsfolge
und die Prüfsumme der Aufzeichnung. Die Fehlermeldung nennt die
Versionskonstante, die dann erhöht werden muss.
Der neue Test in world.rs deckt ab, dass überlange Namen auf beiden
Beitrittswegen gekürzt gespeichert werden: beim ersten Beitritt und
bei der Rückkehr.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Der Vergleich der Prüfsummen allein konnte nicht fehlschlagen, weil die
Startzahl selbst in die Prüfsumme eingeht. Jetzt vergleicht der Test
auch die Karten beider Welten.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Fünf Tests prüfen über tausende Takte mit vier Spielern, dass gleiche
Befehle dieselbe Welt ergeben: im direkten Vergleich, nach einem
Snapshot mitten in der Sitzung, beim Wiederholen einer Aufzeichnung
und nach dem Weg über die Kodierung.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Eine Aufzeichnung hält den Startzustand und die Befehle jedes Takts
fest. Wiederholen ergibt exakt dieselbe Welt; verify meldet, wenn das
Ende von der aufgezeichneten Prüfsumme abweicht.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Der Snapshot enthält den vollständigen Zustand der Welt hinter einem
Kopf mit Format und Version. Er dient als Speicherstand und als
Grundlage der Prüfsumme. Das Konzept ist entsprechend korrigiert: Der
Speicherstand ist der vollständige Zustand, nicht Startzahl plus
Änderungen.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
World::step wendet die Befehle eines Takts der Reihe nach an: Spieler
treten bei und verlassen die Welt, drei Schläge brechen weichen Fels
und bringen einen Stein. Abgelehnte Befehle ändern nichts.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Ein Spieler hat eine dauerhafte Kennung, einen Namen, eine
Werkzeugstufe und ein Inventar. Befehle (Beitreten, Verlassen, Graben)
sind der einzige Weg, die Welt zu ändern, und lassen sich kodieren.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
24 Plätze mit je bis zu 99 Gegenständen. Hinzufügen füllt zuerst
vorhandene Stapel und meldet, was nicht passt; Entfernen nimmt alles
oder nichts.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Baut aus einer Startzahl eine 64 × 64 Felder große Karte mit Rand aus
Grundgestein, Lichtung am Startpunkt, Felsblock daneben, weichem Fels
innen und hartem Fels außen. Die Regeln sind für 300 Startzahlen geprüft.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Gelände mit Grabregeln, Kacheln mit Schaden und eine rechteckige Karte,
die sich kodieren lässt und unmögliche Eingaben ablehnt.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
FNV-1a für Zustandsprüfsummen und SplitMix64 als einzige Zufallsquelle
des Kerns. Beide sind gegen veröffentlichte Referenzwerte geprüft.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Writer und Reader für Befehle, Snapshots und Aufzeichnungen. Beschädigte
Eingaben führen zu einem Fehler statt zu einem Absturz.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Workspace, leeres Crate grovewright-core und ein Wächter-Test, der den
Quelltext des Kerns auf Verstöße gegen die Gleichschritt-Regeln prüft.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Erster von vier Plänen für Meilenstein 1. Elf Tasks bauen den
deterministischen Kern: Kodierung, Prüfsumme, Zufall, Karte, Generator,
Inventar, Spieler, Befehle, Welt, Snapshot, Aufzeichnung und die
Abnahmetests für den Gleichschritt. Der Code im Plan ist vorab geprüft.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ergebnis des Konzeptgesprächs vom 30.09.2026: lebendige Fabrik,
Koop im Gleichschritt, Rust-Kern mit GDScript-Hülle, sechs Meilensteine.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>