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=2
gibt es 20 * 20 = 400
mö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 .