Wir haben nun also ein rießiges PGN-Archiv fast aller jemals gespielter Meister-Partien zur Hand, das wir in die soeben erstellte relationale Struktur unserer chess Datenbank importieren wollen. Wir müssen uns vorab ein paar Gedanken machen, wie wir das genau bewerkstelligen können.
- offensichtlich benötigen wir eine Software, die sowohl in der Lage ist, mit dem PGN-Dateiformat umgehen zu können wie auch eine Anbindung an unsere MariaDB-Datenbank bietet.
- die Import-Software soll die Daten nicht nur stumpf von A nach B übertragen, sondern über eine gewisse Intelligenz verfügen
- sie soll Partien mit ungültigen Zügen und/oder fehlenden Angaben etwa zu den beteiligten Spielern beim Importvorgang ignorieren.
- sie soll Duplikate bei den Spielern erkennen und diese nicht mehrfach in die player Tabelle eintragen. Sprich, die Software muss in der Lage sein, die Daten in normalisierter Form in die Datenbank zu überführen.
- sie soll aus der Ausgangsstellung einer Partie für jeden Zug die daraus resultierende Stellung konstruieren und als FEN-Zeichenkette liefern können.
- wir wollen für die später durchzuführenden Analysen und Visualisierungen in Jupyter Notebooks eine möglichst einheitliche und gut etablierte, breit aufgestellte Software nutzen, die alle Aspekte abdeckt.
- Die einzusetzende Software soll nicht proprietär, sondern frei, möglichst als Open Source Software für die wichtigsten Plattformen wie MS Windows, Linux und macOS verfügbar sein.
Python – die eierlegende Wollmilchsau
Die vielfältigen individuellen Anforderungen an diese Software sprechen klar für eine flexible Programmiersprache. Im Zusammenhang mit der Entscheidung für Jupyter Notebooks liegt daher der Griff zu Python nicht allzu fern.
- Python ist eine ausgereifte moderne objektorientierte Programmiersprache, die dazu überragend gut dokumentiert ist.
- Python ist insbesondere im Jupyter-Umfeld ein Quasi-Standard
- Python bietet mit seinen zahllosen gut dokumentierten Software-Bibliotheken (Modulen) Unterstützung für alle unsere Anforderungen
- python-chess ist eine grandiose Bibliothek mit allen Funktionen, die wir für unsere schachlichen Aufgaben benötigen
- Handhabung von PGN-Dateien
- Unterstützung gängiger chess engines, insbesondere Stockfish. Damit sind Zug-Verifikation, die Erzeugung von Stellungsbildern im FEN-Format und Zugbewertungen für die spätere Partienanalyse ein Kinderspiel.
- mariadb ist ebenfalls eine ausgereifte Python Software-Bibliothek, die wir für den Zugriff auf unsere chess Datenbank einsetzen wollen.
- für die späteren analytischen Auswertungen unserer Kennzahlen existieren etablierte Statistik- und Visualisierungs-Pakete in Verbindung mit Jupyter Notebooks.
- python-chess ist eine grandiose Bibliothek mit allen Funktionen, die wir für unsere schachlichen Aufgaben benötigen
Schritt-für-Schritt Installation
Zunächst laden wir die Python-Software für die gewünschte Umgebung in der aktuellen stabilen Form herunter, z.B. das Installationsprogramm für MS-Windows. Sollte Python auf dem System bereits installiert sein, wird uns bei Ausführung der Installations-Software ein Upgrade angeboten, sonst eine Neuinstallation.

Bei der Neuinstallation wählen wir die folgenden Optionen:



Nach der erfolgreichen Installation überprüfen wir diese in der Windows Eingabeaufforderung (cmd.exe) und installieren mit pip, dem integrierten Paket-Manager von Python, gleich die beiden wichtigsten Bibliotheken mariadb und chess.

Im Anschluss installieren wir gleich noch jupyter auf die selbe Weise, was wir aber aufgrund der umfangreichen Bildschirmausgaben hier nicht zeigen wollen. Später werden wir noch das ein oder andere zusätzliche Paket bei Bedarf nachinstallieren, aber fürs erste soll das genügen.
Hier nochmal die Zusammenfassung der einzelnen Paket-Installationen:
pip install mariadb
pip install chess
pip install jupyter
Hinweis: Ab und zu sollte man die vorhandenen Pakete auf Aktualität überprüfen und ggf. upgraden. Unter MS-Windows klappt dies am besten in einerm Powershell Terminal-Fenster mit folgendem Kommando:
pip freeze | %{$_.split('==')[0]} | %{pip install --upgrade $_}
Stockfish – das Schachgehirn
wir haben nun fast alle Komponenten zusammen, um in die Programmierung unseres Datenbank-Importes sowie später auch in die Zug-Analysen einzusteigen. Doch das Herzstück für das Schachverständnis fehlt noch! Zwar ist die chess Bibliothek in der Lage, die Notation einer Schachpartie im PGN-Format syntaktisch zu erkennen und einzulesen, aber die Züge selbst kann sie inhaltlich nicht begreifen. Doch was sie kann, ist eigentlich viel cleverer – sie kann nämlich mit einem Schachprogramm (engl. chess engine) kommunizieren! Die „Sprache“, in der das geschieht, ist technischer Natur und über ein Protokoll festgelegt. Es existieren tatsächlich mehrere Protokolle, doch das relevanteste, welches auch hier genutzt werden soll, ist UCI – Universal Chess Interface. Damit ist gewährleistet, dass beliebige Anwendungsprogramme mit allen Schachprogrammen kommunizieren können, sofern sie nur das selbe Protokoll bedienen. Sollte z.B. ein Schachprogramm durch den Einsatz von KI-Algorithmen in seiner Spielstärke verbessert werden, kann jedes Anwendungsprogramms (etwa zum interaktiven Handling mit einem Schachbrett oder eben Python mit dem chess Modul) ohne Anpassung weiter funktionieren! Man sagt auch, die Funktionen sind entkoppelt – haben weniger Abhängigkeiten voneinander und sind daher wartbarer und störunanfälliger.
Was uns also noch fehlt, ist eine chess engine. Wie bereits erwähnt, wollen wir für unsere Zwecke, Stockfish als derzeit spielstärkste und dazu noch kostenlose Schachmaschine nutzen. Wie wir das Python chess Modul mit Stockfish verbinden zeigen wir gleich, doch zunächst besorgen wir uns das Schachprogramm. Auf der Stockfish-Download-Seite laden wir das für die jeweilige Plattform angebotene ZIP-Archiv herunter und entpacken es an beliebiger Stelle auf unserem Computer. Damit sind wir praktisch fertig. Dann ist weiter nichts zu tun, als in unserem Python-Programm den Ort des Schachprogrammes über einen Parameter mitzuteilen.
Eine erste kleine Anwendung
Als Programmierumgebung für unsere erste Demo nutzen wir ein Jupyter Notebook, ein grafisches Notizbuch, in das wir adhoc Python-Code Stück für Stück eingeben und uns die Ergebnisse sofort anzeigen lassen können. Die Installation haben wir bereits erledigt und der Start eines Notebooks ist genauso simpel. Der Aufruf unten in einem Fenster der Eingabeaufforderung von Windows startet unseren Web-Browser mit einer neuen Seite für unser Notebook.

In der Beispielanwendung unten werden zunächst die benötigten Programm-Pakete (bzw. Bibliotheken) importiert und damit bekannt gemacht (physisch installiert haben wie sie bereits).
import chess
import chess.engine
import chess.pgn
from IPython.display import display, Image
Dann speichern wir den Dateipfad zum eigentlichen Schachprogramm in einer Programm-Variablen und übergeben diese als Parameter dem chess Modul in unserem Python Programm. Das für die weitere Verwendung zu benutzende Objekt weisen wir der Variablen engine zu.
engine_path = "/home/peterk/stockfish/stockfish-ubuntu-x86-64-avx2"
engine = chess.engine.SimpleEngine.popen_uci(engine_path)
Im nächsten Schritt bauen wir aus einer von uns vorgegebenen Stellung im FEN-Format eine Schachstellung aus und lassen uns diese in rudimentärer Textform anzeigen. Interessant dabei ist, dass wir dafür nur Funktionen des chess Moduls nutzen und nicht etwa der engine. Um ein Schachbrett wie vorgegeben aufzubauen, braucht es also keine tieferes Schachverständnis!
board = chess.Board("4Q3/5K1k/8/8/8/8/8/8 w - - 0 1")
print (board)
. . . . Q . . .
. . . . . K . k
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
Achtung – jetzt wird es spannend. Wir bauen nun ein neues Brett auf, wie grafisch unten zu sehen ist und lassen diese Stellung analysieren. Die übergebene FEN in der ersten Zeile unten spezifiziert nicht nur die Positionen der Steine auf dem Brett, sondern auch, wer am Zuge ist, in diesem Falle Schwarz.
In der vierten Zeile übergeben wir das board Objekt nun der engine zur Analyse. Weiter sagen wir der engine mit dem Limit-Parameter, dass sie die Analyse nur mit der maximalen Suchtiefe von 20 Halbzügen ausführen soll – für solch eine simple Stellung mehr als genug! Darüberhinaus wollen wir nicht nur den besten Zug für Schwarz sehen, sondern auch noch den zweit- und dritt-besten.
Die Ausgabe der zurückgelieferten Informationen („print(info…)“) ist etwas unübersichtlich. Die Vielzahl von teils technischen Werten ist mehr für eine programmtechnische Auswertung von Belang. Bei genauem Hinsehen kann man dennoch erkennen, dass die engine das mögliche zweizügige Matt durch c2c1 von Schwarz richtig berechnet („’score‘: PovScore(Mate(+2), BLACK)“) hat und sie gibt alle Züge und Gegenzüge in der Hauptvariante (mit bestem Spiel von Weiß) korrekt an. Weiter lässt sich mit etwas Mühe erkennen, dass die beiden nächst besten Züge c2g2 bzw. g3f4 jeweils mit „PovScore(Cp(0), BLACK)“ bewertet sind. Cp steht dabei für CentiPawn – ein weithin bekanntes Rating-System, ohne darauf jetzt näher einzugehen. Jedenfalls bedeutet Cp(0), dass die Stellung ausgeglichen ist und keine Seite einen merklichen Vorteil besitzt.
board = chess.Board("8/8/6P1/4R3/8/6k1/2r5/6K1 b - - 0 1")
display(board)
# Get the 3 best moves
info = engine.analyse(board, chess.engine.Limit(depth=20), multipv=3)
# Info is now an array with at most 3 elements
# If there aren't 3 valid moves, the array would have less than 3 elements
print(info[0])
print(info[1])
print(info[2])
{'string': 'NNUE evaluation using nn-37f18f62d772.nnue (6MiB, (22528, 128, 15, 32, 1))', 'depth': 20, 'seldepth': 4, 'multipv': 1, 'score': PovScore(Mate(+2), BLACK), 'nodes': 192017, 'nps': 771152, 'hashfull': 45, 'tbhits': 0, 'time': 0.249, 'pv': [Move.from_uci('c2c1'), Move.from_uci('e5e1'), Move.from_uci('c1e1')]}
{'depth': 20, 'seldepth': 9, 'multipv': 2, 'score': PovScore(Cp(0), BLACK), 'nodes': 192017, 'nps': 771152, 'hashfull': 45, 'tbhits': 0, 'time': 0.249, 'pv': [Move.from_uci('c2g2'), Move.from_uci('g1h1'), Move.from_uci('g2h2'), Move.from_uci('h1g1')]}
{'depth': 20, 'seldepth': 28, 'multipv': 3, 'score': PovScore(Cp(0), BLACK), 'nodes': 192017, 'nps': 771152, 'hashfull': 45, 'tbhits': 0, 'time': 0.249, 'pv': [Move.from_uci('g3f4'), Move.from_uci('g6g7'), Move.from_uci('c2c8'), Move.from_uci('e5a5'), Move.from_uci('c8g8'), Move.from_uci('a5a7'), Move.from_uci('f4e5'), Move.from_uci('g1g2'), Move.from_uci('e5f6'), Move.from_uci('a7a6'), Move.from_uci('f6g5')]}

Anmerkung: der ein oder andere Leser wird bemerkt haben, dass wir obige Jupyter-Notebook-Anwendung nicht unter MS-Windows ausgeführt haben. Tatsächlich hindert uns momentan ein lästiger bug mit nicht ganz klarer Ursache (wahrscheinlich Jupyter in Verbindung mit dem Python chess Modul unter MS-Windows) daran, den Aufruf „chess.engine.SimpleEngine.popen_uci(engine_path)“ durchzuführen.
Der Ausweg, den ich gewählt habe ist, die selbe Jupyter Anwendung unter Linux (Ubuntu 24.04) auszuführen. Dort tritt der Fehler nicht auf.
Gleichfalls tritt der Fehler nicht auf, wenn man die selbe Python-Anwendung nicht in einem Jupyter Notebook, sondern aus der Windows Eingebeaufforderung heraus ausführt. Wir hoffen, dass der Fehler bald behoben sein wird.