Speichern die Schach-Engines alle zuvor analysierten Positionen zwischen den Zügen?


17

Ich fange an, mit Schachengines zu spielen. Ich stelle fest, dass die Bewegung der besten Schachengines mehrere Minuten dauern kann. Ich frage mich warum. Vor jeder Bewegung überprüft der Motor alle rechtlichen zukünftigen Bewegungen bis zu einer gewissen Tiefe. Diese Übung scheint sich dann jedoch für den nächsten Zug zu wiederholen. Ist dies nicht ineffizient, da der vorherige Zug bereits im untersuchten Zugbaum enthalten war? Oder habe ich falsch verstanden?

[Bearbeiten: Ich gehe davon aus, dass der Grund, warum Bewegungsanalysen nicht zwischengespeichert werden, auf einige Speicherbeschränkungen des Computers zurückzuführen ist, die den Neustart der Analyse beschleunigen.]

Antworten:


20

Das Programmieren von Schach-Engines ist ein sehr kompliziertes Gebiet, daher verweise ich Sie von vornherein auf das Schach-Programmier-Wiki , das viele großartige Informationen zu diesem Thema enthält.

Hintergrund

Schachberechnungen (und viele ähnliche Dinge) werden im Allgemeinen als "Spielbäume" oder " Entscheidungsbäume " modelliert und angesehen . Im Großen und Ganzen ist dieser Baum ein gerichteter Graph, bei dem sich ein Knoten oben befindet (die aktuelle Position), der zu einem Knoten für jede mögliche Bewegung führt, von denen jeder zu mehr Knoten für jede mögliche nächste Bewegung führt, und so weiter.

In ihrer einfachsten, gewalttätigen Form generieren Schach-Engines alle Positionen in diesem Baum bis zu einer gewissen Tiefengrenze ("Ply") und bewerten jede resultierende Position anhand komplexer Kriterien 1 . Dann spielt es den Zug, der zum besten Ergebnis zu führen scheint. Heutzutage wurden viele wirklich komplizierte Techniken entwickelt, um die Anzahl der Positionen zu begrenzen, die der Motor betrachten muss, aber ich werde diese zum Zweck dieser Antwort ignorieren, da sie das eigentliche Problem bei nicht ändern Hand.

Mathe-Tangens

Der Hauptgrund dafür, dass Motoren in der Regel ungefähr dieselbe Zeit benötigen, um jede Bewegung zu berücksichtigen, besteht darin, dass die Größe des Entscheidungsbaums mit der Tiefe exponentiell zunimmt ( k).

Betrachten Sie die Ausgangsposition. Die Spitze des Baums ( k=0) ist ein Knoten. Es gibt zwanzig mögliche erste Züge für Weiß, also gibt es zwanzig Knoten in der Tiefe k=1. Dann hat Schwarz auch zwanzig verfügbare Züge für jede der Optionen von Weiß: also k=2gibt es 20 * 20 = 400mögliche Positionen! Und es wird nur schlimmer, wenn die Spieler ihre Figuren entwickeln!

Nehmen wir zum Beispiel an, dass für jeden Spieler zu einem bestimmten Zeitpunkt immer zwanzig Züge möglich sind 2 . Sie weisen den Computer an, für jeden Spieler fünf Züge vorauszusehen (zehn Lagen). Schauen wir uns die Größe des Brute-Force-Baums auf jeder Ebene an. Zum Spaß schauen wir uns auch die Gesamtzahl der Positionen im Baum an (von oben bis zum angegebenen Level).

Ply |    Positions   |  Total Tree Size
----------------------------------------
 0  | 1              | 1
 1  | 20             | 21
 2  | 400            | 421
 3  | 8000           | 8421
 4  | 160000         | 168421
 5  | 3200000        | 3368421
 6  | 64000000       | 67368421
 7  | 1280000000     | 1347368421
 8  | 25600000000    | 26947368421
 9  | 512000000000   | 538947368421
10  | 10240000000000 | 10778947368421

Das Ergebnis, dass jede Ebene exponentiell größer ist als die vorherige Ebene, ist, dass die Größe des gesamten Baums von der untersten Ebene dominiert wird . Betrachten Sie das obige Beispiel: Allein die letzte Ebene enthält zehn Billionen Knoten. Der gesamte Rest des Baumes enthält nur fünfhundert Milliarden. Die zehnte Lage enthält ungefähr 95% der Knoten im gesamten Baum (dies gilt tatsächlich für jede Ebene). In der Praxis bedeutet dies, dass die gesamte Suchzeit für die Auswertung des "letzten" Schrittes aufgewendet wird.

Antworten

Wie hängt das mit Ihrer Frage zusammen? Nehmen wir an, der Computer ist wie oben auf zehn Lagen eingestellt und "merkt" sich weiterhin die Ergebnisse seiner Auswertungen. Es berechnet einen Zug, spielt ihn ab und dann machst du einen Zug. Jetzt wurden zwei Züge ausgeführt, sodass alle Positionen aus dem Speicher gelöscht werden, die sich auf die nicht erfolgten Züge beziehen. Es verbleibt ein Baum, der die verbleibenden acht Züge herunterfährt, die er bereits berechnet hat: 26.947.368.421 Positionen!

Gut! Wir müssen also nur die letzten zwei Lagen berechnen! Nach unserer Schätzung von 20 Zügen pro Tiefe müssen wir hier immer noch mehr als zehn Billionen Züge berechnen. Die Positionen, die wir bereits berechnet haben, machen nur 2,5% der Möglichkeiten aus! Selbst wenn wir also die Ergebnisse des letzten Zuges zwischenspeichern, können wir nur auf eine Geschwindigkeitssteigerung von 2,5% hoffen! Im Grunde ist dies der Grund, warum Sie, selbst wenn Ihr Programm frühere Ergebnisse zwischenspeichert, normalerweise keine signifikante Beschleunigung zwischen den Zügen feststellen (mit Ausnahme von Fällen, in denen der Computer einen erzwungenen Partner oder etwas anderes findet!).


Vereinfachung Haftungsausschluss

Es gibt eine Menge von Komplexität in dieser Frage beteiligt, weshalb ich in der Programmierung Wiki an der Spitze verbunden und versuchte , nur die Antwort in breiten mathematischen Begriffen zu erklären. In Wirklichkeit Programme tun im Allgemeinen Cache Teile des Baumes von Bewegung zu bewegen, und es gibt andere Gründe , warum das allein nicht ausreichend ist - einige einfache Gründe (zB eine bestimmte Linie zu acht Züge gut heraus aussehen könnte, aber Enden mit einem Rück -Rangkamerad im neunten Zug!) und viele hochkomplizierte (im Allgemeinen im Zusammenhang mit verschiedenen cleveren Schnittmethoden). Der Computer muss also weiter nach vorne schauen, um zu vermeiden, dass aufgrund der Grenztiefe des vorherigen Zugs falsche Annahmen getroffen werden.


1 Ich werde hier nicht auf heuristische Funktionen eingehen, da dies ein unglaublich komplexer Bereich ist, aber es gibt auch hier häufig einige Vorteile, die durch Positions-Caching-Schemata erzielt werden können.

2 Ein durchschnittlicher Verzweigungsfaktor von 20 ist wahrscheinlich viel zu niedrig .


Sehr interessant, dies erklärt, warum mein RAM fast zusammenbricht, wenn ich tief mit meinem Motor analysiert habe (ein Rätsel, das mich seit einiger Zeit verblüfft hat).
Pablo S. Ocal

Danken! Sehr interessant. Ich fand die Wiki-Diskussion über die Schachengine faszinierend.
Dom

3

Eine typische Schachengine speichert einige der Positionen und ihre Alpha-Beta-Ergebnisse in einer Transpositionstabelle , die bei nachfolgenden Suchvorgängen abgerufen werden kann. Diese Tabelle wird nicht direkt zur Auswahl des nächsten Zugs herangezogen, aber die Suche nach diesem Zug wird auf zwei Arten effizienter.

  1. Eine Position wird wahrscheinlich mehrmals in einem Suchbaum angetroffen, indem eine Sequenz von Zügen transponiert oder permutiert wird. Da die Tabelle konsultiert werden kann, muss eine solche Position möglicherweise nur einige Male (für unterschiedliche feste Suchtiefen) ausgewertet werden, anstatt Dutzende Male, wenn die Position besucht und erneut besucht wird.

  2. Eine Standardtechnik für Alpha-Beta-Suchen besteht in der Verwendung einer iterativen Vertiefung , bei der der Baum wiederholt mit einer größeren Suchtiefe durchsucht wird, bis die Endtiefe erreicht ist. Die in früheren Iterationen berechneten Bewertungsergebnisse werden verwendet, um die in späteren Iterationen gesuchten Züge anzuordnen. Es ist bekannt, dass Alpha-Beta eine bessere Leistung erbringt (dh mehr vom Suchbaum entfernt), wenn gute Züge vor schlechten Zügen gesucht werden.


3

Beispiel für den Speicher des Motors:

Erwägen Sie Positionen, bei denen tiefgreifende theoretische Neuerungen entdeckt werden, insbesondere das Spiel Caruana gegen Topalov, das dieses Jahr gespielt wurde. Wenn Sie den Motor die Position nach dem 12. Zug mehr oder weniger kurz analysieren lassen (z. B. 10-15 Minuten), überprüfen Sie möglicherweise die vorgeschlagenen Züge und stellen fest, dass TN ( 13.Re2!) nicht unter ihnen erscheint. Führen Sie den Zug selbst ein, machen Sie einen Schritt zurück und lassen Sie den Motor mehr oder weniger zur gleichen Zeit dieselbe Position erneut analysieren. Überraschenderweise betrachtet der Motor die TN nach einiger Überlegung als eine der besten Bewegungen und genehmigt sie.

BEARBEITEN: Die ursprüngliche Antwort (siehe unten) ist falsch, liefert jedoch ein nützliches Beispiel für den Speicher des Motors, der oben zitiert wurde.

Soweit ich weiß, beginnen sie die Baumsuche nicht bei jeder Bewegung von vorne.

Sie müssen jedoch eine Funktion haben, die die Werte für jede Bewegung aktualisiert, und diese Funktion verfügt mit Sicherheit über ein gewisses Kurzzeitgedächtnis. Einige Beispiele sind Positionen, in denen tiefgreifende theoretische Neuerungen entdeckt werden, insbesondere das Spiel Caruana gegen Topalov, das dieses Jahr gespielt wurde. Wenn Sie den Motor die Position nach dem 12. Zug mehr oder weniger kurz analysieren lassen (z. B. 10-15 Minuten), überprüfen Sie möglicherweise die vorgeschlagenen Züge und stellen fest, dass TN ( 13.Re2!) nicht unter ihnen erscheint. Führen Sie den Zug selbst ein, machen Sie einen Schritt zurück und lassen Sie den Motor mehr oder weniger zur gleichen Zeit dieselbe Position erneut analysieren. Überraschenderweise betrachtet der Motor die TN nach einiger Überlegung als eine der besten Bewegungen und genehmigt sie.

Ich bin kein Experte für Schachsoftware, aber das passiert. Dies kann zumindest teilweise erklärt werden, wenn (wie gesagt) die Funktion, die die Bewegungen für die Position auswertet, etwas Gedächtnis hat.


2
Nein. Motoren starten die Baumsuche nicht von vorne. Siehe meine Antwort.
SmallChess

Entschuldigung, aber ich finde Ihre Antwort ist ein bisschen irreführend
BlueTrin

1
Ich habe versucht, es klarer zu machen. Wie gesagt, die Antwort ist falsch, das Beispiel ist jedoch gültig und es ist eine nette Sache zu überprüfen (für uns Romantiker gibt es einige Hoffnungen, dass trotz der Tatsache, dass Computer viel stärker als Menschen sind, Intuition, Erfahrung und harte Arbeit ihre Fähigkeiten "übertreffen" können Original).
Pablo S. Ocal

@pablo, dein Beispiel ist in Ordnung. Es ist Speicherplatz vorhanden, da die Suchmaschine beim ersten Ausführen der Suche die Positionsbewertungen in einer Tabelle speichert. Wenn Sie dieselbe Position erneut suchen, kann die Suchmaschine viel schneller suchen. Daher erhalten Sie ein anderes Ergebnis.
SmallChess

Die letzte Änderung war für @BlueTrin, der es für irreführend hielt.
Pablo S. Ocal

2

Henry Keiter gab Ihnen bereits eine allgemeine Antwort, ich gebe Ihnen eine technischere Antwort. Es dreht sich alles um Transpositionstabelle, Suchtiefe und Cutoff. Die Diskussion hier ist VIEL technischer als andere Antworten, aber es wird für jeden von Vorteil sein, der Schachprogrammierung lernen möchte.

Es ist ein weit verbreitetes Missverständnis, dass, wenn eine Position zuvor bewertet wurde, die Bewertungspunktzahl wiederverwendet werden könnte, solange genügend Speicher vorhanden ist, um die Züge zu speichern. Schachprogrammierung ist komplizierter. Selbst bei unendlichem Speicher müssten Sie die Positionen noch einmal suchen. Für jeden Zug wird eine Bewertungspunktzahl mit der Tiefe und der Schranke angehängt. Wenn die Engine beispielsweise eine Bewegung durch Fail-High speichert, hat der Tabelleneintrag eine Untergrenze. Das bedeutet, wenn Sie nach einer Position suchen, müssen Sie immer noch die Grenzen überprüfen, ob Sie die vorherige Bewertungspunktzahl verwenden können.

Ansonsten ist jeder Bewertung eine Tiefe zugeordnet. Wenn Sie in einem sich iterativ vertiefenden Framework die Tiefe für jede Iteration erhöhen, müssen Sie immer noch die Positionen suchen, die Sie bereits in der vorherigen Iteration gesucht haben.

Die kurze Antwort auf Ihre Frage lautet, dass eine Engine zwar alle zuvor analysierten Positionen speichert (solange genügend Speicher vorhanden ist), die gespeicherten Ergebnisse jedoch nicht so einfach wiederverwendet werden können, wie Sie vielleicht gedacht haben . In einer Eröffnungsphase, in der es weniger Wiederholungen gibt, sind diese gespeicherten Ergebnisse für die Reihenfolge der Züge und ein Dutzend Heuristiken zur Reduzierung der Züge am nützlichsten. Zum Beispiel würde man annehmen, dass der beste Zug aus der letzten Tiefe der beste Zug in der aktuellen Tiefe ist, also würden wir die Zuglisten sortieren und den besten Zug vor allen anderen Zügen suchen. Hoffentlich bekommen wir einen frühen Ausfall.

Wir haben keinen unendlichen Speicher zum Speichern der Positionen. Wir müssten einen Hashing-Algorithmus definieren. Zobrist-Hashing-Algorithmus gibt uns eine Pseudozufallsverteilung, aber früher oder später müssten wir noch einige vorhandene Einträge ersetzen.


0

Jeder Motor hat ein eigenes Zeitmanagementschema. Bei einigen Engines und GUIs können Sie das Tempo festlegen, mit dem die Engine abgespielt wird. Engines berechnen / bewerten / minimieren immer so viel, wie sie aufgrund der Einschränkungen, die durch die Zeitverwaltungs-Subroutinen oder die Benutzereinstellungen auferlegt werden, können. Wenn eine Engine lange nachdenkt, liegt dies wahrscheinlich daran, dass die Zeitsteuerung für das Spiel langsam ist oder der Benutzer sie so eingestellt hat, dass sie langsam spielt.

Vom Motor berechnete Positionen und Auswertungen werden in einer Hash-Tabelle gespeichert. Der Benutzer kann die Größe des verfügbaren Hash in den Einstellungen der meisten UCI-Engines festlegen. Die Engine selbst verwendet eine bestimmte Menge an RAM. Wenn Sie die Größe Ihrer Hash-Tabelle zu hoch einstellen, beginnt der Computer mit dem Speichern von Hash auf Ihrer Festplatte in Form von virtuellem RAM. Auf den Festplattenspeicher wird langsamer zugegriffen als auf den RAM-Speicher. In der Regel können Sie das Abwälzen der Festplatte hören. Viele Benutzer legen die Größe der Hash-Tabelle so fest, dass sie in den verfügbaren Arbeitsspeicher passt.

Ein großer Teil einer Hash-Tabelle wird unbrauchbar, nachdem die Engine und ihr Gegner ihre Bewegungen ausgeführt haben, da die anderen betrachteten Positionen nicht mehr relevant sind. Die Engine wird die in Hash gespeicherten Bewertungen wiederverwenden. Einige Bewertungen erweisen sich jedoch aufgrund von Horizonteffekten als falsch, sobald die Engine in derselben Zeile tiefer geht. Daher muss sie ihre Kandidatenbewegungen häufig neu anordnen.

Da die Menge an Hash endlich ist, muss eine Engine auch entscheiden, welche Informationen aus ihrem Hash gelöscht werden sollen, wenn sie neue Informationen hinzufügt. Die Engine weiß nicht im Voraus, welche Züge gespielt werden, und löscht möglicherweise versehentlich Informationen, die beim Hinzufügen neuer Daten hilfreich gewesen wären.

Motoren im Allgemeinen prüfen nicht alle rechtlichen Schritte bis zu einer bestimmten Tiefe. Sie eliminieren bestimmte Zweige des Baumes aus der Betrachtung, die auf dem Beschneiden in Vorwärts- und Rückwärtsrichtung basieren. Wenn für eine Blattknotenposition noch Erfassungen oder Überprüfungen erforderlich sind, fährt der Motor in dieser Zeile weiter, bis er eine ruhige Position erreicht. Der tatsächliche Baum ist wahrscheinlich an einigen Stellen ziemlich tief, während andere Linien nach einer kleinen Anzahl von Zügen möglicherweise abgeschnitten wurden.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.