Markus Himmel 6ab01a7147 test
2016-08-31 18:03:28 +02:00
2016-08-31 18:01:20 +02:00
2016-08-23 17:47:49 +02:00
2016-08-27 14:28:55 +02:00
2016-08-31 18:03:28 +02:00

Morris

Morris ist eine Implementation des klassischen Mühlespiels für die IT-Talents Code Competition. Die Kerngedanken beim Design der Architektur der Software waren Modularität und Erweiterbarkeit. Konkret heißt das, dass man bei Morris nicht nur auf einer GUI gegen eine KI spielen kann, sondern vollkommen frei und beliebig verschiedene GUIs, Methoden der Benutzereingaben und KIs an- und abschalten kann. Die Kernsoftware stellt bereits einige dieser Module bereit, allerdings ist es sehr einfach, selbst neue KIs und Benutzeroberflächen hinzuzufügen. Dafür ist es nicht notwendig, den Quellcode von Morris selbst zu modifizieren (tatsächlich ist es nicht einmal notwendig, den Quellcode zu besitzen, die kompilierte ausführbare Datei ist ausreichend, um die Software zu erweitern). Mehr dazu weiter unten.

Inhalt dieser Readme-Datei

  1. Benutzung der Software
  2. Übersicht
  3. Regelwerk
  4. Der Controller
  5. Ein- und Ausgabesysteme
  6. Die verfügbaren KIs
  7. Malom2Morris 1. Was ist das? 2. Kompilation und Datenbankerstellung
  8. Überblick über die Architektur der Software und den Quellcode
  9. Hinweise zum Lesen des Quellcodes
  10. Grobüberblick über die Architektur
  11. Verzeichnis nicht automatisch generierter Quellcodedateien
  12. Selbst Erweiterungen Entwickeln
  13. Basics
  14. Attribute

Benutzung der Software

Übersicht

Beim Start der Software öffnet sich der sogenannte Controller. Hier kann festgelegt werden, wer gegen wen spielt (dabei kann es sich sowohl um menschliche Spieler als auch KIs handeln, das heißt es sind auch Runden von Mensch gegen Mensch oder KI gegen KI möglich) und auf welchen Formen von GUI dies angezeigt werden soll. Wenn das Spiel gestartet wird, öffnen sich dann die ausgewählten GUIs, auf denen dann das Spiel gespielt bzw. angeschaut werden kann.

Regelwerk

Die vorliegende Implementation stützt sich auf die Mühle-Regeln nach [1] mit einer Zusatzregel. Folgende Regeln kommen zum Einsatz:

  • Wenn in einem Zug mindestens eine Mühle geschlossen wird, wird immer exakt ein Stein entfernt. In anderen Worten: Wenn zwei Mühlen geschlossen werden, darf trotzdem nur ein Stein entfernt werden. Befinden sich alle gegnerischen Steine in einer Mühle, darf ein beliebiger gegnerischer Stein entfernt werden.
  • Das Spiel endet unentschieden, wenn eine Spielsituation einmal wiederholt wird
  • Das Spiel endet außerdem unentschieden, wenn für 50 Züge kein Stein platziert oder geschlagen wurde.
  • Ansonsten verliert ein Spieler, wenn er nur noch zwei Steine hat oder keinen gültigen Spielzug machen kann.

Der Controller

Eine Abbildung des Controllers

Der Controller kontrollliert das aktuelle Spiel. Die beiden Dropdowns legen fest, welche KI oder welcher Spieler die entprechende Partei im Spiel kontrollieren. Durch die Liste "Anzeigen" wird festgelegt, auf welchen Formen der GUI das Spiel angezeigt wird. Mehrfachauswahlen sind hier möglich. Sowohl die Anzeigen als auch die Spieler/KIs können geändert werden, während ein Spiel im Gang ist.

Der Slider "Verzögerung" legt fest, wie lange gewartet werden soll, bis nach einem Zug der nächste Zug angefordert wird. Diese Funktion kann verwendet werden, um Spiele zwischen zwei KIs in einem angenehmen Tempo anzuschauen. Ein Klick auf "Neues Spiel" bricht ggf. das aktuelle Spiel ab und startet ein neues Spiel.

Auf der rechten Seite des Controllers befindet sich eine Liste mit den bisherigen Spielzügen. Mit einem Doppelklick auf einen Spielzug wird das Spiel bis zu diesem Spielzug "zurückgespult".

Mit dem Button "Assembly laden..." können Module geladen werden, die weitere KIs oder GUIs enthalten.

Ein- und Ausgabesysteme

Morris stellt von Haus aus zwei Möglichkeiten bereit, ein Spiel anzusehen und zu Spielen: Ein Konsoleninterface und eine GUI.

Konsole

Eine Abbildung der Konsole

Die Konsole ist in der Lage, das Spielfeld wie in der obigen Abbildung zu zeichnen und Züge entgegenzunehmen. Züge müssen sich im selben Format wie im Controller befinden (mit Ausnahme der Großschreibung), das heißt:

  • A1 bedeutet, dass ein neuer Stein auf A1 gelegt werden soll.
  • A1-D1 bedeutet, dass der Stein auf A1 nach D1 geschoben werden soll.
  • ,G1 an einen Zug anzuhängen bedeutet, den gegenerischen Stein auf G1 zu entfernen

Sollte ein eingegebener Zug nicht dem Format entsprechen oder in der aktuellen Spielsituation ungültig sein, fragt die Konsole direkt nach einem neuen Zug.

GUI

Eine Abbildung der GUI

Die GUI sollte im Großen und Ganzen selbsterklärend sein. In der Statuszeile oben steht, welche Form von Benutzereingabe gerade erwartet wird. Spielsteine können wie erwartet durch Drag&Drop gezogen bzw. per Mausklick gesetzt werden.

Die verfügbaren KIs

Morris stellt ein paar KIs bereit:

Name Beschreibung
Zufalls-KI Die dümmste aller KIs. Sie wählt einen zufälligen gültigen Spielzug aus.
Dumme KI Eine sehr eindimensional denkende KI, die nicht vorausdenkt, sondern einfach nur den besten direkt folgenden Zug basierend auf einer schwachen Heuristik auswählt, der sehr anfällig für lokale Maxima ist.
Einfacher Negamax Eine KI, die den Negamax-Algorithmus in einfacher Form realisiert und vier Züge vorausdenkt, sich aber zum Teil ein paar Sekunden Zeit für einen Zug lässt.
Malom3: Heuristische KI* Eine recht fortgeschrittene KI basierend auf dem Minimax-Algorithmus mit Alpha-Beta-Pruning.
Malom3: Perfekter Spieler* Eine KI, die basierend auf einer im Voraus berechneten Datenbank stets den spieltheoretischen Wert der Spielsituation erreicht (d.h., wenn es bei perfekt spielendem Gegner möglich ist, in der Situation zu gewinnen, gewinnt sie auch). Es ist nicht möglich, gegen diese KI besser als unentschieden zu spielen.

*) Diese KIs sind im Modul Malom2Morris enthalten. Siehe hierzu den nächsten Abschnitt.

Malom2Morris

Was ist das?

Das klassische Mühlespiel wurde Mitte der 90er zum ersten Mal gelöst (im Sinne von, es wurde bewiesen, dass zwei perfekte Spieler stets unentschieden spielen) [1]. Was damals noch Supercomputer vorraussetzte, kann mittlerweile auf dem eigenen Rechner nachgemacht werden. 2014 haben Wissenschaftler aus Ungarn eine Software namens Malom3 veröffentlicht, die (unter anderem) einen perfekten Spieler für das herkömmliche Mühlespiel implementiert [2]. Wäre es nicht cool, wenn diese perfekte KI auch in dieser Plattform, Morris, verfügbar wäre?

Zu diesem Zweck habe ich das Modul Malom2Morris entwickelt, welches sich in Malom einklinkt und die beiden in Malom enthaltenen Computerspieler für Morris verfügbar macht. Dies wird realisiert, indem Malom2Morris den Spielzustand von dem in Morris verwendeten Format in das Malom3-Format übersetzt, den Malom3-Spieler dann mit dieser übersetzten Spielinformation füttert und dann den berechneten Zug vom Malom3-Format wieder in das Morris-Format übersetzt, sodass dieser dann von Morris verwendet werden kann.

Kompilation und Datenbankerstellung

Anmerkung: Malom3 stützt sich auf eine ca. 78 GB große Datenbank, in der sich praktisch alle Mühlespielsituationen befinden. Da diese Datenbank Online nicht verfügbar ist, muss sie auf dem eigenen Rechner berechnet werden. Dies benötigt mindestens 16GB Arbeitsspeicher, logischerweise 78GB freien Festplattenspeicher und ca. 24 Stunden Rechenzeit. Sehr geehrte Mitarbeiter von IT-Talents und der Gauselmann-AG, ich kann verstehen, wenn Ihnen das für eine Einsendung zu viel Aufwand ist. Für diesen Fall finden sie hier ein Video, wie es aussieht, gegen diese Spieler zu spielen. Ich kann aber auch wärmstens empfehlen, sich die Mühe zu machen und selbst gegen diese KI zu spielen, da der vorausschauende Spielstil des perfekten Spielers sehr faszinierend ist und gleichzeitig beeindruckend, wie man vollständig in Grund und Boden gespielt wird.

Um Malom3/Malom2Morris zu kompilieren wird Visual Studio 2015 benötigt. Die kostenlose Community Edition kann hier heruntergeladen werden.

  1. Der Quellcode von Malom3 und Malom2Morris kann hier als Zip-Datei heruntergeladen werden. Die Zip-Datei sollte dann entpackt werden.
  2. Als nächstes sollte die Datei Malom_megoldas.sln im Ordner Malom_megoldas mit Visual Studio geöffnet werden.
  3. Wählen Sie in den drei Dropdowns in der Toolbar die Einträge Release, Any CPU und Controller.

Auswahl in der Toolbar

  1. Klicken Sie rechts im Solution Explorer mit der rechten Maustaste auf Controller und wählen Sie Properties.

Properties

  1. Wählen Sie in dem sich öffnenden Dialog den Reiter Debug und fügen Sie bei Working directory den Pfad zu dem Ordner working ein, der mit der Zip-Datei entpackt wurde.

Working directory

  1. Öffnen Sie durch den Solution Explorer die Datei common.h wie abgebildet.

common.h

  1. Navigieren Sie in der Datei zu Zeile 145. Ersetzen Sie den Dateipfad dort zu dem Ordner dataaux, den Sie mit der Zip-Datei entpackt haben.

Pfad ersetzen

  1. Machen Sie den Eintrag Morris an der abgebildeten Stelle im Solution Explorer ausfindig. Entfernen Sie ihn mit der Entfernen-Taste.

Referenz

  1. Rechtsklicken Sie auf den Eintrag References und wählen Sie Add Reference...

Add

  1. Klicken Sie in dem Dialog unten auf Browse, navigieren Sie zu Morris.exe, die Sie entweder heruntergeladen oder kompiliert haben und bestätigen Sie die Auswahl. Schließen Sie den Dialog dann mit OK.

Confirm

  1. Wählen Sie im Menü Build nun Build Solution.

Build

  1. Warten Sie ca. 30 Sekunden, bis am unteren Rand des Fensters Rebuild All succeeded erscheint.

Rebuild All succeeded

  1. Klicken Sie in der Toolbar nun auf Start.

Start

  1. Das Fenster zur Berechnung der Datenbank öffnet sich. Setzen Sie einen Haken bei Automatic.

Auto

  1. Warten Sie ca. 24 Stunden, bis beide Fortschrittsbalken dreimal durchgelaufen sind. Sobald die Berechnung abgeschlossen ist, öffnet sich ein Dialog mit dem Inhalt Everything Done.

Fertig

  1. Kopieren Sie nun die 9 Dateien, die sich im Ordner Malom2Morris-1.0\Malom_megoldas\Malom2Morris\bin\Release, befinden, in den Ordner Malom2Morris-1.0\working.

Kopiert

  1. Öffnen Sie nun Morris.exe und importieren Sie durch Klick auf Assembly laden... Malom2Morris.dll.

Laden

  1. Fertig! Sie können nun gegen den perfekten Spieler verlieren.

Verloren

Überblick über die Architektur der Software und den Quellcode

Hinweise zum Lesen des Quellcodes

Die Software ist in C# 6 verfasst. Ob der längeren Liste der erlaubten Sprachen gehe ich davon aus, dass nicht jeder Leser dieses Quellcodes zu einhundert Prozent mit dieser Sprache vertraut ist. Da der Code davon extrem starken Gebrauch macht, empfehle ich dem geneigten Lesen, sich vor der Lektüre des Codes, falls notwendig, mit Lamda-Ausdrücken und LINQ (wieder) vertraut zu machen.

Grobüberblick über die Architektur

Wie eingangs erwähnt lag der Kernfokus beim Design der Applikation auf Modularität. Die Software besteht aus mehreren relativ autonomen Teilen: Zentral ist die Kernlogik (Codedateien im Ordner Core). Hier befindet sich die Logik des Mühlespiels in Klassen wie Game, GameState, etc., in denen das Spiel selbst ausgetragen wird. Die Kernlogik kommuniziert über zwei Punkte mit dem Rest des Projekts: Auf der einen Seite über die Klasse Game, weil mit dieser ein tatsächliches Mühle-Spiel erstellt und verwaltet werden kann; auf der anderen Seite über die beiden Interfaces IMoveProvider und IGameStateObserver, die auf austauschbare Weise einen Spieler bzw. eine Anzeige kapseln. Klassen, die damit zutun haben, das Spiel zu kontrollieren, befinden sich im Ordner Control. Klassen, die Spieler oder Anzeigen bereitstellen, befinden sich in den Ordner UI und AI.

Jede Klasse besitzt im Quellcode einen <summary>-Block, der erklärt, was die Funktion der Klasse ist. Weiter möchte ich in dieser Readme-Datei über den Code auch gar nicht verlieren, da ich den Versuch unternommen habe, den gesamten Quellcode leserlich zu gestalten und zu kommentieren (wobei ich bitte, meine teilweise etwas ausschweifenden Kommentare zu entschuldigen).

Zum Quellcode vom Malom2Morris

Der Großteil des Codes im Malom2Morris-Repository wurde nicht von mir verfasst, sondern ist Teil von Malom. Die einzige vollständig von mir verfasste Datei ist die Brücke selbst, welche sich in der Datei BridgedPlayer.cs befindet. Weiterhin habe ich am Malom3-Quellcode diverse Änderungen (Hacks) unternommen, die notwendig waren, weil a) die KIs in Malom3 sehr eng gekoppelt mit der Benutzeroberfläche sind und Änderungen notwendig waren, um überhaupt an die Züge heranzukommen und b) als indirekte Konsequenz aus Grund A eine anderes Threading-Konzept als Morris hat, sodass weitere Änderungen notwendig waren, damit der Zug dann auf dem richtigen Thread verfügbar war. Diese Änderungen sind nicht sonderlich interessant, falls aber auch irgendeinem Grund dennoch Interesse daran besteht, können die Änderungen hier eingesehen werden (dafür ist das Projekt ja Open Source).

Literatur

  1. Gasser, Ralph. "Solving nine mens morris." Games of no chance, MSRI Publications 29 (1998): 101-113. Online verfügbar unter http://library.msri.org/books/Book29/files/gasser.pdf.
  2. http://compalg.inf.elte.hu/~ggevay/mills/ (zuletzt abgerufen am 31. August 2016).
Description
.NET GUI for Nine Men's Morris
Readme MIT 492 KiB
Languages
C# 96.7%
F# 3.3%