Wie ich es gebaut habe.

Zugwunder ist von Grund auf in Swift geschrieben: keine Engine-Bibliothek darunter, kein Framework außer dem, was Apple mitliefert. Diese Seite ist der Rundgang durch das Innere, für neugierige Spieler und für Programmierer, die wissen wollen, worauf die Zahlen beruhen.

Warum von Grund auf

Weil eine Schachengine ein Programm ist, das man messen kann.

Ich baue Software, die auf meinen eigenen Rechnern läuft statt im Rechenzentrum anderer Leute, und eine Schachengine ist der ideale Prüfstein: Jede Idee lässt sich in ein paar tausend Partien schicken und gewinnt entweder Elo oder nicht. Mit dem Partieformular lässt sich nicht streiten. Zugwunder begann als Mattlotse, mein älterer Löser für Matt-in-n-Aufgaben. Am 25. September 2026 habe ich ihn in Zugwunder umbenannt, und in den Wochen danach sind daraus die Suche, die Bewertung, der Trainer, die Partien, das neuronale Netz und die beiden anderen Programme gewachsen, Version für Version, wobei ein Vergleich gegen die Vorversion entschied, ob eine Änderung blieb.

Alles ist Swift 6 mit strikter Nebenläufigkeit, gebaut mit dem Swift Package Manager, und hängt von nichts außerhalb von Apples Frameworks und der Swift-Standardbibliothek ab. Die einzige Ausnahme ist Fathom, eine kleine C-Bibliothek unter MIT-Lizenz, die Syzygy-Endspieltabellen liest; ein eigener Leser für dieses Format hätte nichts als Fehler hinzugefügt.

Das Brett

Bitboards, Zobrist-Schlüssel, eine sperrfreie Hashtabelle.

Die Stellung ist ein Satz 64-Bit-Bitboards, eines je Figurenart und Farbe, sodass „alle Felder, die ein Springer von hier angreift“ ein Tabellenzugriff ist und „alle Figuren, die dieser Turm angreift“ ein paar Bitoperationen. Der Zuggenerator erzeugt ausschließlich legale Züge, einschließlich Rochade, En passant und Umwandlungen, mit einem eigenen schnellen Pfad für Schlagzüge, den die Ruhesuche nutzt.

Jede Stellung hat einen 64-Bit-Zobrist-Schlüssel, der die Transpositionstabelle, die Erkennung von Stellungswiederholungen und den Bauernstruktur-Cache antreibt. Die Transpositionstabelle teilen sich alle Suchthreads ohne Sperren, und Mattbewertungen werden auf den Abstand zur Wurzel normiert, damit ein Eintrag, der in einer Tiefe gefunden wurde, in einer anderen richtig bleibt.

FEN und PGN haben eigene Parser und Schreiber. PGN behält Tags, Kommentare, Bewertungen und verschachtelte Varianten über einen vollständigen Umlauf, und die Prüfung erklärt, warum eine Stellung regelwidrig ist (zwei Könige nebeneinander, ein Bauer auf der Grundreihe, ein Rochaderecht ohne Turm), statt die Engine darin suchen zu lassen.

Die Bewertung

Ein kleines neuronales Netz, trainiert mit den eigenen Partien der Engine.

Die erste Bewertung war handgeschrieben: Figur-Feld-Tabellen, Königssicherheit und Bauernstruktur, mit dem Texel-Verfahren an den eigenen Partien der Engine abgestimmt. Sie steckt noch in der Binärdatei (UseNNUE false) als Vergleichsmaßstab. Die Bewertung, die heute spielt, ist ein NNUE, die Art Netz, die Stockfish zum Standard gemacht hat, hier aber aus den eigenen Daten der Engine trainiert:

  • 768 Eingänge, einer je Figur, Farbe und Feld, aus der Sicht jeder Seite; zwei versteckte Akkumulatoren mit 256 Neuronen und Clipped ReLU; ein Ausgang.
  • Gewichte, quantisiert auf 16-Bit-Ganzzahlen. Die Akkumulatoren werden beim Ziehen inkrementell aktualisiert, 16 Neuronen je SIMD-Vektor, sodass eine Stellungsbewertung ein paar hundert Ganzzahloperationen kostet statt eines vollständigen Vorwärtsdurchlaufs.
  • Das Netz lebt in der Binärdatei als Swift-Quelldatei, die aus den trainierten Gewichten erzeugt wird; es gibt keine separate Datei, die verloren gehen könnte. Für Experimente lässt sich mit der Option NNUEFile ein anderes Netz laden.

Die Trainingsschleife ist so schlicht, wie sie klingt. zugwunder-datagen lässt die Engine gegen sich selbst und gegen Stockfish spielen und schreibt je Stellung einen 68-Byte-Datensatz: die Merkmale, Zugwunders eigene Suchbewertung und das Partieergebnis. Etwa vier Millionen Stellungen pro Stunde auf acht Kernen. Ein 150-zeiliges PyTorch-Skript trainiert das Netz auf einer Apple-GPU, embed.py verwandelt die Gewichte in Swift, und die nächste Version trägt das neue Netz. Die sechste Generation wurde mit 236,6 Millionen Stellungen trainiert; jede Generation wurde erst nach Vergleichen gegen ihren Vorgänger und gegen fremde Engines übernommen.

Was nicht hineinkam: kein Stockfish-Code, keine Stockfish-Gewichte, keine Stockfish-Bewertungen. Stockfish war Gegner und Maßstab, sonst nichts.

Endspielwissen

Berechnet, nicht heruntergeladen.

Für König und Dame, Turm oder Bauer gegen den nackten König sowie für die beiden Viersteiner Läufer und Springer gegen König und Turm gegen Läufer schätzt Zugwunder nicht: Es berechnet die exakten Tabellen beim Start selbst durch Retrograde-Analyse und nutzt die Symmetrien des Bretts, um die Arbeit klein zu halten. Die Viersteiner-Tabellen brauchen etwa anderthalb Sekunden im Hintergrund. Suche, Löser und Rätselgenerator lesen sie alle.

Für alles andere mit bis zu fünf Steinen gibt es die Syzygy-Tabellen: Zeig der Engine einen Ordner, und sie spielt diese Endspiele perfekt und nutzt die Urteile in der Suche nach jedem Schlagzug oder Bauernzug.

Der Rätselgenerator ist das kleinste Programm auf dieser Seite und mein liebstes. Er durchläuft die 64 × 64 × 64 Aufstellungen von drei Steinen in zufälliger Reihenfolge mit einer ungeraden Schrittweite, sodass jede Aufstellung genau einmal besucht wird, ohne Liste; behält nur legale Stellungen, deren Tabelleneintrag „Matt in genau n“ sagt; und prüft dann, dass genau ein erster Zug das erreicht. Die Stellung auf der Startseite stammt daraus.

Exakte Löser

Beweisbäume für Matt, Hilfsmatt, Selbstmatt und Patt.

Eine Spielsuche beantwortet „Was ist hier gut?“. Ein Problemlöser muss „Gibt es ein erzwungenes Matt in drei, und ist es eindeutig?“ beantworten, und das ist ein anderes Programm. Zugwunders Löser bauen UND/ODER-Beweisbäume: Bei den Zügen des Angreifers muss ein Ast funktionieren, bei den Zügen des Verteidigers muss jede Antwort beantwortet sein. Das Ergebnis ist der vollständige Lösungsbaum mit allen Verteidigungen, ein Urteil bewiesen, widerlegt oder innerhalb der Grenze unentschieden, und eine Warnung, wenn ein zweiter erster Zug die Aufgabe ebenfalls löst, denn für einen Problemkomponisten ist eine Aufgabe mit Nebenlösung eine kaputte Aufgabe.

Hilfsmatts (beide Seiten arbeiten zusammen, alle Lösungen werden aufgelistet), Selbstmatts (die Partei am Zug zwingt den Gegner, sie mattzusetzen) und Pattaufgaben nutzen dieselbe Maschinerie mit anderem Zieltest. Diese Löser sprechen direkt mit der Engine-Bibliothek statt über UCI, weil sie den Beweisbaum brauchen, nicht einen besten Zug.

Messen

Tausende Partien, und die Disziplin, ihnen zu glauben.

Das Projekt liefert seine eigenen Messwerkzeuge mit. zugwunder-match lässt zwei Engines aus einem Eröffnungsbuch gegeneinander spielen, jede Eröffnung mit beiden Farben, mit Uhren, Adjudikation und einem Sequential Probability Ratio Test (SPRT), der aufhört, sobald das Ergebnis statistisch klar ist. Eine Suchänderung muss diesen Test gegen den vorigen Build bestehen, bevor sie übernommen wird. zugwunder-rating verwandelt Vergleichsergebnisse gegen Stockfish auf mehreren UCI_Elo-Stufen in eine Maximum-Likelihood-Schätzung auf Stockfishs Skala.

Die Hauptzahl, 3065 ± 20 Elo für das aktuelle Netz, stammt aus 500 Partien gegen Stockfish bei UCI_Elo 3100 unter turnierähnlichen Bedingungen: 60 Sekunden plus 0,6 pro Zug, ein Kern, 256 MB Hash, Syzygy-Tabellen, wobei Engine-Binärdatei, Netze und Gegner in einem Referenzordner eingefroren und ihre Prüfsummen festgehalten sind. Eine zweite Serie gegen Arasan ergab ein stimmiges Bild. Derselbe eingefrorene Aufbau hatte das vorige Netz bei 3045 ± 20 gesehen. Diese 500 Partien sind die eingefrorene Referenzmessung; über die Wochen hat der Cluster der Rechner hinter dem Projekt Tausende weitere gegen Stockfish und andere Engines gespielt, für SPRT-Tests, Kalibrierung und Datensammlung. All das sind Schätzungen auf der kalibrierten Skala einer anderen Engine, keine offizielle Rangliste, und das sage ich überall, wo die Zahl auftaucht.

Die einstellbare Spielstärke der UCI-Engine wurde genauso kalibriert: mehr als 5.000 Partien gegen Stockfish 17.1 auf einem Linux-Rechner auf sieben Stufen, mit Konfidenzintervallen für jede, interpoliert zu einer UCI_Elo-Skala von 1100 bis 3000. Sie gilt als vorläufig, bis sie gegen einen zweiten Gegner und unter macOS geprüft ist, denn eine Kalibrierung gegen einen Gegner ist eine Kalibrierung gegen einen Gegner.

Unter den Vergleichen liegen 364 Swift-Tests über Engine, App und Wolfgang sowie 32 Protokolltests, die die echte zugwunder-uci-Binärdatei durch die UCI-Spezifikation von 2006 treiben: Pondern, searchmoves, Knotengrenzen, ein stop, das vor dem Suchstart eintrifft, Eingaben, die über mehrere Pipe-Schreibvorgänge verteilt sind. Der Protokolltest liegt jedem Paket bei, damit du ihn auf deinem eigenen Rechner ausführen kannst.

Die Apps

SwiftUI und AppKit, vier Sprachen, VoiceOver, ein sorgfältiger Updater.

Zugwunder und Wolfgang sind native macOS-Apps in SwiftUI, mit AppKit dort, wo SwiftUI nicht reicht (Fenster, Tastatur, Bildschirmfotos, Schlüsselbund). Das Brett zeichnet die App selbst, es unterstützt Drag-and-drop und vollständige Tastaturbedienung und liest unter VoiceOver Züge vor. Alle rund 900 Oberflächentexte gibt es auf Deutsch, Englisch, Französisch und Niederländisch, und die Sprache wird in der Fensterkopfzeile umgeschaltet.

Beide Apps bringen ihre eigene Kopie von zugwunder-uci mit und sprechen über UCI mit ihr, genau wie mit Stockfish: Die App sendet Ausgangsstellung und vollständige Zugliste, und der Engine-Prozess mit seiner Hashtabelle überlebt die ganze Partie. Nur die exakten Löser rufen die Engine-Bibliothek direkt auf. Eine App-Version ändert deshalb nie die Version der Engine, und die Engine lässt sich für sich allein testen.

Der Updater ist ein kleiner separater Helfer. Die App fragt einen JSON-Feed über HTTPS ab, prüft, dass der Download vom selben Host kommt, kontrolliert Größe und SHA-256, hängt das Disk-Image schreibgeschützt ein, prüft Bundle-Identifier und Developer-ID-Team, tauscht das Bundle atomar mit Sicherung aus und startet neu. Schlägt ein Schritt fehl, kommt die alte Version zurück.

Die neueste Funktion, die Erkennung einer Stellung aus einem Foto, ist die eine Stelle, an der die App mit einem Dienst spricht: Sie schickt das Bild über den von dir eingegebenen API-Schlüssel an Anthropics Claude, und nur, wenn du den Knopf drückst. Die Antwort wird als Vorschlag behandelt, als Schachstellung geprüft und zur Korrektur angezeigt. Eine frühere Fassung nutzte ein lokales Modell über Ollama; die jetzige nutzt Claude und sagt das neben dem Knopf.

Drei Plattformen, ein Build

macOS, Linux und der Browser aus derselben Quelle.

  • macOS: Apple Silicon, mit Developer ID signiert, von Apple notarisiert und gestapelt. Das Release-Skript erhöht die Version, baut, signiert, notarisiert, schreibt den Update-Feed und ein Manifest mit Prüfsummen und bewahrt die Protokolle auf.
  • Linux: auf dem Mac mit dem statischen Linux-SDK von Swift cross-kompiliert für x86_64 (mit AVX2) und ARM64. Das Ergebnis ist eine statisch gelinkte Datei, die keine Bibliotheken braucht, weshalb sie auf einem Raspberry Pi ebenso läuft wie auf einem Server.
  • WebAssembly: gebaut mit dem Swift-SDK für WebAssembly gegen WASI und FoundationEssentials. Ein Web Worker führt die Engine aus, damit die Seite bedienbar bleibt; das komprimierte Modul ist kleiner als drei Megabyte. Die Spielseite nutzt es, und dasselbe Paket liegt bei den Downloads, falls du es selbst hosten willst.

Ein vollständiger Build führt die Swift-Tests, die Protokolltests und die Tests des Datensammlers aus, baut alle Pakete, signiert und notarisiert die macOS-Pakete und weigert sich zu veröffentlichen, wenn irgendetwas fehlschlägt. Der Ordner, den er erzeugt, enthält die Pakete, ein Manifest, eine SHA256SUMS-Datei und vierzig Protokolldateien.

In Zahlen

Gezählt am 2. November 2026.

Engine10.200 ZeilenSwift, ohne die 4.400 erzeugten Zeilen mit den Netzgewichten.
Zugwunder-App11.900 ZeilenSwiftUI und AppKit, einschließlich der Oberfläche der Löser, des Trainers, des Updater-Clients und des Bildimports.
Wolfgang8.800 ZeilenDie UCI-Schachoberfläche mit Turnieren und Engine-Verwaltung.
Werkzeuge1.400 ZeilenProgramme für Vergleiche, Tuning, Datenerzeugung, Bewertung, Analyse und Neubewertung, dazu die UCI- und WebAssembly-Frontends.
Tests7.500 Zeilen364 Swift-Tests, 32 Protokolltests und die Python-Prüfungen für den Datensammler.
Training150 ZeilenDas PyTorch-Skript, das sechs Netzgenerationen hervorgebracht hat.
VeröffentlichungenÜber 30App-Versionen seit dem 25. September 2026, von Mattlotse 1.0.3 bis Zugwunder 1.1.15.

Zeitleiste

Von einem Mattlöser zu drei Programmen.

  • Aus Mattlotse wird Zugwunder

    Meine alte App Mattlotse, ein Matt-in-n-Löser mit vollständigen Beweisbäumen und Nebenlösungsprüfung, in Zugwunder umbenannt. PVS, Nullzug, LMR und die handgeschriebene Bewertung kommen am selben Tag.

  • Partien, Trainer, Dateien

    Spielstärken, Schachuhr, Aufgabenbibliothek, PGN mit Kommentaren und Varianten, Hilfsmatt- und Selbstmattlöser, eigene Endspieltabellen, die UCI-Engine und das Match-Werkzeug mit SPRT, Lazy SMP.

  • Vier Sprachen, Netz v4

    Deutsch, Englisch, Französisch und Niederländisch. Das vierte Netz (113 Millionen Stellungen) gewinnt 78 Elo gegenüber dem dritten.

  • Netz v5, erste Wertungsschätzung

    170 Millionen Stellungen, +25 Elo. Tausend Partien gegen Stockfish 19 setzen die Engine auf 3006 ± 17 auf der CCRL-ähnlichen Skala.

  • Im Browser

    Die Engine baut für WebAssembly und spielt auf f97.be ohne Installation.

  • Drei Pakete

    App, Wolfgang und Engine werden getrennte Pakete mit einem gemeinsamen Release-Skript. Beide Apps bringen die Engine mit und sprechen UCI mit ihr.

  • Netz v6, UCI vollständig

    Vergleiche über je 500 Partien gegen Stockfish und Arasan; 3065 ± 20 Elo. Jeder Befehl der UCI-Spezifikation umgesetzt und getestet.

  • Stärkekalibrierung

    Mehr als 5.000 Partien gegen Stockfish 17.1 geben der Engine eine vorläufige UCI_Elo-Skala von 1100 bis 3000.

  • Rätselgenerator, Bildimport

    Neue Rätsel aus den Endspieltabellen mit geprüft eindeutigem Schlüsselzug; Stellungen aus Fotos in allen vier Sprachen; Fensteraufnahmen.

Prüf die Zahlen selbst.

Jedes Paket enthält Prüfsummen und den Protokolltest. Die Match-Werkzeuge stecken im Werkzeuge-Archiv.