Das Spiel entwirft nie ein Puzzle von Hand. Es lässt eines aus einer einzigen Zahl wachsen und spielt dieses Puzzle dann gegen sich selbst, um sicherzustellen, dass es weder trivial noch unmöglich ist, bevor Sie es überhaupt zu sehen bekommen.
Auf Englisch verfasst und redigiert. Diese deutsche Fassung wurde maschinell übersetzt; wo es auf Genauigkeit ankommt, ist das englische Original maßgeblich. Das englische Original lesen →
Ein Contraption-Level wird nirgendwo gespeichert. Es gibt keine Datei, die auflistet, wo das Ziel liegt oder wie die Vorsprünge angeordnet sind. Stattdessen wird das gesamte Level, der Startpunkt des Balls, die Position des Ziels, die Anzahl und Anordnung der festen Wände sowie das Inventar der Teile, die Sie erhalten, aus einer einzelnen Ausgangszahl berechnet, die als Seed bezeichnet wird. Geben Sie dem Generator denselben Seed, und Sie erhalten, Byte für Byte, dasselbe Puzzle. 1
Das ist möglich, weil die einzige Quelle der „Zufälligkeit“ des Generators ein kleiner deterministischer Pseudozufallszahlengenerator ist. Der, den Contraption verwendet, ist mulberry32, ein kompakter 32-Bit-Generator von Tommy Ettinger; Sie erkennen ihn an seiner charakteristischen Konstante, 0x6d2b79f5, die bei jeder Ziehung zum Zustand addiert wird. 2 Er ist schnell, er passt in ein paar Zeilen, und, was entscheidend ist, er ist überhaupt nicht zufällig: Er ist eine feste mathematische Folge, die lediglich durchmischt aussieht. Geben Sie ihm einen Anfangspunkt, und er liefert für immer denselben Zahlenstrom. Der Generator liest dann diesen Strom, um zu entscheiden, wo alles hinkommt.
Der Daily-Modus stützt sich auf diesen Determinismus, um etwas Soziales zu leisten. Sein Seed ist nicht zufällig, er wird aus dem Kalender gefertigt. Das Spiel nimmt das heutige Datum in Weltzeit, schreibt es in eine kurze Zeichenfolge wie „contraption:2026-7-28“ und lässt diesen Text durch eine Hash-Funktion laufen, um ihn auf eine einzige 32-Bit-Zahl einzudampfen. 1
Der Hash ist FNV-1a, ein bekannter nicht-kryptografischer Mischer: Er startet bei einem festen Offset, der Dezimalzahl 2166136261 oder 0x811c9dc5, und verknüpft für jedes Zeichen den Buchstaben per Exklusiv-Oder mit dem Zustand und multipliziert dann mit der FNV-Primzahl 16777619. 3 Übrig bleibt eine Zahl, die für einen gegebenen Tag stabil ist und sich von einem Tag zum nächsten wild unterscheidet, sodass eine Änderung um ein einziges Zeichen im Datum den Seed vollständig verstreut. Da der Kalender für alle gleich ist, ist es auch der Seed und damit auch das Puzzle. Zwei Fremde auf entgegengesetzten Seiten der Welt öffnen Daily und stehen vor dem identischen Spielfeld, und genau das macht es sinnvoll, Punkte zu vergleichen.
Die tägliche Herausforderung ist nicht deshalb fair, weil ein Server allen dasselbe Level schickt, sondern weil jede Kopie unabhängig dasselbe Level aus demselben Datum neu berechnet.
Zufällige Geometrie ist leicht; gute zufällige Geometrie nicht. Ein Generator, der einfach ein Ziel und ein paar Vorsprünge verstreut, wird manchmal ein Level erzeugen, in dem der Ball, von oben fallen gelassen, einfach von allein schnurstracks ins Ziel fällt. Das ist kein Puzzle, das ist eine Zwischensequenz. Bevor Contraption eine Kandidatenwelt akzeptiert, führt es daher einen stillen Test aus, den der Code preSolved nennt: Es simuliert das Level mit null platzierten Teilen und prüft, ob der bloße Ball allein durch die Schwerkraft das Ziel erreicht. Wenn ja, wird das Level verworfen, und der Generator würfelt neu. 1
Das ist eine subtile Umkehrung dessen, wie Sie normalerweise über eine Physik-Engine denken. Dieselbe simulate()-Funktion, die Ihre Maschine durchspielt, wird bei der Generierung nach innen gekehrt, um die Schwierigkeit des Levels selbst zu prüfen. Die Engine ist sowohl der Schiedsrichter Ihrer Lösung als auch der Qualitätsprüfer des Puzzles.
Das zu leichte Level abzulehnen ist nur die halbe Arbeit. Die andere Gefahr ist das unmögliche Level, ein Ziel, das irgendwo versteckt liegt, wo keine Anordnung von Teilen hinreicht. Contraption schützt sich davor mit einem kleinen Brute-Force-Löser namens plausiblySolvable. Er denkt nicht schlau nach. Er probiert einfach aus. 1
Konkret fährt er mit einer einzelnen Rampe ein Raster von Positionen über dem Spielfeld ab und probiert sie an jeder Stelle in vier verschiedenen Neigungen. Für jede dieser Testplatzierungen führt er die vollständige Simulation aus und fragt: Hat diese eine Rampe den Ball nach Hause geleitet? Sobald irgendeine Platzierung gelingt, wird das Level für lösbar erklärt und an Sie ausgeliefert. Funktioniert nach dem gesamten Durchgang keine davon, verwirft der Generator das Level lieber und probiert einen frischen Seed.
| Durchgangsparameter | Bereich | Bedeutung |
|---|---|---|
| Rampe X | 15 → 85, Schritt 14 | Spalten über das Spielfeld |
| Rampe Y | 30 → 120, Schritt 18 | Zeilen das Spielfeld hinab |
| Getestete Winkel | 4 | ±0,45 und ±0,8 Radiant |
| Test pro Platzierung | 1 vollständige Simulation | erreicht der Ball das Ziel? |
Es lohnt sich, ehrlich zu sagen, was das beweist, denn der Code ist darin ebenfalls ehrlich: Der Kommentar nennt es eine „billige Plausibilitätsprüfung … nicht erschöpfend“. Ein Level, das besteht, hat definitiv mindestens eine grobe Lösung mit einer einzigen Rampe. Ein Level, das scheitert, könnte immer noch mit einem Ventilator, einem Zahnrad oder einer klügeren Rampe lösbar sein, die das Raster nie probiert hat, daher behandelt der Generator ein Scheitern nicht als Beweis der Unmöglichkeit. Er behält die erste nicht-triviale Welt, die er gefunden hat, als Fallback, und wenn vierzig Versuche vergehen, ohne dass eine saubere Ein-Teil-Lösung dabei ist, liefert er dieses Fallback aus, statt für immer in einer Schleife zu bleiben. Das Ergebnis ist eine pragmatische Garantie: nie ein geschenkter Sieg, fast immer ein echter Weg hinein und nie ein endloses Warten auf das „perfekte“ Level. 1
Tritt man einen Schritt zurück, ist der Levelgenerator von Contraption ein sauberes Beispiel für ein sehr altes und sehr allgemeines Problemlösungsmuster: Generieren und Testen. Einen Kandidaten zufällig vorschlagen; ihn gegen die Anforderungen prüfen; behalten, wenn er besteht, verwerfen und neu vorschlagen, wenn er scheitert. Das ist die Schleife hinter prozeduralen Inhalten in unzähligen Spielen, hinter einem Großteil der evolutionären Algorithmen und, wenn man die Augen etwas zusammenkneift, hinter der natürlichen Selektion selbst: Die Mutation schlägt vor, die Umwelt testet, Überlebende bestehen fort. 4
Was die Version von Contraption elegant macht, ist, dass der Test kein billiger Ersatz für Spielbarkeit ist, er ist die Spielbarkeit. Der Generator schätzt nicht, ob ein Level fair ist, indem er Vorsprünge zählt oder Abstände misst. Er spielt das Level buchstäblich, und zwar zweimal: einmal leer, um sicherzustellen, dass es kein Geschenk ist, und einmal mit einer Testrampe, um sicherzustellen, dass ein Weg existiert. Dieselbe deterministische Physik, die Ihre Lösung beurteilen wird, ist die Physik, die das Puzzle geprüft hat. Es gibt keinen separaten „Level-Validator“, der aus dem Gleichlauf mit den echten Regeln geraten könnte, denn es gibt nur einen einzigen Regelsatz, der für alles verwendet wird.
Und weil alles deterministisch ist, ist die Prüfung in beide Richtungen frei von Glück. Der tägliche Seed kann sich nicht verschwören, einem Spieler ein leichteres Spielfeld zuzuteilen als einem anderen, und er kann niemandem ein unfaires zuteilen, denn bevor das Level eine Herausforderung ist, hat es bereits überlebt, gespielt zu werden, vom Spiel, gegen sich selbst.