Schönheit First
Mich interessiert zuerst, wie ich in SVG ein äußerlich passables Go-Brett mit Steinen hinbekomme. Im Netz finde ich ein mit Inkscape erzeugtes welches. Der SVG-Code ist gruselig: Die Koordinaten für die Gitterpunkte des Brettes haben etliche Nachkommastellen und die Datei enthält viel zu viele Knoten, welche ich nicht verstehe und nicht brauche. Unverständlicher, generierter Code, den ich nicht programmatisch manipulieren möchte, da ich an die Koordinaten und Knoten schlecht heran komme. Also schreibe ich mir meinen SVG-Code anhand von W3C-Spezifikation und Beispielen selber. Als erstes erzeuge ich mir mit dem SVG-Attribut ViewBox ein eigenes Koordinatensystem. Dieses Koordinatensystem lässt sich als Auflösung auffassen. Ich sage damit, wie groß meine Auflösung immer ist, egal, wieviel Platz ich tatsächlich zur Verfügung habe. Das macht mein SVG-Dokument skalierbar.
Für das Holzbrett zeichne ich nun ein Rechteck in hellbraun. Da ich unbedingt eine Art Holzmaserung sehen will und andernfalls nicht auf das Brett gucken kann, definiere ich mir für das Rechteck einen Filter, der aus den Effekten feTurbulence und feDiffuseLighting besteht.
Das Gitter zu zeichen ist trivial, doch das mache ich lieber in Java mit einer for-Schleife, da ich dann alle Abstände zentral ändern kann, statt 19 mal 19 Linien händisch zu programmieren. Um die Steine zu zeichnen definiere ich mir für schwarze und weiße Steine jeweils einen radialGradient-Knoten und einen Filter für den Schatten. Nachdem ich probeweise einige Steine gezeichnet habe, sehen diese zwar ganz reizend aus, aber wenn der Schatten zu groß ist, fällt er über den benachbarten Stein. Ich finde auf Anhieb keine Lösung, bin aber damit zufrieden, wenn der Schatten nur 1 Pixel breit ist und dieser bei einem Abstand eines Steines vom nächsten von ebenfalls 1 Pixel nur noch aufs Brett fällt.
Nun fange ich an, für ein Spiel Stein für Stein aufs Brett zu zeichnen, indem ich die ‘Logik’ ausblende. Die Logik besteht daraus, dass auch Steine vom Brett genommen werden müssen, wenn ein gesetzter Stein eine Gruppe gegnerischer Farbe berührt und dieser die letzte Freiheit nimmt. Ich möchte am liebsten pro Zug alles neu darstellen und alle Steinknoten dem SVG Dokument neu hinzufügen. Doch je mehr Steine ich hinzufüge, desto länger dauert es. Da die Verzögerung deutlich erkennbar ist, brauche ich eine Lösung. Im Netz kursiert der Vorschlag, vom SVG-Dokument ein Fragment abzuspalten und alles nötige zuerst dem Fragment anzuhängen, bevor das Fragment als Ganzes in das Dokument eingefügt wird. So müsse die Darstellung des Dokuments nur einmal gerechnet werden. Leider schafft auch dieser Vorschlag keine wesentliche Abhilfe. Deshalb gebe ich jedem Steinknoten eine eindeutige ID und füge nur einen neuen Knoten hinzu, wenn es ihn noch nicht gibt.
Logik
Da die Darstellung der kleinen Anwendung nun flüssig läuft und schön aussieht, kann ich mich nun der Logik zuwenden. Ein Zug verändert den Zustand des Brettes. Ein Zustand ist vom vorherigen Zustand abhängig. Wie kann ich es erreichen, dass ich wahllos zu irgendeinem Brettzustand eines Spiels springen kann, z.B. Zug n? Ich muss für Zug n den Zustand aus den voran gegangenen n-1 Zuständen errechnen. Meine eigenen Gedanken zum Thema Freiheiten von Steinen berechnen und diese Steine nahtlos vom Brett entfernen, erscheinen mir sehr kompliziert und ich schaue mir den offenen Quellcode von qgo an. Dieser Code ist in C++ geschrieben und ich finde sofort, was ich brauche. In dem qgo-Modell existieren Gruppen von Steinen. Beim Setzen eines Steins wird geprüft, ob er einer Gruppe angehört. Dabei wird Stein für Stein geschaut, ob der neue Stein von der Position her ein nördlicher, südlicher, westlicher oder östlicher Nachbar sein könnte. Gehört er zwei Gruppen an, werden die zwei Gruppen zu einer Gruppe verbunden und der Stein hinzugefügt. Gehört er keiner Gruppe an, wird aus ihm eine neue gebildet. Weiter wird pro Zug der Stand der Freiheiten der Gruppen anhand des Brettzustands berechnet. Hat mindestens ein Stein einer Gruppe einen benachbarten Punkt, der weder von einem weißen noch einem schwarzen Stein besetzt ist, besitzt die Gruppe Freiheit. Hat die Gruppe keine mehr, wird sie gelöscht, nachdem Stein für Stein dieser Gruppe vom Brett genommen wird. Es wird also pro Zug allerhand berechnet, aber das geht immer noch schneller, als ich klicken kann.
Zum Schluß hätte ich gerne noch alle 40.000 Spiele, die ich in einer viele MB großen Datei beherberge, in einer übersichtlichen Tabelle. Dort möchte ich Spiele anhand von Spielernamen, Turnier und Datum filtern können. Dies habe ich mir schon einmal mit F# in .NET programmiert. Aber wie ich festellen muss, sind es in Java mehr Zeilen und geht nicht so leicht von der Hand. Die string.split() Funktion in Java ist nämlich viel zu langsam, da sie einen regulären Ausdruck als Argument erwartet. Zum parsen des Strings, der alle Spiele enthält, muss ich ihn char für char begutachten, mir Positionen von Spieltrennern merken und pro Spiel einen Substring aus dem Gesamtstring extrahieren. Auch das läuft nach einigen Optimierungen flüssig, so dass der Import nur ein paar Sekunden dauert.
Sogar mein 4-jähriger Sohn kann auf diese Weise Profi-Partien nachspielen! Züge können in der geschwindigkeitsverstellbaren Animation äußerst gut gefunden werden, statt in einem PDF-Dokument, welches 50 Züge als Zahlen beinhaltet, suchen zu müssen.
