Um Min / Max erfolgreich auf ein rundenbasiertes Strategiespiel anzuwenden, müssen Sie alle verfügbaren Schachtechniken korrekt anwenden ...
Bewertungsfunktion
Sogar Schach-Engines haben eine sehr schlechte Stärke, wenn Ihre Bewertungsfunktionen schlecht sind. Die einfachste Version einer Bewertungsfunktion ist: 1 = Spiel von Weiß gewonnen, -1 = Spiel von Schwarz gewonnen, 0 = alle anderen Fälle; Dies würde jedoch zu einer sehr schlechten Leistung führen. Das gleiche passiert mit deinem rundenbasierten Spiel! Wenn Sie wie im Schach Min / Max (mit Alpha / Beta-Bereinigung und so weiter) verwenden möchten, müssen Sie auch eine vernünftige Auswertungsfunktion implementieren! Andernfalls können Sie die Leistung dieser Algorithmen bei der Anwendung auf Ihr Strategiespiel nicht mit dem Fall vergleichen, in dem sie auf Schach angewendet werden.
Was Bewertungsfunktionen von Schachengines tun, ist die Bewertung von Dingen wie:
- Wie gut ist die Position eines Stücks auf dem Brett?
- Wie oft wird ein Stück angegriffen?
- Wie oft ist das Stück geschützt?
- Wie gut kann sich jedes Teil auf dem Brett frei "bewegen"? (oder: Wie viele Kacheln "kontrolliert" es?)
Diese Teile der Bewertungsfunktion müssen zuerst in Ihr Spiel "übersetzt" werden:
- Position des Stückes: Befindet es sich zB auf einem Hügel, der seinen Schießstand erweitert?
- Angegriffen: Wie viel ist jedes Stück in Gefahr? (zB Summe der Angriffswerte von Einheiten, die eine Spezialeinheit angreifen können, multipliziert mit einer Wahrscheinlichkeit, mit der sie angegriffen werden kann; Wahrscheinlichkeit steigt, wenn die Einheit bereits beschädigt ist; verringert sich, wenn sich viele andere Einheiten in Reichweite der angreifenden Einheit befinden)
- Eigener Angriff: Wie viele Einheiten können von dieser Einheit angegriffen werden?
- Schutz: Wie viel eigene Teile sind daneben (um zu helfen)? Möglicherweise greift eine Einheit Einheiten unter einer Mindestentfernung nicht an und es ist vorzuziehen, sie durch eine Einheit zu schützen, die die Möglichkeit hat, Einheiten in der Nähe anzugreifen.
- Mobilität: Wie mobil ist Ihr Gerät? (Kann es fliehen?)
Die verschiedenen Bewertungen müssen für alle Einheiten durch die Gewichtungsfunktion (factor_a * rating_a + factor_b * ranting_b + ...) summiert werden ...
In Strategiespielen müssen auch die verbleibenden Ressourcen (Gold, Holz, ...) berücksichtigt werden.
Wenn Ihre Bewertungsfunktion gut genug ist, müssen Sie in den meisten Fällen nicht wirklich "tief" in den Baum suchen. Sie müssen sich also wahrscheinlich nur die drei oder zehn vielversprechendsten Optionen genauer ansehen. Siehe nächstes Kapitel ...
Mögliche Bewegungen an jeder Position
Das Schwierigste an der Verwendung von Min / Max für Strategiespiele ist, dass Sie mehrere Einheiten in einer Runde befehlen können, während Sie im Schach nur eine Einheit befehlen dürfen (mit Ausnahme der Rochade, aber dies ist eine klar definierte Zugkombination). Dies führt zu 5 ^ N möglichen Zügen für N Einheiten für jede "Position" (Schachbegriff), wenn Sie sich nur für jede Einheit zwischen "Nach Norden, Süden, Westen, Osten ODER Stopp" entscheiden würden. Sie können dies lösen, indem Sie den komplexen Befehl in Befehle der unteren Ebene aufteilen: Wählen Sie z. B. die Aktion für Einheit A, gehen Sie in die Tiefe und entscheiden Sie sich für Einheit B .... entscheiden Sie sich für Einheit N ... und beenden Sie dann diesen Zug. Dies allein ändert jedoch nichts an der Komplexität! Sie müssen die Reihenfolge optimieren, in der Aktionen Einheiten zugewiesen werden (z. B. zuerst Einheit B, C, D und dann Einheit A). Sie können die Auswirkung der Entscheidung für jede Einheit während der letzten Berechnung aufzeichnen und dann nach Wichtigkeit sortieren. Auf diese Weise kann Alpha-Beta-Bereinigung verwendet werden, um jede schlechte Kombination sehr früh aus dem Suchbaum zu entfernen. Die höchste Priorität sollte in jeder Iteration immer "nichts mehr tun und deinen Zug beenden" (Null-Verschiebungsbeschnitt) sein. Auf diese Weise können Sie das Zuweisen der meisten Aufgaben zu den meisten Einheiten "überspringen" und sie einfach so weitermachen lassen, wie sie es zuvor getan haben. Auf diese Weise wird die Suche schnell vertieft, indem Sie sich nur die "kritischen" Einheiten ansehen (z. B. die Einheiten, die sich gerade wirklich im Kampf befinden). Stellen Sie sicher, dass Sie jede Einheit nur einmal kommandieren ... Sie können auch einen Zufallsbefehl verwenden, um sicherzustellen, dass die "wichtigen" Einheiten auch von Zeit zu Zeit einen Befehl erhalten. Insbesondere Einheiten, die einen Job beenden (z. B.
Iterative Vertiefung + Caching / Hash-Tabelle
Dann kann man "interaktiv vertiefen", um mehr und mehr in die Tiefe zu gehen, bis ein gewisses Zeitlimit erreicht ist. Sie werden also tiefer suchen, wenn es weniger Einheiten gibt, und Sie haben immer ein "Ergebnis", wenn Sie aufhören, nach einer besseren Lösung zu suchen. Für die iterative Vertiefung müsste eine Hash-Tabelle verwendet werden, um frühere Suchergebnisse zwischenzuspeichern. Dies ermöglicht auch die Wiederverwendung einiger Ergebnisse der Suche in den letzten Runden (der Zweig des Suchbaums, der die Befehle abdeckt, die tatsächlich in der letzten Runde ausgeführt wurden). Um dies zu implementieren, benötigen Sie eine sehr gute Hash-Funktion (siehe "zobrist key"), die iterativ aktualisiert werden kann. Das Aktualisieren des Hash-Schlüssels bedeutet, dass Sie nur den Hash-Schlüssel der alten "Position" nehmen und nur die Änderung der Position einleiten können (z. B. Nehmen Sie das Gerät an Position x ab und setzen Sie es an Position y). Auf diese Weise ist die Berechnung des Hash-Schlüssels schnell und Sie müssen nicht die gesamte Board-Situation verarbeiten, um ihn zu berechnen, nur um zu überprüfen, ob der Hash einen früheren Eintrag für diese Position enthält. In gewisser Weise müssen Sie sicherstellen, dass keine Hash-Kollisionen auftreten.
Nicht deterministisches Verhalten
Nicht deterministisches Verhalten ist ein Problem bei Min / Max-Suchen. Dies bedeutet, dass Sie nicht sicher sind, ob Sie ein angegriffenes Ziel treffen werden (z. B. beträgt die Wahrscheinlichkeit 10%). Dann kann man das eben nicht planen. In diesem Fall müssen Sie den Algorithmus ändern und eine "Wahrscheinlichkeits" -Ebene dazwischen setzen. Es ist ein bisschen wie "es sind die Wahrscheinlichkeiten". Jedes unabhängige Ergebnis muss separat betrachtet werden. Die Bewertung durch diese Tiefen- "Schicht" muss dann abgetastet werden (Monte Carlo Sampling) und das Ergebnis der eingehenden Bewertung muss mit der Wahrscheinlichkeit des Auftretens gewichtet werden. Unterschiedliche Ergebnisse der Wahrscheinlichkeitsschicht müssen als unterschiedliche gegnerische Bewegungen betrachtet werden (aber anstelle von min / max muss der "Durchschnitt" berechnet werden). Dies erhöht natürlich die Komplexität des Suchbaums.
Zusammenfassung
Wenn Sie all diese Techniken (die alle von aktuellen Schachengines verwendet werden) auf ein deterministisches Spiel anwenden, werden Sie mit Sicherheit auch für ein Spiel vernünftige Ergebnisse erzielen können. Für nicht deterministische Spiele wird dies wahrscheinlich komplizierter sein, aber ich halte es immer noch für handlich.
Eine gute Quelle zur Erklärung dieser Techniken (für Schach) ist http://chessprogramming.wikispaces.com/
Sie können sogar eine Art gerichtete Zufälligkeit in Min / Max-Suchen implementieren. Anstatt die besten Ergebnisse zuerst in jeder Iteration deterministisch zu untersuchen, können Sie diese einfach randomisieren und ihre Reihenfolge durch eine Wahrscheinlichkeitsverteilung bestimmen lassen, die auf den aktuellen Auswertungen basiert ...