Masterarbeit Alpha-Beta-Pruning Oliver Kock Bochum, Februar 2009 Fakultät für Mathematik Ruhr-Universität Bochum Inhaltsverzeichnis Kapitel I. Einleitung 4 Kapitel II. Grundlagen der Spieletheorie II.1. Zwei-Personen-Nullsummenspiele mit perfekter Information II.2. Spielbäume II.3. Auswertung eines Spielbaums II.4. Prinzipieller Aufbau eines Computerprogramms II.5. Der Bestimmtheitssatz von Zermelo 6 Kapitel III. Alpha-Beta-Pruning III.1. Beschreibung eines Vorläufers III.2. Alpha-Beta-Pruning: Beschreibung, Algorithmus und Korrektheitsbeweis III.3. Analyse des besten Falls III.4. Analyse des schlechtesten Falls III.5. Allgemeinere Ergebnisse 7 8 10 12 14 18 18 21 25 31 32 Kapitel IV. Spielabhängige und allgemeine Modifikationen des Alpha-Beta-Prunings IV.1. Vorsortierung der Züge IV.2. Hash-Funktionen IV.3. Endspieldatenbanken IV.4. Suchfenstertechnik IV.4.1. F-Verbesserung IV.4.2. L-Verbesserung IV.5. Nullfenster-Suchverfahren IV.5.1. Negascout-Verfahren IV.5.2. F-Verbesserung IV.5.3. L-Verbesserung IV.5.4. TL-Verbesserung 33 33 34 38 41 41 43 43 44 46 47 47 Literaturverzeichnis 49 3 KAPITEL I Einleitung Schach ist sicherlich eines der bekanntesten Brettspiele der Welt. Allein die Strategie der zwei Spieler ist für den Sieg ausschlaggebend. Es gibt keine zufälligen Elemente. Auch können dem Gegner keine wichtigen Informationen vorenthalten werden, wie es bei den meisten Kartenspielen der Fall ist. Dort hält man im Allgemeinen Karten auf der Hand, die der Spielpartner nicht einsehen kann. Schach oder allgemeiner eine Klasse von Spielen, die ähnliche Eigenschaften wie Schach aufweisen, bieten sich damit für die Erforschung künstlicher Intelligenz besonders an. Ziel dieser Arbeit wird es sein, für diese Klasse von Spielen einen möglichst starken automatischen Spielpartner im Form eines Computerprogramms zu konstruieren. Die zentrale Frage, die es zu beantworten gilt, ist: Welcher Spielzug ist in der aktuellen Situation optimal? Ein mathematischer Ansatz zur Lösung besteht darin, alle denkbaren Züge zu verfolgen, bis eine Endstellung erreicht ist. Dort lässt sich ablesen, welcher der beiden Spieler gewonnen hat. Durch vollständige Suche wird so ein exaktes Ergebnis für die Optimalität eines Zuges berechnet. Eine ausführliche Herleitung dieser grundlegenden Spielbaumsuche sowie Details zur Klasse der Spiele, auf die dieser Algorithmus anwendbar ist, finden sich in Kapitel II. Als theoretische Grundlage dient der Bestimmtheitssatz von Zermelo, welcher in Abschnitt II.5 formuliert und bewiesen wird. Nun gibt es Spiele, die relativ komplex sind. Hier ist ein hoher Verzweigungsgrad des Spielbaumes gemeint, d.h. die Zahl möglicher Züge pro Situation ist relativ hoch. Für komplexe Spiele kann diese simple Spielbaumsuche nicht vollständig durchgeführt werden, weil sie zu lange dauern würde. In Kapitel III dieser Arbeit wird ein Algorithmus vorgestellt, der als Verbesserung der normalen Spielbaumsuche interpretiert werden kann. Spielzüge, die nicht zu einem besseren Ergebnis führen können, werden direkt erkannt und brauchen nicht näher untersucht werden. Dieser Algorithmus heißt Alpha-Beta-Pruning. Er wird in Kapitel III ausführlich hergeleitet und es wird bewiesen, dass das Ergebnis des Algorithmus korrekt ist. Außerdem erfolgt eine kurze 4 I. EINLEITUNG 5 Analyse der Laufzeit und es wird eine gewisse Optimalität gezeigt. In der Praxis wird das Alpha-Beta-Pruning häufig mit Techniken kombiniert, wie sie in der Informatik gebräuchlich sind. Die ersten Abschnitte des Kapitels IV dienen der Vorstellung dieser Techniken. Exemplarisch seien hier die beiden Stichworte Hash-Funktion und Endspieldatenbank genannt. Abschließend werden in den Abschnitten IV.4 und IV.5 einige Modifikationen des Alpha-Beta-Prunings präsentiert, die kleinere Schwächen beheben sollen. Sie können als optimierte Varianten des ursprünglichen Algorithmus verstanden werden. KAPITEL II Grundlagen der Spieletheorie Bei Spielen bestimmen mehrere Faktoren darüber, wer am Ende gewinnt. Im Wesentlichen lassen sich diese jedoch auf zwei verschiedene Ursachen eingrenzen: 1. Zufall 2. Strategie Bei Gesellschaftsspielen tritt der Zufall vor allem in Form eines Würfels auf. Auch das Mischen von Spielkarten bewirkt einen zufälligen Einfluss auf das Spielergebnis. Als Beipiele für reine Glücksspiele seien hier Lotto und Roulette genannt. Aber nur wenige Spiele hängen einzig vom Zufall ab. Bei vielen Spielen ist über eine zufällige Komponente hinaus zusätzlich strategisches Geschick notwendig, um gewinnen zu können. Etwa bei Skat, Kniffel oder Mensch ärgere dich nicht“ ist ” dies der Fall. Schließlich gibt es noch solche Spiele, bei denen die Zufallskomponente gar keine Rolle spielt, z.B. Schach oder Go. Sie werden kombinatorische Spiele genannt. Ihr Schwierigkeitsgrad besteht allein aus einer großen Anzahl von möglichen Zügen. Bevor die Betrachtung dieser kombinatorischen Spiele näher präzisiert wird, folgt zunächst eine formale Definition eines Spiels: Definition II.0.1. Ein Spiel wird durch seine Spielregeln charakterisiert. Folgende Fragen müssen darin beantwortet werden: • Wie viele Spieler sind zu dem Spiel zugelassen? • Für jeden möglichen Spielstand: – Wer ist am Zug? – Welche Zugmöglichkeiten bestehen für den ziehenden Spieler? – Auf Basis welcher Informationen hat er seine Entscheidung zu treffen? • Für Spielstände, an denen das Spiel beendet ist: Wer hat wieviel gewonnen? • Falls Zufall im Spiel ist: Wie wahrscheinlich sind die möglichen Ergebnisse? Falls ein Spieler während eines Spiels die Gelegenheit hat, im Rahmen der Spielregeln zu handeln, so wird dieser Spielabschnitt als Spielzug 6 II.1. ZWEI-PERSONEN-NULLSUMMENSPIELE 7 bezeichnet. Besonders bei der Betrachtung von kombinatorischen Spielen kommt eine zentrale Frage auf: Wie findet man einen optimalen Spielzug? Hier gibt es schliesslich (mindestens) einen besten Zug, da zufällige Einflüsse ausgeschlossen sind. Im Folgenden wird ein Teilbereich der kombinatorischen Spiele definiert, in dem diese Frage durch einen Algorithmus beantwortet werden kann. II.1. Zwei-Personen-Nullsummenspiele mit perfekter Information Definition II.1.1. Ein gegebenes Spiel heißt Zwei-Personen-Nullsummenspiel mit perfekter Information, falls folgende Kriterien erfüllt sind: • Das Spiel wird von zwei Personen gespielt. • Der Gewinn des einen Spielers entspricht dem Verlust des anderen Spielers, d.h. Verlust wird als negativer Gewinn interpretiert und die Summe beider Gewinne beträgt Null. • Sämtliche Informationen über den erreichten Spielstand liegen beiden Spielern offen. • Der Zufall ist ausgeschlossen. • Jeder Spieler hat zu jedem Zeitpunkt nur endlich viele Zugmöglichkeiten. • Das Spiel endet nach einer begrenzten Anzahl von Zügen. Warum gerade diese Forderungen an zu untersuchende Spiele gestellt werden, wird in Abschnitt II.5 deutlich. Dort wird ausgeführt, dass der Bestimmtheitssatz von Ernst Zermelo für Zwei-Personen-Nullsummenspiele mit perfekter Information gilt und welche Konsequenzen diese Tatsache für die Suche nach einem optimalen Zug hat. Es wird für das gesamte Kapitel II davon ausgegangen, dass ein Zwei-PersonenNullsummenspiel mit perfekter Information vorliegt, wenn von einem Spiel gesprochen wird. Bemerkung II.1.2. In Definition II.1.1 wird nicht explizit gefordert, dass die Spieler abwechselnd ziehen. Gleichzeitige Züge sind jedoch gemäß dem dritten Punkt der Definition ausgeschlossen, so dass von einer abwechselnden Zugfolge ausgegangen werden kann. Definition II.1.3. Eine Situation im Spiel, wie sie vor und nach Spielzügen auftreten kann, heißt (Spiel-)Position. Sie enthält alle Angaben über den aktuellen Spielstand sowie die Angabe, welcher Spieler gerade am Zug ist. Eine Position, in der ein Spiel endet, heißt Endposition. II.2. SPIELBÄUME 8 Ein Computerprogramm kann den Ausgang eines Spiels und speziell die damit verbundenen Gewinne nur in Endpositionen verlässlich bewerten, da nur dort die Gewinne bzw. Verluste direkt ablesbar sind. Zur Bewertung der Gewinnaussichten in anderen Situationen müssten Schätzwerte herangezogen werden. Daraus ergibt sich direkt ein wesentlicher Unterschied zwischen einer menschlichen Strategie und einer computergestützten Analyse: • Computer können (zumindest theoretisch) jeden möglichen Zug inklusive aller Antworten des Gegners betrachten. Kein Spielzug wird im Voraus ausgeschlossen, da erst am Ende des Spiels ablesbar ist, ob eine Zugfolge von Erfolg gekrönt ist. Zumindest wird erst nach einigen Doppelzügen ein verläßlicher Schätzwert für die Gewinnaussichten erzielt. • Menschen hingegen wiegen nur einige wenige Züge gegeneinander ab. Es entwickelt sich ein Gefühl dafür, welche Züge erfolgsversprechend sind und schlechte Züge werden intuitiv ausgeschlossen. Die Details einer Computersuche werden im Laufe dieses Kapitels entwickelt. Hier soll nur ein erster Eindruck der grundlegenden Strategie vermittelt werden. II.2. Spielbäume Zur grafischen Darstellung der Suche nach einem optimalen Zug eignen sich Bäume besonders gut. Sie definieren eine hierarchische Struktur auf einer Menge von Elementen. Ein bekanntes Beispiel eines Baums ist ein Familien-Stammbaum. Die folgenden Definitionen führen Bäume ein, wie sie als Veranschaulichung von Spielanalysen verwendet werden können. Definition II.2.1. Ein Baum ist ein Paar (K, Rer ), wobei K eine Menge von beliebigen Elementen ist, die Knoten genannt werden. Unter allen Knoten gibt es einen ausgezeichneten Wurzelknoten (oder kurz Wurzel ) w. Weiter wird auf der Menge K der Knoten eine zyklenfreie Relation Rer ⊂ K × K definiert (genannt Elternrelation“), so dass für jeden Knoten ” k ∈ K \ {w} genau ein Knoten ke 6= k existiert mit (ke , k) ∈ Rer . Definition II.2.2. Es werden einige Bezeichnungen definiert: • Falls (ke , k) ∈ Rer ist, heißt ke Elternknoten von k und k heißt Kind (bzw. Kinderknoten) von ke . • Knoten k, für die es kein k0 mit (k, k0 ) ∈ Rer gibt, heißen Blätter des Baumes bzw. Blattknoten oder terminale Knoten. II.2. SPIELBÄUME 9 Abbildung II.2.1. Beispiel eines Baums Alle Knoten, die keine Blätter sind, heißen nicht-terminal oder innere Knoten. Da die Definition eines Baumes doch etwas technisch ist, wird zusätzlich eine einfache Charakterisierung von Bäumen angegeben. Bemerkung II.2.3. Ein Baum kann rekursiv aufgebaut werden wie folgt: • Ein einzelner Knoten ist ein Baum. Dieser Knoten ist gleichzeitig Wurzel des Baums. • Sei k ein Knoten und B1 , B2 , . . . , Bn Bäume mit jeweiligen Wurzeln k1 , k2 , . . . , kn . Dann entsteht ein neuer Baum, indem man k zum Elternknoten von k1 , k2 , . . . , kn macht. Im neuen Baum ist k der ausgezeichnete Wurzelknoten und k1 , k2 , . . . , kn seine Kinder. In Abbildung II.2.1 ist ein typisches Beipiel eines Baums zu sehen. Die Relation Rer gibt in diesem Fall die Hierarchie des Enthalten” seins“ an. Ein Buch enthält seine Kapitel und die Kapitel enthalten wiederum Abschnitte. Links ist eine typische Darstellung in einem Inhaltsverzeichnis und rechts eine Darstellung als Baumform zu sehen. Definition II.2.4. Ein ungerichteter Graph ist ein Paar (V, E), wobei V eine Menge von Knoten und E ⊂ V × V eine Menge von Kanten (d.h. Verbindungen zwischen Knoten) ist. Weiterhin muss (b, a) ∈ E gelten, falls (a, b) ∈ E ist. Die Forderung (a, b) ∈ E ⇒ (b, a) ∈ E bewirkt, dass in der Kantenmenge keine gerichteten Kanten auftreten können, d.h. die Knoten II.3. AUSWERTUNG EINES SPIELBAUMS 10 a und b sollen so verbunden werden, dass keine explizite Richtung angegeben wird. Da später (in Abschnitt III.5) der Begriff eines Pfades und speziell die Pfadlänge benötigt wird, ist eine Verbindung zwischen Bäumen und ungerichteten Graphen hilfreich: Bemerkung II.2.5. Ein Baum B = (K, Rer ) mit Wurzel w entspricht einem ungerichteten Graphen G = (V, E) mit ausgezeichnetem −1 ist. Die Umkehrrelation Knoten w, wobei V := K und E := Rer ∪ Rer −1 Rer ist dabei definiert als −1 Rer := {(b, a) : (a, b) ∈ Rer }. G ist hierbei zyklenfrei, da Rer ebenfalls zyklenfrei ist. Definition II.2.6. Gegeben sei ein ungerichteter Graph G = (V, E). Ein Pfad P = (v1 , v2 , . . . , vn ) ist eine Folge von Knoten vi ∈ V, i = 1, 2, . . . , n, für die gilt: (vi , vi+1 ) ∈ E für alle i ∈ {1, 2, . . . , n − 1} Weiterhin gelte vi 6= vj , falls i 6= j. Die Länge des Pfades P = (v1 , v2 , . . . , vn ) ist gegeben durch die Anzahl seiner Kanten, nämlich n − 1. Aber nun zurück zum eigentlichen Thema, den Spielen. Spielabläufe können in Baumform dargestellt werden: Definition II.2.7. Ein Baum heißt Spielbaum, falls seine Knoten den Positionen des Spiels entsprechen. Ebenso entspricht die Relation Rer den Spielzügen. Genauer gilt für die Positionen p und p0 (p, p0 ) ∈ Rer , wenn es bei Spielposition p einen Spielzug gibt, der zu Position p0 führt. Mit anderen Worten: Um Spiele in Baumform darzustellen, Wird die Ausgangsstellung als Wurzel des Baumes interpretiert. Allgemein werden dann alle möglichen Positionen, die durch einen legalen Spielzug erreicht werden können, als die Kinder des Ausgangsknotens definiert. II.3. Auswertung eines Spielbaums Nachdem die Darstellung des Problems durch Bäume gelöst ist, wird näher auf die eigentliche Problemstellung eingegangen. Zur Auswertung eines Spielbaums stellt sich zunächst die Frage, wie man die Güte von Spielpositionen in Zahlen messen kann. Prinzipiell wird ein höherer Zahlwert eine bessere Position bedeuten. Doch bereits hier gibt es zwei mögliche Varianten: • Ansatz 1 : Es wird der Wert einer Position stets aus Sicht von Spieler A bewertet. Hohe Werte bedeuten dann, dass Spieler A einen Vorteil hat. Negative Werte sprechen für Spieler B. II.3. AUSWERTUNG EINES SPIELBAUMS 11 Abbildung II.3.1. Situation innerhalb eines Baums • Ansatz 2 : Der Wert einer Position wird aus Sicht desjenigen Spielers ermittelt, der gerade am Zug ist. Wieder bedeuten hohe Werte einen Vorteil für den jeweiligen Spieler. Dieser Unterschied mag vielleicht nebensächlich erscheinen. Er hat jedoch große Auswirkungen auf die folgenden Überlegungen zur Auswertung eines Spielbaums. Später werden folgerichtig auch zwei verschiedene Versionen einer Spielbaumsuche entstehen. Bereits klar ist, dass nur Endpositionen direkt ausgewertet werden können. Schließlich kann man nur an ihnen direkt den Gewinner eines Spiels ablesen. Für andere Positionen bzw. nicht-terminale Knoten im Spielbaum erfolgt eine rekursive Definition der Bewertungsfunktion F (p). Ansatz 1 : Für Blätter des Spielbaums ist Bewertungsfunktion möglich: ∞ −∞ F (p) = 0 wie erwähnt eine direkte Definition der bei Sieg für Spieler A bei Sieg für Spieler B bei Unentschieden Für nicht-terminale Knoten sei Situation II.3.1 gegeben. Die Werte der Positionen p1 , p2 , . . . , pd seien bekannt. Falls Spieler A in Position p am Zug ist, wird er einen Spielzug derart wählen, dass der Wert der Folgeposition pi möglichst hoch ist. Analog wird Spieler B einen Zug wählen, so dass der Wert der Folgeposition möglichst niedrig ausfällt: max{F (p1 ), . . . , F (pd )}, falls Spieler A am Zug ist F (p) = min{F (p1 ), . . . , F (pd )}, falls Spieler B am Zug ist So ist die Bewertungsfunktion rekursiv für alle Knoten des Baumes definiert. Bei diesem Ansatz wird stets derjenige Spieler, der an der Wurzel des Baumes am Zug ist, Spieler A genannt. Somit muss der Wert des Wurzelknotens stets maximiert werden. Ansatz 2 : Hier bezeichnet F (p) den Wert einer Position aus Sicht des Spielers, II.4. PRINZIPIELLER AUFBAU EINES COMPUTERPROGRAMMS 12 der gerade am Zug ist. Der Wert aus Sicht des anderen Spielers ist dann −F (p). Die Bewertungsfunktion für Blätter des Spielbaums lautet somit bei Sieg ∞ −∞ bei Niederlage . F (p) = 0 bei Unentschieden Für nicht-terminale Knoten sei wiederum Situation II.3.1 gegeben. Die Werte der Positionen p1 , p2 , . . . , pd seien bekannt. Der bei Position p ziehende Spieler wird einen Spielzug derart wählen, dass der Wert der Folgeposition pi aus seiner Sicht (d.h. −F (pi )) möglichst hoch ist: F (p) = max{−F (p1 ), . . . , −F (pd )} Eine Fallunterscheidung ist also hier nicht notwendig. Ziel einer Spielbaumsuche ist es nun, die Bewertung des Wurzelknotens zu finden. Sobald sie bekannt ist, kann der optimale Zug aus den Bewertungen der Kinder des Wurzelknotens abgelesen werden. II.4. Prinzipieller Aufbau eines Computerprogramms Da die Definition der Bewertungsfunktion bereits rekursiv ist, bietet es sich an, auch die Implementierung eines Computerprogramms rekursiv zu gestalten. Es werden entsprechende Java-Methoden angegeben: Algorithmus II.4.1. Diese Implementierung verfolgt Ansatz 1 aus Abschnitt II.3: 1 int valueMax(Position p){ 2 if(endposition(p)) 3 return f(p); 4 int m = - INF; 5 berechneMöglicheZüge(p); 6 while(zügeÜbrig()){ 7 Position q = führeNächstenZugAus(p); 8 m = max(m,valueMin(q)); 9 } 10 return m; 11 } II.4. PRINZIPIELLER AUFBAU EINES COMPUTERPROGRAMMS 13 14 15 16 17 18 19 20 21 22 23 13 int valueMin(Position p){ if(endposition(p)) return f(p); int m = INF; berechneMöglicheZüge(p); while(zügeÜbrig()){ Position q = führeNächstenZugAus(p); m = min(m,valueMax(q)); } return m; } Je nachdem, welcher Spieler am Zug ist, wird die jeweilige Methode aufgerufen. Der erstmalige Aufruf am Wurzelknoten w erfolgt durch valueMax(w); Zu Beginn des Algorithmus (Zeile 2 bzw. 14) wird überprüft, ob man sich an einer Endposition befindet. Falls ja, kann der Algorithmus einfach mit Rückgabe des exakten Wertes abgebrochen werden. Andernfalls muss berechnet werden, welche Spielzüge möglich sind (Anweisung 5 bzw. 17). Durch die Anweisungen 6-9 bzw. 18-21 wird erreicht, dass die Werte aller Kinderknoten rekursiv berechnet werden und der Optimalwert (d.h. das Maximum, falls Spieler A am Zug ist und das Minimum, falls Spieler B am Zug ist) in Variable m gespeichert wird. Bei Maximumsbildung (bzw. Minimumsbildung) wurde m vor Schleifenbeginn entsprechend mit −∞ (bzw. ∞) initialisiert. Anweisung 10 bzw. 22 gibt den optimalen Wert schließlich aus. Falls Ansatz 2 benutzt wird, ist beim Entwurf des Algorithmus wie schon bei der Definition der Bewertungsfunktion keine Fallunterscheidung, welcher Spieler das Zugrecht besitzt, notwendig: 1 2 3 4 5 6 7 8 9 10 11 Algorithmus II.4.2. int value(Position p){ if(endposition(p)) return f(p); int m = - INF; berechneMöglicheZüge(p); while(zügeÜbrig()){ Position q = führeNächstenZugAus(p); m = max(m,-value(q)); } return m; } II.5. DER BESTIMMTHEITSSATZ VON ZERMELO 14 Bemerkung II.4.3. • Algorithmus II.4.1 wird Minimax-Suche genannt, da abwechselnd Minima und Maxima gebildet werden. • Algorithmus II.4.2 wird entsprechend Negamax-Suche genannt. • In der Praxis sind diese naiven Algorithmen praktisch unbrauchbar, da der Aufwand zu groß ist. In Kapitel III wird gezeigt, wie die Spielbaumsuche verbessert werden kann, indem nicht mehr sämtliche Zweige des Baumes durchsucht, sondern unwichtige Teile abgeschnitten“ werden. ” • Zusätzlich verwendet man in der Praxis eine feste Suchtiefe oder auch einen Zeitmonitor. Dadurch müssen die Werte an den Blättern zwar geschätzt werden und es wird nur eine Näherung an die Bewertungsfunktion F berechnet, aber die Laufzeit rückt in eine akzeptable Größenordnung. In Abbildung II.4.1 ist ein Beispieldurchlauf der Minimax-Suche II.4.1 zu sehen. II.5. Der Bestimmtheitssatz von Zermelo Am Ende dieses Kapitels steht der Begriff der Strategie im Mittelpunkt. Mit Hilfe des Strategiebegriffs lässt sich der Bestimmtheitssatz von Zermelo präzise formulieren. Weiterhin wird in diesem Abschnitt von einer Verwendung des Minimax-Ansatzes ausgegangen. Definition II.5.1. Sei ein Spiel gegeben. Eine Strategie eines Spielers für dieses Spiel ist eine vollständige Handlungsanweisung, die für jede denkbare Position einen Spielzug vorsieht. Für theoretische Betrachtungen ist es möglich, sämtliche Entscheidungen, die während eines Spiels von Spielern getroffen werden müssen, an den Anfang zu verschieben, indem beide Spieler ihre Strategie offenlegen. Dass in der Praxis das Offenlegen einer vollständigen Strategie unmöglich oder zumindest schwierig ist, weil die enthaltenen Informationen sehr umfangreich sind, wird für folgende theoretischen Betrachtungen übersehen. Das Offenlegen der Strategie der beiden Spieler A und B kann auf verschiedene Arten geschehen: • Beide Spieler offenbaren ihre Strategie gleichzeitig. • Spieler A offenbart seine Strategie, bevor Spieler B sich entscheiden muss. • Spieler B offenbart seine Strategie, bevor Spieler A sich entscheiden muss. Ein gleichzeitiges Offenlegen der Strategie entspricht dem normalen Spiel. Es ist unerheblich, ob eine Entscheidung, die während des Spiels gefällt wird, an den Anfang verlegt wird. Wichtig ist nur, dass diese II.5. DER BESTIMMTHEITSSATZ VON ZERMELO Abbildung II.4.1. Beispieldurchlauf der Minimax-Suche 15 II.5. DER BESTIMMTHEITSSATZ VON ZERMELO 16 Entscheidung in beiden Fällen am gleichen Informationsstand ausgerichtet ist. In den beiden anderen Fällen hat jedoch einer der beiden Spieler einen Vorteil. Er kennt die Strategie seines Gegners und kann gezielt deren Schwächen ausnutzen. Falls Spieler A seine Strategie zuerst offenlegen muss, wählt er möglichst eine Strategie, bei der er unabhängig von den Erwiderungen durch Spieler B einen möglichst hohen Gewinn erhält. Spieler B wird anschließend seine Strategie darauf ausrichten, den Gewinn von Spieler A zu minimieren. Der Gewinn, den sich Spieler A trotzdem sichern kann, ist das Maximum dieser Minima und wird Maximin-Wert genannt. Falls umgekehrt Spieler B seine Strategie zuerst offenlegen muss, wird er bestrebt sein, eine Strategie zu wählen, bei der Spieler A trotz seiner Erwiderungen einen möglichst niedrigen Gewinn erhält. Spieler A wird anschließend seine Strategie darauf ausrichten, seinen eigenen Gewinn zu maximieren. Der Gewinn, den sich Spieler A so sichern kann, ist das Minimum dieser Maxima und wird Minimax-Wert genannt. Spieler A verfügt also über eine Strategie, die ihm mindestens den Maximin-Wert als Gewinn sichert und Spieler B kann den Gewinn von Spieler A auf den Minimax-Wert beschränken. Damit gilt Maximin-Wert ≤ Minimax-Wert. In dieser Situation setzt der Bestimmtheitssatz an: Satz II.5.2. Gegeben sei ein Zwei-Personen-Nullsummenspiel mit perfekter Information. Dann gilt Maximin-Wert = Minimax-Wert. Dieser übereinstimmende Wert wird auch Wert des Spiels genannt. Der Beweis dieses Satzes erfolgt per vollständiger Induktion: Induktionsvoraussetzung: Für Spiele, die nicht länger als n Züge dauern, ist der Maximin-Wert gleich dem Minimax-Wert. Induktionsanfang: Für n = 0 ist die Induktionsvorraussetzung sicherlich erfüllt. Ein solches Spiel besteht aus 0 Spielzügen und somit nur aus einer Vorschrift, wer wieviel gewinnt. II.5. DER BESTIMMTHEITSSATZ VON ZERMELO 17 Induktionsschritt: Betrachte ein Spiel, dass maximal n + 1 Züge dauert. Aus Symmetriegründen kann davon ausgegangen werden, dass Spieler A den ersten Zug macht. Jedes Endspiel“, dass sich an den ersten Zug anschließt, ” besteht aus maximal n Zügen. Der Bestimmtheitssatz gilt damit für diese Spiele. Weiterhin bezeichne v die größte Zahl unter den Werten dieser Endspiele. Spieler A zieht optimalerweise zum Endspiel mit dem Wert v. Damit ist der Maximin-Wert des gesamten Spiels v. Im Endspiel gilt nun Minimax-Wert = Maximin-Wert, so dass Spieler B sich so verteidigen kann, wie er es in diesem einzelnen Endspiel könnte. Damit ist auch der Minimax-Wert des Gesamtspiels v und der Bestimmtheitssatz gilt für das Spiel mit n + 1 Zügen. Wenn man die Menge der betrachteten Spiele so einschränkt, dass nur Sieg, Unentschieden und Niederlage möglich ist und die Höhe eines Gewinns nicht variabel ist, so lautet eine alternative Formulierung des Bestimmtheitssatzes: Korollar II.5.3. Bei Schach oder vergleichbaren Spielen tritt stets einer der drei folgenden Fälle ein: • Weiß kann einen Sieg erzwingen, unabhängig von der Spielweise von Schwarz. • Schwarz kann einen Sieg erzwingen, unabhängig von der Spielweise von Weiß. • Beide Spieler können unabhängig voneinander ein Unentschieden erreichen. Spätestens an dieser Stelle ist klar, dass ein Durchschauen des Gegners bei Spielen wie Schach unwichtig ist. Das optimale Vorgehen kann völlig unabhängig vom Gegner geplant werden, zum Beispiel mit einem Computerprogramm. KAPITEL III Alpha-Beta-Pruning In diesem Abschnitt wird das Alpha-Beta-Pruning hergeleitet und erklärt. Durch Verwendung dieses Algorithmus kann eine vollständige Durchsuchung des Spielbaums, wie sie bei einer normalen Baumsuche erforderlich ist, vermieden werden. Der Baum wird an Knoten, deren Werte für die Untersuchung des Wurzelknotens unerheblich sind, be” schnitten“ (engl.: to prune). Außerdem wird ein Korrektheitsbeweis geführt sowie eine gewisse Optimalität gezeigt. In Sätzen, deren Beweisen und bei Algorithmen wird nun im Allgemeinen die Negamax-Schreibweise benutzt, um Fallunterscheidungen zu vermeiden. Beispiele hingegen sind in Minimax-Schreibweise anschaulicher. III.1. Beschreibung eines Vorläufers Zunächst wird ein Vorläufer des Alpha-Beta-Prunings vorgestellt. Gegeben sei Situation III.1.1 in einem Spielbaum (Minimax-Schreibweise). Spieler B sei am Zug. Abbildung III.1.1. Idee 1 Gesucht sei der Wert des Knotens p. Der Algorithmus habe für p1 bereits beispielhaft den Wert FA (p1 ) = 10 ermittelt und inspiziere nun den zweiten Teilbaum (insbesondere p2 ). 18 III.1. BESCHREIBUNG EINES VORLÄUFERS 19 Wegen der Bildung des Minimums bei p gilt FB (p) = min {FA (p1 ), FA (p2 )} ≤ FA (p1 ) = 10. Falls nun FA (p21 ) ≥ 10 ist, so wird der Wert von p2 wegen der Maximumsbildung ebenfalls nicht kleiner als 10 sein. Der gesamte Teil des Baumes, der statt des ?“ noch folgen könnte, braucht somit nicht ” mehr betrachtet zu werden, da das Ergebnis FA (p) = 10 nicht mehr beeinflusst werden kann. Formell bedeutet dies die Einführung einer oberen Schranke β, so dass der (noch zu bestimmende) Suchalgorithmus F 1(p, β) folgende Bedingungen erfüllen muss: (III.1.1) F 1(p, β) = F (p), wenn F (p) < β F 1(p, β) ≥ β, wenn F (p) ≥ β Im Worten: Es muss der exakte Wert ausgegeben werden, falls F (p) kleiner als die Schranke β ist. Andernfalls soll am Rückgabewert erkennbar sein, dass das Ergebnis größer als die Schranke β war. Bemerkung III.1.1. F 1(p, β) ist durch diese Bedingungen nicht vollständig definiert. Allerdings gilt bei Forderung III.1.1 insbesondere F 1(p, ∞) = F (p) für alle Positionen p, denn bei der Wahl β = ∞ tritt immer der Fall 1 (d.h. F (p) < β) ein. Nach Bemerkung III.1.1 sind verschiedene Realisierungen des Algorithmus denkbar. Als Beispiel dient die folgende rekursive Form einer Java-Methode: 1 2 3 4 5 6 7 8 9 10 11 Algorithmus III.1.2. int value(Position p , int beta){ if(endposition(p)) return f(p); int m = - INF; berechneMöglicheZüge(p); while(zügeÜbrig() && m<beta){ Position q = führeNächstenZugAus(p); m = max(m,-value(q,-m)); } return m; } Die Unterschiede zur normalen Spielbaumsuche aus Kapitel II sind hier blau markiert. Im Folgenden wird bewiesen, dass Algorithmus III.1.2 die Bedingungen III.1.1 einhält. III.1. BESCHREIBUNG EINES VORLÄUFERS 20 Falls p eine Endposition ist (d = 0, d.h. keine möglichen Spielzüge), produziert der Algorithmus III.1.2 als Ausgabe den exakten Wert f (p), unabhängig von β. Denn es gilt endposition(p) == true (Zeile 2), so dass mit Ausgabe f (p) abgebrochen wird (Zeile 3). Der Beweis, dass die Bedingungen III.1.1 für den Aufruf value(p, beta) an allen anderen Knoten erfüllt sind, erfolgt per Induktion nach der Baumhöhe. Der Induktionsanfang besteht aus den Endpositionen und der Induktionsschritt setzt voraus, dass die Bedingungen für die Folgepositionen bereits gelten. Aber nun wird zunächst eine Invarianzbedingung bewiesen, die im weiteren Verlauf des Beweises benötigt wird: (III.1.2) Vor Beginn der i-ten Iteration der while-Schleife gilt m = max{−F (p1 ), . . . , −F (pi−1 )} Auch diese Invarianzbedingung wird durch vollständige Induktion bewiesen: Induktionsanfang: i = 1 Es gilt die Initialisierung m=-INF, die vor Beginn der Schleife geschieht. Außerdem ist max ∅ = −∞, so dass die Invarianzbedingung erfüllt ist. Induktionsschritt: i → i + 1 1. Fall: F (pi ) < −m mit m = max{−F (p1 ), . . . , −F (pi−1 )} Da die Bedingungen III.1.1 für pi gelten, gilt insbesondere value(pi , −m) = F (pi ), wenn F (pi ) < −m. Während der i-ten Iteration der while-Schleife wird mneu = max(m, −value(pi , −m)) gesetzt. Mit obiger Überlegung sowie der Induktionsvoraussetzung gilt damit mneu = max{−F (p1 ), . . . , −F (pi−1 ), −F (pi )}. 2. Fall: F (pi ) ≥ −m mit m = max{−F (p1 ), . . . , −F (pi−1 )} Auch in diesem Fall gelten die Bedingungen III.1.1 für pi , insbesondere value(pi , −m) ≥ −m, wenn F (pi ) ≥ −m. Damit ist −value(pi , −m) ≤ m und die Zuweisung mneu = max(m, −value(pi , −m)) führt wegen −value(pi , −m) ≤ m und der Induktionsvoraussetzung zu mneu = m = max{−F (p1 ), . . . , −F (pi−1 ), −F (pi )}. Nach dem Beweis der Invarianzbedingung kann nun die Korrektheit des Algorithmus gezeigt werden: Induktionsanfang: Sei p Endposition. Falls p eine Endposition ist, gilt F (p) = f (p) und value(p, β) = f (p) III.2. BESCHREIBUNG UND KORREKTHEITSBEWEIS 21 Abbildung III.1.2. Ausgangssituation für alle β (siehe oben). Damit sind beide Bedingungen in III.1.1 erfüllt. Induktionsschritt: Falls p keine Endposition ist, gibt es d mögliche Züge (siehe Ausgangssituation). Seien also p1 , . . . , pd Folgepositionen von p, die die Bedingungen III.1.1 erfüllen. 1. Bedingung: value(p, β) = F (p) für F (p) < β F (p) < β ⇒ max{−F (p1 ), . . . , −F (pd )} < β ⇒ max{−F (p1 ), . . . , −F (pi )} < β für alle i Wegen der Invarianzbedingung III.1.2 gilt nach jeder Iteration der while-Schleife m < β und der Algorithmus gibt max{−F (p1 ), . . . , −F (pd )} = F (p) aus. 2. Bedingung: value(p, β) ≥ β für F (p) ≥ β F (p) ≥ β ⇒ max{−F (p1 ), . . . , −F (pd )} ≥ β ⇒ ∃i minimal mit max{−F (p1 ), . . . , −F (pi )} ≥ β Wegen der Invarianzbedingung III.1.2 ist nach der i-ten Iteration der while-Schleife erstmals m ≥ β und der Algorithmus bricht mit der Ausgabe m ≥ β ab. III.2. Alpha-Beta-Pruning: Beschreibung, Algorithmus und Korrektheitsbeweis Zusätzlich zur Spielsituation, die zur Idee für Algorithmus III.1.2 geführt hat, wird eine weitere Situation analysiert. Sei ein Ausschnitt aus einem Spielbaum gegeben, wie er in Abbildung III.2.1 zu sehen ist und sei Spieler A am Zug. Gesucht sei der Wert des Knotens p. Der Algorithmus habe für p1 bereits den Wert FB (p1 ) = 10 ermittelt und inspiziere nun den zweiten Teilbaum. Es folgt eine zum vorigen Abschnitt ähnliche Überlegung. III.2. BESCHREIBUNG UND KORREKTHEITSBEWEIS 22 Abbildung III.2.1. Idee 2 Wegen Maximumsbildung bei p gilt FA (p) = max {FB (p1 ), FB (p2 )} ≥ FB (p1 ) = 10. Falls nun FA (p21 ) ≤ 10 ist, so wird der Wert von p2 wegen Minimumsbildung ebenfalls kleiner als 10 sein. Der gesamte Teil des Baumes, der statt des ?“ noch folgen könnte, braucht nicht mehr betrachtet zu wer” den, da das Ergebnis FA (p) = 10 nicht mehr beeinflusst werden kann. Dieses Vorgehen entspricht formal der Einführung einer unteren Schranke α (zusätzlich zum bereits eingeführten β). Der noch näher zu spezifizierende Algorithmus F 2(p, α, β) sollte folgende Bedingungen erfüllen: (III.2.1) F 2(p, α, β) ≤ α, wenn F (p) ≤ α F 2(p, α, β) = F (p), wenn α < F (p) < β F 2(p, α, β) ≥ β, wenn F (p) ≥ β In Worten: Innerhalb des Intervalls (α, β) wird der exakte Wert F (p) zurückgeliefert. Falls F (p) nicht im Intervall (α, β) liegt, soll zumindest erkennbar sein, ob der exakte Wert kleiner als α oder größer als β ist. Bemerkung III.2.1. Es gilt eine zu III.1.1 analoge Bemerkung. Insbesondere gilt F 2(p, −∞, ∞) = F (p) für alle Positionen p. Auch hier sind verschiedene Realisierungen des Algorithmus denkbar. Als Beispiel dient erneut eine rekursive Java-Methode: III.2. BESCHREIBUNG UND KORREKTHEITSBEWEIS 1 2 3 4 5 6 7 8 9 10 11 23 Algorithmus III.2.2. int value(Position p , int alpha, int beta){ if(endposition(p)) return f(p); int m = alpha; berechneMöglicheZüge(p); while(zügeÜbrig() && m<beta){ Position q = führeNächstenZugAus(p); m = max(m,-value(q,-beta,-m)); } return m; } Die Unterschiede zur normalen Spielbaumsuche aus Kapitel II sind blau markiert. Korrektheitsbeweis: Die Technik des Beweises ist vollkommen analog zu F1. Allerdings lautet hier die passende Invarianzbedingung (III.2.2) m = max{α, −F (p1 ), . . . , −F (pi−1 )}. Die zusätzliche Bedingung m < β darf oBdA vorausgesetzt werden. Ansonsten würde die while-Schleife abgebrochen und das aktuelle m ausgegeben. Es könnte keine Aussage für einen folgenden Durchlauf getroffen werden. Die Fallunterscheidung im Induktionsschritt zum Beweis der Invarianzbedingung lautet damit: 1.Fall: F (pi ) ≤ β 2.Fall: −β < F (pi ) < −m Die ausstehenden Beweisdetails können analog zu vorigem Kapitel ausgefüllt werden. Bemerkung III.2.3. Das folgende Beispiel eines Alpha-BetaPruning-Durchlaufs verwendet eine auf der Minimax-Notation basierende Version des Algorithmus: 1 2 3 4 5 6 7 8 9 10 11 int valueMax(Position p , int alpha, int beta){ if(endposition(p)) return f(p); int m = alpha; berechneMöglicheZüge(p); while(zügeÜbrig() && m<beta){ Position q = führeNächstenZugAus(p); m = max(m,valueMin(q,alpha,beta)); } return m; } III.2. BESCHREIBUNG UND KORREKTHEITSBEWEIS Abbildung III.2.2. Beispiel für Alpha-Beta-Pruning 24 III.3. ANALYSE DES BESTEN FALLS 13 14 15 16 17 18 19 20 21 22 23 25 int valueMin(Position p , int alpha, int beta){ if(endposition(p)) return f(p); int m = alpha; berechneMöglicheZüge(p); while(zügeÜbrig() && m>alpha){ Position q = führeNächstenZugAus(p); m = min(m,valueMax(q,alpha,beta)); } return m; } Bemerkung III.2.4. Zu Abbildung III.2.2: • Für jeden Knoten gibt es einen zugehörigen Aufruf des AlphaBeta-Prunings. Die Beschriftungen der Knoten geben den Rückgabewert an. Neben den Knoten ist zusätzlich das Intervall (α, β) beim Aufruf des Algorithmus angegeben. Hierbei ist die für die Abbruchbedingung (Zeile 6 und 18 in Algorithmus III.2.3) relevante Grenze fett gedruckt. • Grundsätzlich wird der Spielbaum rekursiv in PostorderReihenfolge ausgewertet, d.h. lokal werden zunächst die Werte der Kinderknoten bestimmt, um anschließend den Elternknoten berechnen zu können. Die in diesem Beispiel abgeschnittenen Knoten sind dabei grau eingefärbt. • Bei Knoten, an denen neue Intervallgrenzen vorkommen, ist durch farbige Pfeile markiert, woher sie stammen. • Welche Bedingung das Abschneiden eines Knotens (auch Cutoff“ genannt) verursacht hat, wird auch manchmal durch ” die Begriffe α-Cutoff und β-Cutoff verdeutlicht. In diesem Beispiel kommt sowohl ein α-Cutoff als auch ein β-Cutoff vor. III.3. Analyse des besten Falls Die Analyse des besten Falls beschäftigt sich mit der Frage, wieviele Knoten des Spielbaums mindestens besucht werden müssen. Um diese Frage zu beantworten, zunächst zwei Definitionen: Definition III.3.1. Gegeben sei ein Spielbaum. Man ordnet den Knoten des Baums nach folgendem Schema Koordinaten zu: • Der Wurzelknoten des Baums ist eindeutig und benötigt keine Koordinaten. III.3. ANALYSE DES BESTEN FALLS 26 • Wenn (im Teilbaum unten) p die Koordinaten a1 . . . al hat, dann ordnet man dem Knoten pi jeweils die Koordinaten a1 . . . al i zu. Abbildung III.3.1 Die folgende Definition beschäftigt sich mit kritischen Knoten. Später wird sich zeigen, dass bei einer bestimmten Sortierung des Spielbaums nur kritische Positionen vom Alpha-Beta-Pruning besucht werden. Definition III.3.2. Gegeben sei ein Spielbaum. Eine Position a1 . . . al im Spielbaum heißt kritisch, wenn mindestens eine der folgenden Bedingungen erfüllt ist: • ai = 1 für alle ungeraden i (1 ≤ i ≤ l) • aj = 1 für alle geraden j (1 ≤ j ≤ l) Die Position des Wurzelknotens heißt immer kritisch. Bemerkung III.3.3. Betrachte die Koordinaten eines Knotens im Spielbaum: • Alle ungeraden Koordinaten (a1 , a3 , a5 , . . . ) eines Knotens stehen für die Züge, die Spieler A bis zur aktuellen Position gewählt hat. • Analog stehen gerade Koordinaten (a2 , a4 , a6 , . . . ) für die Zugfolge von Spieler B. Satz III.3.4. Gegeben sei ein Spielbaum, bei dem der Wurzelknoten nicht den Wert −∞ oder ∞ hat. Weiter sei an jeder Position im Baum der erste Spielzug optimal (für den Spieler, der jeweils am Zug ist), d.h. falls a1 . . . al ein Blatt ist f (a1 . . . al ), ) sonst −F ( a . . . a 1 1 l F (a1 . . . al ) = | {z } erster Nachfolger von a1 . . . al Dann wertet der Algorithmus III.2.2 genau die kritischen Positionen des Spielbaums aus. Zum Beweis werden drei Typen von kritischen Positionen eingeführt. Definition III.3.5. Eine kritische Position al heißt • vom Typ 1, falls ai = 1 für alle i ∈ {1, . . . l} gilt. III.3. ANALYSE DES BESTEN FALLS 27 • vom Typ 2, falls ai = 1 für i ∈ {1, . . . j − 1} gilt, aj 6= 1 sowie l − j gerade ist. • vom Typ 3, falls ai = 1 für i ∈ {1, . . . j − 1} gilt, aj 6= 1 sowie l − j ungerade ist. Die (kritische) Position des Wurzelknotens p hat den Typ 1, d.h. beim anfänglichen Aufruf value(p, −∞, ∞) wird eine Typ 1-Position untersucht. Laut Voraussetzung gilt zudem, dass der Wurzelknoten nicht den Wert −∞ oder ∞ hat. Typ 1-Positionen: Eine Position p vom Typ 1 wird durch den Aufruf value(p, −∞, ∞) ausgewertet. Sofern es Folgepositionen gibt, sind sie alle kritisch. p1 ist vom Typ 1 und p2 , . . . , pd sind vom Typ 2. p1 wird durch den Aufruf value(p1 , −∞, ∞) untersucht. Da der erste Zug stets optimal sein soll, folgt F (p) = −F (p1 ). Somit gilt F (p) 6= ±∞ für alle Positionen vom Typ 1. Die Positionen p2 , . . . , pd werden durch den Aufruf value(pi , −∞, F (p1 )) untersucht, wobei max{−F (p2 ), −F (p3 ), . . . , −F (pd )} ≤ −F (p1 ) und weiter F (p1 ) ≤ F (pi ) für alle i ∈ {2, . . . , d} gilt, da p1 als erste Folgeposition bereits optimal ist. Typ 2-Positionen: Eine Position p vom Typ 2 wird durch einen Aufruf value(p, −∞, β) ausgewertet, wobei −∞ < β ≤ F (p) gilt. Bei den Aufrufen von Typen 1 und 3 ist diese Form nachgewiesen. Sofern es Folgepositionen gibt, ist p1 vom Typ 3. p2 , . . . , pd sind keine kritischen Positionen. Bei p1 gilt F (p) = −F (p1 ), da der erste Zug optimal ist. Für den zu p1 gehörigen Aufruf value(p1 , −β, ∞) gilt insbesondere ∞ > −β ≥ −F (p) = F (p1 ). Bei den Aufrufen value(pi , −β, F (p1 )) für p2 , . . . , pd ist −β ≥ F (p1 ). Damit ist die Bedingung m<beta ⇐⇒ F (p1 ) < −β (Zeile 6 in Algorithmus III.2.2) offensichtlich verletzt und der Algorithmus bricht ab, ohne p2 , . . . , pd zu untersuchen. Typ 3-Positionen: Eine Position p vom Typ 3 wird durch einen Aufruf value(p, α, ∞) ausgewertet, wobei ∞ > α ≥ F (p) gilt. Bei den Aufrufen von Typ 2 ist diese Form nachgewiesen. Sofern es Folgepositionen gibt, sind sie alle vom Typ 2. Denn nach Definition von Typ 3 ist dort die letzte Koordinate 1, da aj 6= 1 sowie l − j ungerade gilt. Zudem muss bei kritischen Positionen jede zweite Koordinate 1 sein. Die zugehörigen Aufrufe lauten value(pi , −∞, −α), i ∈ {1, . . . d} und es gilt (wegen F (p) = max{−F (p1 ), . . . , −F (pd )}) −∞ < −α ≤ −F (p) ≤ F (pi ). III.3. ANALYSE DES BESTEN FALLS 28 Abbildung III.3.2. Gegenbeispiel zur optimalen Sortierung Aus den obigen Punkten folgt induktiv, dass nur kritische Positionen untersucht werden. Außerdem werden, vom Wurzelknoten ausgehend, alle kritischen Positionen besucht. Es folgt die Behauptung. Bemerkung III.3.6. Warum es ein Vorteil ist, den ersten möglichen Zug als optimal anzunehmen, wurde nicht gezeigt. Intuitiv sollte eine frühe Untersuchung des optimalen Zuges zwar zumindest nicht schlecht sein. Jedoch gibt es durchaus Situationen, bei denen eine abweichende Sortierung bessere Laufzeiten des Alpha-Beta-Prunings liefert. Abbildung III.3.2 zeigt eine solche Situation. Im Spielbaum auf der linken Seite ist von zwei möglichen Zügen jeweils der erste optimal. Trotzdem braucht das Alpha-Beta-Pruning hier mehr Rechenschritte als bei der Anordnung rechts. Wie die Bemerkung zeigt, ist eine optimale Sortierung des Spielbaums nicht offensichtlich. Es ist aber trotzdem möglich, einen Vergleich mit beliebigen anderen Algorithmen zu treffen und eine gewisse Optimalität des Alpha-Beta-Prunings zu zeigen: Satz III.3.7. Gegeben sei ein beliebiger Spielbaum und ein beliebiger Algorithmus, der den Wert des Wurzelknotens berechnet. Dieser Algorithmus habe keine Kenntnis von eventuell vorhandenen Abhängigkeiten zwischen den Werten der Blattknoten (bzw. Endpositionen). Dann gibt es eine Permutation des Baumes (d.h. für alle Knoten des Baummes gibt es eine Umnummerierung der Kinderknoten), so dass jeder Knoten, der vom Alpha-Beta-Pruning untersucht wird, auch vom gegebenen Algorithmus untersucht wird. Wenn der Wert des Wurzelknotens III.3. ANALYSE DES BESTEN FALLS 29 weder ∞ noch −∞ ist, wertet das Alpha-Beta-Pruning genau diejenigen Knoten aus, deren Position bzgl. der Permutation kritisch sind. Zum Beweis werden zunächst für die Werte aller Knoten p in Baum bestmögliche Schranken gesucht (in NegaMax-Schreibweise): (III.3.1) falls p terminal und noch nicht besucht −∞, f (p), falls p terminal und bereits besucht Funten (p) = max{−F (p ), . . . , −F (p )} sonst oben 1 oben d falls p terminal und noch nicht besucht ∞, f (p), falls p terminal und bereits besucht Foben (p) = max{−F sonst unten (p1 ), . . . , −Funten (pd )} Die Schranken für Blattknoten ergeben sich zum genauen Wert, falls der Knoten bereits vom gegebenen Algorithmus besucht worden ist. Sonst ist −∞ untere und ∞ obere Schranke. Für nicht-terminale Knoten erhält man Schranken analog zur Spielbaumsuche aus Kapitel II rekursiv. Insbesondere gilt Funten (p) ≤ Foben (p) für alle Positionen p. Dies ist direkt per Induktion nach Baumhöhe einzusehen. Da der Wert des Wurzelknotens vom gegebenen Algorithmus genau berechnet wird, muss hierfür F (p) = Funten (p) = Foben (p) gelten. Sei F (p) ∈ / {−∞, ∞}. Es wird nun explizit eine Permutation des Baumes konstruiert, so dass das Alpha-Beta-Pruning genau die kritischen Positionen des Baumes und der andere gegebene Algorithmus mindestens die kritischen Positionen untersucht. Kritische Positionen werden erneut in die drei verschiedenen Typen aus Definition III.3.5 eingeteilt. Typ 1-Positionen: An Positionen p vom Typ 1 gilt Foben (p) = Funten (p) = F (p) 6= ±∞. Sie werden vom Alpha-Beta-Pruning durch den Aufruf value(p, −∞, ∞) ausgewertet. Falls p ein Blattknoten ist, muss er vom gegebenen Algorithmus untersucht werden, da Funten (p) 6= −∞ gilt. Falls p Kinderknoten hat, bezeichne pj einen Knoten mit Funten (p) = −Foben (pj ) und pk einen Knoten mit Foben (p) = −Funten (pk ). Unter Ausnutzung von III.3.1 erhält man Foben (pj ) = min{Foben (p1 ), . . . , Foben (pd )} bzw. Funten (pk ) = min{Funten (p1 ), . . . , Funten (pd )}. Damit gilt Funten (pk ) ≤ Funten (pj ) ≤ Foben (pj ) = −F (p) = Funten (pk ) III.3. ANALYSE DES BESTEN FALLS 30 und weiter Funten (pj ) = Funten (pk ). Also kann j = k angenommen werden. Durch Permutation der Kinderknoten von p wird pj nun an die erste Position gesetzt. Alle Folgepositionen von p sind kritisch. Genauer ist nach der Permutation p1 vom Typ 1 und wird durch value(p1 , −∞, ∞) ausgewertet. p2 , . . . , pd sind vom Typ 2 und werden durch den Aufruf value(pi , −∞, F (p1 )), i ∈ {2, . . . , d} untersucht. Da der erste Zug nach Konstruktion optimal ist, folgt F (p) = −F (p1 ). Somit gilt Foben (p) = Funten (p) = F (p) 6= ±∞ für alle Positionen vom Typ 1. Bei den Positionen p2 , . . . , pd gilt max{−Funten (p2 ), . . . , −Funten (pd )} ≤ −Funten (p1 ) ≤ −F (p1 ) und weiter Funten (pi ) ≥ F (p1 ) > −∞ für i ∈ {2, . . . , d}, da p1 bereits optimal ist. Typ 2-Positionen: An Positionen p vom Typ 2 gilt Funten (p) > −∞. Sie werden vom Alpha-Beta-Pruning durch den Aufruf value(p, −∞, β) ausgewertet, wobei −∞ < β ≤ Funten (p) gilt. Bei den Aufrufen von Typen 1 und 3 ist diese Form nachgewiesen. Falls p ein Blattknoten ist, muss er vom gegebenen Algorithmus untersucht werden, da Funten (p) 6= −∞ gilt. Falls p Kinderknoten hat, bezeichne pj denjenigen Knoten mit Funten (p) = −Foben (pj ). Durch Permutation der Kinderknoten von p wird pj nun an die erste Position gesetzt. Nach Permutation ist p1 kritisch, vom Typ 3 und wird durch value(p1 , −β, ∞) ausgewertet. Wegen Foben (p1 ) = −Funten (p) ≤ −β ist der Rückgabewert ≤ β. Somit werden die anderen Positionen p2 , . . . , pd vom Alpha-Beta-Pruning nicht untersucht. Im Übrigen sind sie nicht kritisch. Aus Funten (p) = −Foben (p1 ) folgt Foben (p1 ) < ∞. Für den zu p1 gehörigen Aufruf value(p1 , −β, ∞) gilt insbesondere ∞ > −β ≥ −Funten (p) = Foben (p1 ). Typ 3-Positionen: An Positionen p vom Typ 3 gilt Foben (p) < ∞. Sie werden vom AlphaBeta-Pruning durch den Aufruf value(p, α, ∞) ausgewertet, wobei Foben (p) ≤ α < ∞ gilt. Bei den Aufrufen von Typ 2 ist diese Form nachgewiesen. Falls p ein Blattknoten ist, muss er vom gegebenen Algorithmus untersucht werden, da Foben (p) 6= ∞ gilt. Falls p Kinderknoten hat, sind alle Knoten pi , i ∈ {1, . . . , d} kritisch und vom Typ 2. Sie werden durch den Aufruf value(pi , −∞, −α) ausgewertet. Permutation der Kinderknoten von p ist hier nicht nötig. III.4. ANALYSE DES SCHLECHTESTEN FALLS 31 Für diese Aufrufe gilt wegen Foben (p) = max{−Funten (p1 ), . . . , −F (pd )}: −∞ < −α ≤ −Foben (p) ≤ Funten (pi ) Bisher wurden die Fälle F (p) = ∞ und F (p) = −∞ ausgelassen. Hier führen analoge Argumente zum Ziel. Wenn F (p) = ∞ ist, behandelt man den Wurzelknoten als Knoten des Typs 2. Bei F (p) = −∞ hingegen ist eine Betrachtung erfolgreich, die den Wurzelknoten als Knoten des Typs 3 behandelt. Damit ist gezeigt, dass sämtliche Positionen, die vom Alpha-BetaPruning untersucht werden, auch vom anderen gegebenen Algorithmus untersucht werden müssen. Die Voraussetzung, dass der gegebene Algorithmus keine Kenntnis von eventuellen Abhängigkeiten zwischen den Blattknoten haben darf, führt zu einer schichtweisen Untersuchung des Spielbaums. Unter der konstruierten Permutation kann das AlphaBeta-Pruning also nicht schlechter sein als der gegebene Algorithmus. Bemerkung III.3.8. Eine Sortierung der Kinder eines Typ 3Knotens hat keinerlei Auswirkung auf die Laufzeit des Alpha-BetaPrunings. Dies wurde während des vorigen Beweises deutlich. III.4. Analyse des schlechtesten Falls Satz III.4.1. Gegeben sei ein beliebiger Spielbaum. Dann können die Werte der Blätter so gewählt werden, dass sämtliche Knoten im Baum vom Alpha-Beta-Pruning ausgewertet werden müssen. Die Werte können derart an den Blättern angeordnet werden, dass an jedem Knoten p beim Aufruf von value(p, α, β) die Beziehung α < −F (p1 ) < −F (p2 ) < · · · < −F (pd ) < β erfüllt ist. Damit ist zu jedem Zeitpunkt α < m < β und die Abbruchbedingung der while-Schleife in Algorithmus III.2.2 ist nie erfüllt. Es folgt die Behauptung. Von praktischer Bedeutung ist dieses Resultat jedoch kaum, da bei Verwendung des Alpha-Beta-Prunings eine Vorsortierung der Züge integriert werden sollte. Das grundlegende Ziel einer solchen Vorsortierung besteht darin, vielversprechende Züge relativ früh (durch das Alpha-Beta-Pruning) untersuchen zu lassen. Damit steigt die Chance auf Cutoffs. Der Abschnitt IV.1 befasst sich mit diesem Thema. Insbesondere wird dort beschrieben, wie man solche guten Züge im Voraus III.5. ALLGEMEINERE ERGEBNISSE 32 erkennen kann. III.5. Allgemeinere Ergebnisse Aussagekräftiger als der worst case ist ein anderer Ansatz. Es werden gleichmäßige Spielbäume mit Höhe h und Grad d betrachtet, deren Blättern zufällige Werte zugeordnet werden. Definition III.5.1. Ein Spielbaum heißt gleichmäßig (mit Höhe h und Grad d), falls die beiden folgenden Bedingungen erfüllt sind: • Ein Pfad von der Wurzel des Baums zu einem beliebigen Blatt hat Länge h. • Nicht-terminale Knoten haben genau d Kinder. Satz III.5.2. Gegeben sei ein gleichmäßiger Spielbaum mit Höhe h und Grad d. Den Blättern seien zufällige Werte zugeordnet. Dann ist die Anzahl der zu durchsuchenden Positionen proportional zu (rd )h wobei rd von d abhängig ist. Genauer ist rd proportional zu d . ln d Auf einen Beweis dieses Satzes wird verzichtet. Selbst eine Analyse des schwächeren Algorithmus III.1.2 würde bereits den Rahmen dieser Arbeit sprengen. In An Analysis of Alpha-Beta-Pruning“ ist ein ” entsprechender Beweis zu finden. (siehe [4], S. 311ff.) KAPITEL IV Spielabhängige und allgemeine Modifikationen des Alpha-Beta-Prunings Nachdem in den letzten Kapiteln die grundlegende Arbeitsweise des Alpha-Beta-Prunings deutlich geworden ist, werden hier Techniken vorgestellt, die mit dem Alpha-Beta-Pruning kombiniert werden können. Auf diese Weise lässt sich die Effizienz des Algorithmus weiter steigern. Denkbar sind beispielsweise spielabhängige Modifikationen des Algorithmus wie Eröffnungs- und Endspieldatenbanken oder HashFunktionen. Bei solchen Optimierungen wird im Folgenden auf Schach als wohl bekanntestes Zwei-Personen-Nullsummenspiel Bezug genommen. Ab Abschnitt IV.4 werden schließlich einige Modifikationen des Alpha-Beta-Prunings vorgestellt, die nicht von speziellen Spielregeln abhängen. IV.1. Vorsortierung der Züge Um möglichst viele Cutoffs im Spielbaum zu erreichen, ist es von Vorteil, gute Züge“ im Rahmen des Alpha-Beta-Algorithmus möglichst ” früh zu untersuchen. Ein guter Zug ist hier ein Zug, der zu einem möglichst guten Ergebnis führt, d.h. einem möglichst hohen Wert der Bewertungsfunktion. Dazu bietet es sich an, die Spielzüge zu sortieren, bevor sie durch das Alpha-Beta-Pruning analysiert werden. Eine Sortierung jedoch kann nur dann optimal durchgeführt werden, wenn das Spiel bekannt ist. Beim Schach beispielsweise gibt es hierzu verschiedene heuristische Kriterien: • Wer schlägt wen? Es wird überprüft, ob eine Figur eine andere schlägt. Falls ja, werden die Werte der beiden beteiligten Figuren verglichen und die verschiedenen Züge nach der Differenz sortiert. Üblich sind Werte wie 1000, 9, 5, 3, 3 und 1 für die Figuren König, Dame, Turm, Läufer, Springer und Bauer. Der Wert des Königs ist unverhältnismäßig hoch, weil sein Verlust nicht durch andere Figuren aufgewogen werden kann. • Liegt ein Schlagabtausch vor? Falls im letzten Zug eine eigene Figur geschlagen wurde, ist in vielen Fällen ein Zurückschlagen sinnvoll. 33 IV.2. HASH-FUNKTIONEN 34 • Wurde diese Stellung schon einmal untersucht? Wenn die Suchtiefe des Alpha-Beta-Prunings groß genug ist, wurden viele Züge, die in der momentanen Situation möglich sind, bereits (mit kleinerer Suchtiefe) bei der Untersuchung für den vorhergehenden Zug analysiert und bewertet. Diese Bewertung kann direkt zur Sortierung verwendet werden. • Killer-Heuristik: Man betrachtet ausschließlich die aktuelle Ebene des Baumes. Nun geht man davon aus, dass Züge, die in den bereits untersuchten Teilbäumen ein gutes Ergebnis geliefert haben, auch im aktuellen Teilbaum gut sein müssen. Dabei ist jedoch keineswegs klar, dass diese Züge überhaupt gültig sind. • Nullzug-Ansatz: Abgesehen vom Endspiel, für das es eine eigenständige Modifikation gibt (siehe Abschnitt IV.3), ist beim Schach das Zugrecht von Vorteil. Wenn man nun zwei Züge direkt hintereinander ausführt und den Zug des Gegners streicht, sollte sich eine signifikante Verbesserung der Situation ergeben. Falls dies nicht der Fall ist, war vermutlich schon der erste der beiden Züge schlecht und wird für die Vorsortierung nach hinten geschoben. Der Begriff heuristisch bedeutet, dass diese Ansätze intuitiv sinnvoll sind und sich in der Praxis als nützlich erwiesen haben. Ihre Effektivität ist jedoch nicht in jedem Spezialfall gesichert. IV.2. Hash-Funktionen Beim Schach können Stellungen auf verschiedene Weise entstehen. Sei beispielsweise die Stellung aus Abbildung IV.2.1 gegeben (mit Weiß am Zug). Diese Position kann aus 13 verschiedenen Zugfolgen entstehen, genauer aus 4 Vierzügern und 9 Sechszügern. Theoretisch sind auch Spielzüge mit 8 und mehr Zügen möglich, dies entspricht aber nicht einer realistischen Eröffnung. Um diese Stellung im Alpha-Beta-Pruning nicht 13-mal auswerten zu müssen, können für eine Stellung errechnete Ergebnisse gespeichert werden. Eine solche Datenbank benötigt allerdings viel Speicherplatz. Zudem muss eine wesentliche Bedingung erfüllt sein: Bewertungen müssen schnell abgerufen werden können. Wenn das Wiederfinden einer Position zu lange dauert, könnte man die Stellung letztlich schneller neu berechnen. Als Datenstruktur zur Realisierung bieten sich Hashtabellen an. Eine solche Tabelle besteht allgemein aus einem Index und den zugehörigen Daten. Sie kommen in IV.2. HASH-FUNKTIONEN 35 Abbildung IV.2.1. Beispielsituation vielen Bereichen der Informatik vor. In diesem speziellen Anwendungsbereich bestehen die gespeicherten Daten aus Positionsbewertungen. Der Index (d.h. die Position in der Tabelle), an dem diese Daten gespeichert werden, berechnet sich aus der aktuellen Spielstellung mittels einer Hash-Funktion. Definition IV.2.1. Eine Hashfunktion ist eine Funktion, die einer Eingabe beliebiger Länge eine Ausgabe kleinerer Länge zuordnet. Die Menge der möglichen Ausgabewerte ist ebenfalls kleiner als die Menge möglicher Eingabewerte. Diese Definition ist sehr weit gefasst. Idealerweise erfüllt eine Hashfunktion noch weitere Bedingungen, die für Hashtabellen wichtig sind: Bemerkung IV.2.2. • Die Ausgabe der Hashfunktion sollte eine ganze Zahl in einem vorgegebenen Intervall sein (beispielsweise zwischen 0 und 232 − 1). • Die Funktion ist weitgehend kollisionsfrei, d.h. zu zwei verschiedenen Spielpositionen generiert die Hashfunktion unterschiedliche Ausgaben. So wird sichergestellt, dass die Größe der Hashtabelle beschränkt ist. Außerdem werden zwei Stellungsbewertungen möglichst unter verschiedenen Indizes gespeichert. Es gibt etwa 1043 verschiedene Schachstellungen (siehe [3], S. 175) Wenn der Index nun die Größe 232 (≈ 4 · 109 ) hat, liegt es auf der Hand, dass es keine Funktion geben kann, die für jede Position einen eigenen Index generiert. Wenn zwei IV.2. HASH-FUNKTIONEN 36 Stellungen derselbe Index zugewiesen werden muss, wird dieser Fall Kollision genannt. Er ist zwar sehr selten, aber es gibt Lösungsstrategien: • Kollisionen werden ignoriert. Es wird ausgenutzt, dass Kollisionen sehr selten sind. Der dadurch entstehende Fehler wird vernachlässigt. • Statt nur die Bewertung in der Tabelle zu speichern, können zusätzlich die Daten der Position gespeichert werden. So kann überprüft werden, ob es sich um die richtige Position handelt. Zusätzlich ist es denkbar, bei Kollisionen am entsprechenden Index mehrere Positionen in einer Liste zu verwalten. Der Schnelligkeit wegen wird in der Praxis häufig die erste Variante gewählt. Um das Hashing möglichst effizient durchführen zu können, wird die Berechnung der Indizes iterativ organisiert, d.h. bei jedem Spielzug wird der Index der Folgeposition aus dem Index der Vorgängerposition berechnet. Genauer wird zu Beginn eines Spiels für jedes Paar (Figur, Feld) eine binäre Zufallszahl aus dem Zahlenraum von 0 bis 232 − 1 erzeugt. Um den Überblick in einem konkreten Beispiel zu wahren, beschränken sich diese Zufallszahlen im Folgenden auf 16 Stellen: Weißer König auf Feld a1: Weißer König auf Feld a2: Weißer König auf Feld a3: ... Weißer Turm auf Feld d5: ... Schwarzer König auf Feld c8: ... Schwarzer Bauer auf Feld h7: 0111010110101110 1110111000100101 0001001101110100 1100100101011011 0110110011110001 1000111110010101 Für die konkrete Berechnung eines Indexes bietet sich die bitweise Addition XOR an. XOR steht hierbei für eXclusive OR“ . Diese ” Verknüpfung zweier Wahrheitswerte ist genau dann wahr, wenn beide Wahrheitswerte unterschiedlich sind. Dies entspricht einer Addition ohne Übertrag. Der Operator XOR wird auf binäre Zahlen angewendet, indem er auf jede Stelle einzeln angewendet wird. Der Hash-Index von Beispielsituation IV.2.2 berechnet sich demnach zu: IV.2. HASH-FUNKTIONEN 37 Abbildung IV.2.2. Beispielsituation Weißer König auf Feld a2: Weißer Turm auf Feld d5: Schwarzer König auf Feld c8: 1110111000100101 1100100101011011 0110110011110001 Ausgabe der Hashfunktion: 0100101110001111 Wenn nun Weiß am Zug ist und mit dem König von a2 nach a3 zieht, ist die Berechnung des neuen Hashwertes denkbar einfach: Bisheriger Wert: Weißer König auf Feld a2: Weißer König auf Feld a3: 0100101110001111 1110111000100101 0001001101110100 Ausgabe der Hashfunktion: 1011011011011110 Dies ist so einfach möglich, da XOR(a, a) = 0 für a ∈ {0, 1} gilt. In diesem konkreten Beispiel bedeutet der iterative Ansatz keinen großen Vorteil gegenüber einer kompletten Neuberechnung. In der Anfangsphase des Spiels jedoch, wenn noch bis zu 32 Figuren auf dem Spielfeld sind, sieht das anders aus. IV.3. ENDSPIELDATENBANKEN 38 IV.3. Endspieldatenbanken Nun ist klar, wie man bereits berechnete Spielpositionen schnell wiederfinden kann. Da liegt die Idee nahe, beste Züge für bestimmte Stellungen bereits vor dem eigentlichen Durchlauf des Alpha-BetaPrunings zu berechnen und in einer Datenbank zu speichern. So könnte während des Alpha-Beta-Prunings einfach auf diese Datenbank zugegriffen und alle nötigen Berechnungen eingespart werden. Falls das Spiel Tic Tac Toe betrachtet würde, könnten für alle Positionen sämtliche Bewertungsdaten gespeichert werden. Die Anzahl möglicher Positionen beträgt 5478 (siehe [3], S. 170) und ist damit gering genug, um die Datenbank klein zu halten. Beim Schach sieht das anders aus. Hier können aus Platzmangel nur ausgewählte Positionen gespeichert werden, zum Beispiel Eröffnungen oder Endspiele. Eröffnungsbibliotheken sind erfolgsversprechend, da in der Anfangsphase die Zahl möglicher Züge noch überschaubar ist. Am Beginn dürfen schließlich nur Bauern und Springer ziehen. Um Eröffnungsbibliotheken zu erstellen, wird Expertenwissen benötigt. Ein Großmeister weiß intuitiv, welche Eröffnungen sinnvoll sind. Daher werden Datenbanken oft generiert, indem Meisterpartien analysiert werden. Mathematisch interessanter sind Endspieldatenbanken. Denn um sie zu erzeugen, ist man nicht auf Expertenwissen angewiesen. Da in Endspielen im Allgemeinen wenige Figuren auf dem Spielfeld sind, ist auch die Zahl möglicher Stellungen stark eingeschränkt. Trotz dieser Einschränkung ist mit einem enormen Platzaufwand zu rechnen. So können bislang mit einem Platzaufwand von 1,3 TByte lediglich alle Schachstellungen mit maximal 6 Figuren erfasst werden (siehe [3], Seite 171). Meist werden deshalb bestimmte Figurenkonstellationen vorgegeben (z.B. Weiß mit Turm und König gegen Schwarz mit König) und so sukzessiv eine umfangreiche Datenbank angelegt. Dies ist jedoch nicht zwingend erforderlich. Mit folgendem Algorithmus lässt sich eine solche Endspieldatenbank erstellen: Algorithmus IV.3.1. Gegeben sei eine Konstellation von Schachfiguren. Schritt 1: Erzeugen möglicher Stellungen (1) Erstelle einen Index für alle Positionen, die mit der gegebenen Figuren-Konstellation zulässig sind. Schließe dabei auch Positionen ein, die nur einen Teil der gegebenen Figuren benötigen. IV.3. ENDSPIELDATENBANKEN 39 Schritt 2: Gewinnstellungen für Weiß (1) Markiere alle Stellungen, bei denen Schwarz bereits matt gesetzt ist. Setze n = 1. (2) Suche alle Stellungen, bei denen Weiß am Zug ist und mindestens ein möglicher Zug zu einer Stellung führt, die in Teilschritt (1) markiert worden ist. Weiß kann hier in einem Zug matt setzen. Ergänze den Index an allen gefundenen Stellungen um den zugehörigen Zug und den Hinweis Gewinnstellung für ” Weiß“. (3) Markiere alle Stellungen, bei denen Schwarz am Zug ist und jeder mögliche Zug von Schwarz zu einer Gewinnstellung für Weiß führt. Schwarz kann hier eine Niederlage nicht verhindern. Gehe zu Schritt 3, sobald keine Stellungen mehr markiert wurden. (4) Erhöhe n um 1. Suche alle Stellungen, bei denen Weiß am Zug ist und mindestens ein möglicher Zug zu einer Stellung führt, die in Teilschritt (3) markiert worden ist. Weiß kann hier in n Zügen matt setzen. Ergänze den Index an allen gefundenen Stellungen um den zugehörigen Zug und den Hinweis Gewinnstellung für Weiß“. Gehe zurück zu Teilschritt (3). ” Schritt 3: Gewinnstellungen für Schwarz (1) Markiere alle Stellungen, bei denen Weiß bereits matt gesetzt ist. Setze n = 1. (2) Suche alle Stellungen, bei denen Schwarz am Zug ist und mindestens ein möglicher Zug zu einer Stellung führt, die in Teilschritt (1) markiert worden ist. Schwarz kann hier in einem Zug matt setzen. Ergänze den Index an allen gefundenen Stellungen um den zugehörigen Zug und den Hinweis Gewinn” stellung für Schwarz“. (3) Markiere alle Stellungen, bei denen Weiß am Zug ist und jeder mögliche Zug von Weiß zu einer Gewinnstellung für Schwarz führt. Weiß kann hier eine Niederlage nicht verhindern. Gehe zu Schritt 4, sobald keine Stellungen mehr markiert wurden. (4) Erhöhe n um 1. Suche alle Stellungen, bei denen Schwarz am Zug ist und mindestens ein möglicher Zug zu einer Stellung führt, die in Teilschritt (3) markiert worden ist. Schwarz kann hier in n Zügen matt setzen. Ergänze den Index an allen gefundenen Stellungen um den zugehörigen Zug und den Hinweis Gewinnstellung für Schwarz“. Gehe zurück zu Teilschritt (3). ” IV.3. ENDSPIELDATENBANKEN 40 Schritt 4: Remisstellungen (1) Stellungen, die weder einen Hinweis Gewinnstellung für Weiß“ ” noch Gewinnstellung für Schwarz“ haben, können als Remis” stellungen behandelt werden. Abbildung IV.3.1. Beispielsituation Bemerkung IV.3.2. • Die Zahl aller betrachteten Stellungen ist endlich. Daher kann - genug Speicher vorausgesetzt - auch ohne Probleme ein entsprechender Index erstellt werden. • Algorithmus IV.3.1 muss nach endlicher Zeit enden, da die Zahl der betrachteten Stellungen endlich ist. Insbesondere kann es nur endlich viele Gewinnstellungen für Weiß sowie Schwarz geben. • Ein Problem bei der Anwendung dieses Algorithmus stellt die 50-Züge-Regel des Schachs dar. Sie besagt, dass eine Partie als Remis zu werten ist, falls von beiden Spielern mindestens 50 Züge gemacht worden sind, ohne dass ein Stein geschlagen oder ein Bauer gezogen wurde. Dies kann zu einem Problem werden, wenn Endspieldatenbanken diese Regel nicht berücksichtigen. Beispielsweise ist es Weiß in der Situation aus Abbildung IV.3.1 erst im 243. (Doppel-)Zug möglich, einen schwarzen Springer zu schlagen (siehe [2], S. 189). IV.4. SUCHFENSTERTECHNIK 41 IV.4. Suchfenstertechnik Bislang wurde das Alpha-Beta-Pruning immer mit einem Suchfenster (−∞, ∞) gestartet. Dieses Intervall enthält den optimalen Wert auf jeden Fall. Man könnte das Alpha-Beta-Pruning allerdings auch direkt mit einem eingeschränkten Suchfenster (α, β) mit α, β ∈ Z starten und hoffen, dass der optimale Wert der Position darin liegt. So steigt zwar die Chance auf Cutoffs und damit die Chance auf eine Beschleunigung der Suche. Aber falls der Wert nicht in dem vorgegebenen Fenster liegt, muss der gesamte Baum mit anderem Suchfenster nochmals durchsucht werden. Man spricht hier von einer Wiederholungssuche. Bei einer solchen Wiederholungssuche können zwei Fälle eintreten. Hier sei p der Knoten, an dem die Suche gestartet wird. • Die erste Suche mit Intervall (α, β) liefert α. Dann ist der Minimax-Wert F (p) ≤ α und die Suche muss mit Fenster (−∞, α) wiederholt werden. • Die erste Suche mit Intervall (α, β) liefert β. Dann ist der Minimax-Wert F (p) ≥ β und die Suche muss mit Fenster (β, ∞) wiederholt werden. IV.4.1. F-Verbesserung. Wenn der Minimax-Wert außerhalb des Suchfensters liegt, wird eine Intervallgrenze vom Alpha-Beta-Pruning zurückgeliefert. Es wäre jedoch wünschenswert, eine bessere Abschätzung zu haben. Damit könnte das Suchfenster für eine Wiederholungssuche schon vor dem Start eingegrenzt werden. Glücklicherweise kann eine solche Änderung beim Alpha-Beta-Pruning auf einfache Art realisiert werden. Zunächst wird an das normale“ Alpha-Beta-Pruning erinnert: ” 1 int value(Position p , int alpha, int beta){ 2 if(endposition(p)) 3 return f(p); 4 int m = alpha; 5 berechneMöglicheZüge(p); 6 while(zügeÜbrig() && m<beta){ 7 Position q = führeNächstenZugAus(p); 8 m = max(m,-value(q,-beta,-m)); 9 } 10 return m; 11 } IV.4. SUCHFENSTERTECHNIK 42 Die Invarianzbedingung des Korrektheitsbeweises für das AlphaBeta-Pruning in Abschnitt III.2, m = max{α, −F (p1 ), . . . , −F (pi−1 )}, zeigt bereits einen Ansatz auf, wie eine optimierte Schranke ausgegeben werden kann. Ziel einer Änderung muss es sein, den Eingabeparameter α nicht in die Maximumsbildung einzubeziehen. Falls das α aus der Maximumsbildung in Programmzeile 8 ausgeschlossen wäre, würden nicht nur Werte innerhalb des Suchintervalls ausgegeben. Möglich wäre auch die Ausgabe eines korrekten Maximums, das außerhalb des Intervalls (α, β) liegt. Insbesondere darf ein Maximum dann kleiner als α sein. Wäre es größer als β, läge ein Cutoff vor und weitere Betrachtungen wären nicht notwendig. Um Werte möglich zu machen, die kleiner als α sind, muss die Initialisierung der Variable m in Zeile 4 zu int m = - INF; geändert werden. So ist α keine untere Schranke für den Ausgabewert mehr. Nun müssen allerdings mehr Knoten durchsucht werden als vor der Änderung, da in Zeile 8 für −m der Wert ∞ statt −α übergeben wird. Um dies zu korrigieren, wird auch dieser Übergabeparameter geändert: m = max(m,-value(q,-beta,-max(m,alpha))); Damit sind die übergebenen Werte vor und nach der Änderung gleich. Somit werden im Verlauf des Algorithmus auch genau dieselben Knoten besucht wie vorher. Der so entstandene Algorithmus heißt F-Verbesserung des AlphaBeta-Prunings. Das F“ steht hier für failsoft. ” Algorithmus IV.4.1. Die F-Verbesserung des Alpha-Beta-Prunings: 1 int value(Position p , int alpha, int beta){ 2 if(endposition(p)) 3 return f(p); 4 int m = - INF; 5 berechneMöglicheZüge(p); 6 while(zügeÜbrig() && m<beta){ 7 Position q = führeNächstenZugAus(p); 8 m = max(m,-value(q,-beta,-max(m,alpha))); 9 } 10 return m; 11 } Falls allerdings das Suchfenster zu Beginn geschickt gewählt wird, sind kaum Wiederholungssuchen nötig. Die F-Verbesserung hat so kaum IV.5. NULLFENSTER-SUCHVERFAHREN 43 Möglichkeiten, ihre Vorteile auszuspielen. In Abschnitt IV.5 bei den Nullfenster-Suchverfahren ändert sich dies. Das dort vorgestellte Verfahren basiert hauptsächlich auf Wiederholungssuchen. IV.4.2. L-Verbesserung. Beim Entwurf der Bewertungsfunktion wird oft darauf geachtet, dass sie nur ganzzahlige Werte annehmen kann. In diesem Fall sind auch alle berechneten Minimax-Werte ganze Zahlen. Falls zusätzlich nur der bestmögliche Zug gefunden werden soll und die exakte Berechnung des Minimax-Wertes vernachlässigt werden kann, gibt es eine weitere Verbesserung des Alpha-Beta-Prunings. Sie heißt L-Verbesserung (engl. last move improvement). Falls der Algorithmus an Knoten p aufgerufen wird und p genau d Kinder hat, werden nur die ersten d − 1 Kinder auf bekannte Weise vom Alpha-Beta-Pruning untersucht. Der dabei ermittelte vorläufige Optimalwert sei m. Die Untersuchung des letzten Unterbaums von p wird nun mit dem Intervall (m, m + 1) durchgeführt. Dieses Suchfenster kann keinen Minimax-Wert enthalten, da die Bewertungsfunktion nur ganze Zahlen liefert. Falls der Wert m zurückgegeben wird, ist der Wert des letzten Unterbaums (d.h. −F (pd )) nicht größer als m. Der bisherige optimale Zug bleibt weiterhin optimal. Falls allerdings m + 1 zurückgegeben wird, gilt −F (pd ) ≥ m + 1 > m und der letzte Zug ist besser als alle vorherigen (d.h. optimal). Diese Verbesserung braucht keinen zusätzlichen Speicherplatz oder zusätzliche Rechenzeit. Es wird lediglich ein Suchfenster verkleinert. Dadurch steigen natürlich die Chancen auf einen Cutoff, was der wesentliche Vorteil der L-Verbesserung ist. Hier wird diese Technik nur beim letzten Unterbaum angewendet. Im nächsten Abschnitt jedoch wird die Verwendung solcher minimalen Suchfenster weiter ausgeweitet. Es entsteht eine neue Klasse von Spielbaumsuchverfahren. IV.5. Nullfenster-Suchverfahren Bereits bei der L-Verbesserung der Suchfenstertechnik IV.4.2 kamen minimale Suchfenster der Form (m, m+1) (für m ∈ Z) vor. Solche Suchfenster, die keinen Minimax-Wert enthalten können, werden Nullfenster genannt. Bei Verwendung des Alpha-Beta-Prunings mit einem Nullfenster kann lediglich geprüft werden, ob der Minimax-Wert ≤ m oder ≥ m + 1 ist. Die genaue Größe des Minimax-Wertes kann nicht ermittelt werden. IV.5. NULLFENSTER-SUCHVERFAHREN 44 Aufrufe mit einem Nullfenster sind sehr effizient, da das Suchfenster sehr klein ist. Allgemein wird bei Nullfenster-Suchverfahren der Wert des linken Kinderknotens auf herkömmliche Weise bestimmt. Danach wird für alle weiteren Kinder lediglich geprüft, ob dieser Wert noch übertroffen werden kann. Falls nicht, ist man am Ziel. Falls er hingegen noch verbessert werden kann, muss eine Wiederholungssuche erfolgen, um den Wert des neuen“ optimalen Knotens zu bestimmen. ” Bei Kombination mit einer entsprechenden Vorsortierung der Züge, wie sie in Abschnitt IV.1 vorgestellt wurde, sind in der Praxis kaum Wiederholungssuchen notwendig. IV.5.1. Negascout-Verfahren. Der wohl bekannteste Vertreter der Nullfenster-Suchverfahren ist das Negascout-Verfahren. Abbildung IV.5.1. Ausgangssituation Sei im Baum die Ausgangssituation IV.5.1 gegeben. Die Basis für das Negascout-Verfahren IV.5.1 bildet das normale“ Alpha-Beta-Pru” ning III.2.2. Der Wert von p1 muss exakt berechnet werden. Hierfür erfolgt ein Aufruf mit normalem Suchfenster. Sobald ein Wert für p1 berechnet wurde, wird er mittels eines Nullfenster-Aufrufs mit den Werten von p2 , . . . , pd verglichen. Sobald ein Knoten pi gefunden wird, der einen besseren Wert als p1 hat, wird eine Wiederholungssuche mit einem normalen Suchfenster eingeleitet. Der so berechnete Wert kann dann wiederum mit den Knoten pi+1 , . . . , pd mittels Nullfenster verglichen werden. Zunächst wird der Algorithmus angegeben. Änderungen zum AlphaBeta-Pruning werden blau markiert. Danach folgen einige Erklärungen. IV.5. NULLFENSTER-SUCHVERFAHREN 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 45 Algorithmus IV.5.1. int value(Position p, int alpha, int beta){ if(endposition(p)) return f(p); int lo m = alpha; int hi m = beta; int j = 1; berechneMöglicheZüge(p); while(zügeÜbrig() && lo m<beta){ Position q = führeNächstenZugAus(p); int t = -value(q,-hi m,-lo m); if(t>lo m && t<beta && j>1) t = -value(q,-beta,-t); lo m = max(lo m,t); hi m = lo m + 1; j++; } return lo m; } Die Variable j dient ausschließlich dem Zählen des Knotens, welcher momentan untersucht wird (Anweisungen 6 und 15). Beim ersten Kind p1 ist schließlich ein anderes Vorgehen notwendig als bei p2 , . . . , pd . Die Variable lo m ist gleichwertig zur Variable m des Alpha-BetaPrunings. Sie wurde umbenannt, da die Einführung einer neuen Variable hi m notwendig ist. Diese beiden Variablen bilden zusammen das Suchfenster des Algorithmus. Dies dient dazu, durch eine einfache Zuweisung der Variable hi m ein Nullfenster zu erzeugen. Erinnerung: Beim Alpha-Beta-Pruning ist das Suchfenster (m, β), wobei m mit α initialisiert wird. Vor dem Betreten der while-Schleife ist der Algorithmus identisch zum Alpha-Beta-Pruning. Es kommen lediglich die beiden Initialisierungen in den Zeilen 5 und 6 hinzu. Auch die Abbruchbedingung für die while-Schleife ist gleich. Sie besagt weiterhin: Breche ab, falls alle Züge untersucht sind oder ein Cutoff vorliegt. Beim ersten Durchlauf der while-Schleife (j = 1) ist das Suchfenster (lo m, hi m) = (α, β) aktuell. Dies entspricht dem normalen Alpha-Beta-Pruning. Die Wiederholungssuche in Zeile 12 wird nicht ausgeführt, da die Bedingung j > 1 in Zeile 11 verletzt ist. Daher entsprechen die Zeilen 10 und 13 ebenfalls dem normalen Alpha-BetaPruning. IV.5. NULLFENSTER-SUCHVERFAHREN 46 Abschließend wird innerhalb der while-Schleife ein Nullfenster gewählt (Zeile 14) und der untersuchte Knoten aktualisiert (Zeile 15). Für das Nullfenster ist zu beachten, dass lo m den aktuellen MinimaxWert enthält. Daher bildet (lo m, lo m + 1) das zugehörige Nullfenster. Indem diese beiden Zuweisungen nach jedem Schleifendurchlauf aktualisiert werden, wird sichergestellt, dass sie stets die korrekten Werte enthalten. Ab dem zweiten Schleifendurchlauf kommen die Anweisungen 11 und 12 zur Geltung. Sie bilden zusammen mit Anweisung 10 das Herzstück des Algorithmus. Das Suchfenster (lo m, hi m) ist ein Nullfenster. Falls bei diesem Intervall lo m als Ergebnis zurückgeliefert wird, war der zugehörige Zug nicht besser als der bisherige Favorit. Dann kann die Wiederholungssuche in Zeile 12 übersprungen werden (Bedingung t>lo m in der if-Anweisung). Falls aber hi m zurückgeliefert wird, kann noch geprüft werden, ob ein Cutoff möglich ist. Das wäre bei β ≥ t der Fall. Somit muss der genaue Wert nur berechnet werden, wenn t < β gilt. Um den Negascout-Algorithmus zu optimieren, kann man nur bei den Wiederholungssuchen ansetzen. Nullfenster-Suchen sind bereits optimal (für einen Beweis siehe [5]). In den folgenden Abschnitten werden einige konkrete Verbesserungen vorgestellt. IV.5.2. F-Verbesserung. Bereits im Rahmen des Alpha-Beta-Prunings wurde eine F-Verbesserung vorgestellt. Sie entfaltet jedoch erst hier im Rahmen des Negascout-Verfahrens ihre volle Wirkung. Die grundlegende Idee der failsoftVerbesserung besteht darin, für den Minimax-Wert eine genauere Abschätzung als die jeweilige Intervallgrenze zu liefern. Insbesondere wird auch bei Nullfenstersuchen eine genaue Abschätzung zurückgegeben. Die ausführliche Herleitung der F-Verbesserung für das Alpha-BetaPruning aus Abschnitt IV.4.1 kann analog übernommen werden. Das Ziel ist auch hier, die Übergabeparameter zu erhalten und α aus der Maximumsbildung auszuschließen. IV.5. NULLFENSTER-SUCHVERFAHREN 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 47 Algorithmus IV.5.2. int value(Position p, int alpha, int beta){ if(endposition(p)) return f(p); int lo m = - INF; int hi m = beta; int j = 1; berechneMöglicheZüge(p); while(zügeÜbrig() && lo m<beta){ Position q = führeNächstenZugAus(p); int t = -value(q,-hi m,-max(lo m,alpha)); if(t>max(lo m,alpha) && t<beta && j>1) t = -value(q,-beta,-t); lo m = max(lo m,t); hi m = max(lo m,alpha) + 1; j++; } return lo m; } Änderungen im Vergleich zu Algorithmus IV.5.1 sind wiederum blau markiert. IV.5.3. L-Verbesserung. Während des Negascout-Algorithmus werden alle Knoten p2 , . . . , pd mit einem Nullfenster untersucht, insbesondere auch der Letzte. Daher ist die L-Verbesserung für das Alpha-Beta-Pruning aus Abschnitt IV.4.2 im Wesentlichen bereits in der Ursprungsversion integriert. Es muss nur zusätzlich sichergestellt werden, dass bei Untersuchung des Knotens pd auch dann keine Wiederholungssuche stattfindet, wenn er allen vorigen Knoten überlegen sein sollte. IV.5.4. TL-Verbesserung. Ein Spielbaum werde mit dem Negascout-Verfahren samt F-Verbesserung durchsucht und der Wert des Knotens p1 sei bereits bekannt. Nun beginnt der Algorithmus mit Nullfenster-Untersuchungen der Knoten p2 , . . . , pd . Sobald ein überlegener Knoten gefunden wird, liefert die FVerbesserung eine untere Schranke für den Wert des Knotens. Mit dieser Schranke kann man das Suchfenster aktualisieren und die Suche nach weiteren überlegenen Knoten mit einem Nullfenster fortsetzen, ohne eine direkte Wiederholungssuche zu starten. Wenn man Glück hat, gibt es keine weiteren Knoten, die als Favorit in Frage kommen. In diesem Fall muss man den Wert des überlegenen Knotens nicht genau ausrechnen, weil bereits feststeht, dass es der beste ist. Diese IV.5. NULLFENSTER-SUCHVERFAHREN 48 Verbesserung kann demnach als verfeinerte L-Verbesserung angesehen werden. Bei dieser Strategie gibt es allerdings einen Schwachpunkt. Wenn der Wert des ersten gefundenen überlegenen Knotens nicht direkt ausgerechnet wird, werden zwar die dazu nötigen Berechnungen gespart. Aber die folgenden Knoten müssen dann mit einem ungünstigeren Suchfenster untersucht werden, weil nur eine untere Schranke anstelle des exakten Wertes verwendet werden kann. Und sobald eine Wiederholungssuche unabwendbar ist, bedeutet dies einen großen Mehraufwand. Im Endeffekt ist es eine Spekulation, ob ein weiterer überlegener Knoten existiert. Es ist nicht a priori klar, ob sich diese Erweiterung des Negascout-Algorithmus positiv oder negativ auswirkt. Die einfache L-Verbesserung hingegen bedeutet nie einen Nachteil. Literaturverzeichnis [1] Alfred V. Aho, John E. Hopcroft, Jeffrey D. Ullman, Data structures and algorithms, Addison-Wesley Publishing Company, 1987. [2] Jörg Bewersdorff, Glück, Logik und Bluff, 4., durchgesehene und ergänzte Auflage, Friedr. Vieweg & Sohn Verlag, Wiesbaden, 2007. [3] Lars Bremer, Von MIPS zu Grips: Wie Computer den Menschen bei Denkspielen schlagen - oder nicht, c’t 18/2007 (2007), Heise Zeitschriften Verlag. [4] Donald E. Knuth and Ronald W. Moore, An Analysis of Alpha-Beta-Pruning, Artificial Intelligence 6 (1975), 293–326. [5] Alexander Reinefeld, Spielbaum-Suchverfahren, Informatik-Fachberichte 200 (1989), Springer-Verlag. [6] Wikipedia, Alpha-Beta-Pruning, http://de.wikipedia.org/wiki/Alpha-BetaPruning, abgerufen am 27.11.2008. [7] Wikipedia, Endspieldatenbank, http://de.wikipedia.org/wiki/Endspieldatenbank, abgerufen am 05.01.2009. 49 Eigenständigkeitserklärung Hiermit versichere ich, dass ich diese Masterarbeit selbständig verfasst und keine anderen als die angegebenen Quellen und Hilfsmittel verwendet sowie Zitate kenntlich gemacht habe. Bochum, 19. Februar 2009