Minimax für Bomberman

11

Ich entwickle einen Klon des Bomberman-Spiels und experimentiere mit verschiedenen Arten von KI. Zuerst habe ich mit A * den Zustandsraum durchsucht und jetzt möchte ich einen anderen Ansatz mit dem Minimax-Algorithmus ausprobieren. Mein Problem ist, dass jeder Minimax-Artikel, den ich gefunden habe, Spieler abwechselt. Aber in Bomberman macht jeder Spieler gleichzeitig eine Aktion. Ich denke, ich könnte alle möglichen Zustände für einen Spiel-Tick generieren, aber mit vier Spielern und 5 Grundaktionen (4 Züge und Bombenplatz) gibt es 5 ^ 4 Zustände auf der ersten Ebene des Spielbaums. Dieser Wert steigt mit jedem nächsten Level exponentiell an. Vermisse ich etwas Gibt es Möglichkeiten, es zu implementieren, oder sollte ich einen völlig anderen Algorithmus verwenden? Vielen Dank für alle Vorschläge

Billda
quelle
1
Während dies ein wenig vom Thema abweicht, ist eine Sache, die ich gerne mit KI mache, die Verwendung von Zielen oder Persönlichkeiten für die KI. Es können Dinge wie Horten von Power-Ups, Nicht-Aggressives, Rache, Eile usw. sein. Mit solchen Zielen können Sie grob sagen, in welche Richtung Sie sich bewegen sollten, und eine Bombe nur dann abwerfen, wenn sie Ihren Fortschritt zum Ziel voranbringt (wenn Es ist ziemlich nah an einem Spieler, den Sie jagen, oder einem Block, den Sie zerstören möchten.
Benjamin Danger Johnson
2
Ja, Sie vermissen ein paar Dinge, aber Sie werden mir nicht dafür danken, dass ich sie darauf hingewiesen habe, weil sie es noch schlimmer machen. Es gibt keine 5 grundlegenden Aktionen. Einige Felder haben 5 "Züge" (4 Richtungen und bleiben still); andere haben 3 (weil sie in zwei Richtungen blockiert sind); Im Durchschnitt ist es 4. Aber Sie können eine Bombe während des Laufens abwerfen , sodass der Verzweigungsfaktor im Durchschnitt 8 beträgt. Und jemand mit einem Hochgeschwindigkeits-Powerup kann in mehr Züge passen und seinen Verzweigungsfaktor effektiv erhöhen.
Peter Taylor
Ich habe Ihnen die Antwort in Ihrer Frage mithilfe der Monte-Carlo-Baumsuche gegeben.
SDwarfs
Minimax ist in einer Situation mit so vielen Auswahlmöglichkeiten wie Bomberman einfach nicht nützlich. Sie erschöpfen Ihre Suchfähigkeit, bevor Sie weit genug gehen, um festzustellen, ob eine Bewegung sinnvoll ist oder nicht.
Loren Pechtel

Antworten:

8

Echtzeit-Strategiespiele wie Bomber Man haben es schwer mit KI. Sie möchten, dass es intelligent ist, aber gleichzeitig kann es nicht perfekt sein.

Wenn die KI perfekt ist, werden Ihre Spieler frustriert sein. Entweder weil sie immer verlieren oder Sie erhalten 0,3 Bilder pro Sekunde.

Wenn es nicht intelligent genug ist, werden sich Ihre Spieler langweilen.

Meine Empfehlung ist, zwei KI-Funktionen zu haben, eine, die bestimmt, wohin die KI geht, die andere, die bestimmt, wann eine Bombe am besten abgeworfen werden kann. Mithilfe von Bewegungsvorhersagen können Sie feststellen, ob sich ein Feind auf einen Punkt zubewegt, der gefährlich ist, wenn eine Bombe am aktuellen Standort abgeworfen wird.

Je nach Schwierigkeitsgrad können Sie diese Funktionen ändern, um den Schwierigkeitsgrad zu verbessern oder zu verringern.

UnderscoreZero
quelle
2
Zeit, Frust und Langeweile sind kein Problem. Ich schreibe eine Bachelorarbeit über verschiedene KI-Ansätze in Bomberman und vergleiche sie. Also, wenn es perfekt ist, ist es besser. Ich bin gerade mit diesem Minimax
festgefahren
1
Das Problem, auf das Sie im Minimax-Algorithmus stoßen werden, ist die Verarbeitungszeit. Sie müssen alle feindlichen Aktionen im Auge behalten und ihren Spielstil und Ihren Gegenspielstil bestimmen. Es scheint, als ob Sie sich dessen bereits bewusst sind, aber dies kann für ein Echtzeitspiel eine ziemlich entmutigende Aufgabe sein, ohne das Spiel zu verlangsamen. Anstatt einen Spielbaum zu erstellen, müssen Sie Ihre Aktionen in Echtzeit bestimmen und möglicherweise einen Algorithmus für maschinelles Lernen erstellen, der umso besser wird, je mehr er spielt.
UnderscoreZero
4

Wie Sie bemerkt haben, ist Bomberman viel zu komplex, um als rundenbasiertes Spiel simuliert zu werden. Eine mögliche eigene Entscheidung und jede mögliche Entscheidung eines anderen Spielers zu extrapolieren, funktioniert einfach nicht.

Stattdessen sollten Sie eher einen strategischeren Ansatz wählen.

Sie sollten sich fragen: Wie trifft ein menschlicher Spieler Entscheidungen, während er Bomberman spielt? Normalerweise sollte ein Spieler vier grundlegende Prioritäten befolgen:

  1. Explosionsbereiche von Bomben vermeiden
  2. Platziere Bomben, damit andere ihren Explosionsgebieten nicht ausweichen können
  3. sammle Powerups
  4. Platziere Bomben, um Steine ​​in die Luft zu jagen

Die erste Priorität kann durch die Erstellung einer "Gefahrenkarte" erfüllt werden. Wenn eine Bombe platziert wird, sollten alle von ihr bedeckten Kacheln als "gefährlich" gekennzeichnet werden. Je früher die Bombe explodiert (Kettenreaktionen im Auge behalten!), Je höher die Gefahr. Immer wenn die KI bemerkt, dass sie sich auf einem Feld mit hoher Gefahr befindet, sollte sie sich entfernen. Wenn ein Pfad (aus welchem ​​Grund auch immer) gezeichnet wird, sollten Felder mit einer hohen Gefährdungsstufe vermieden werden (kann implementiert werden, indem ihnen künstlich höhere Pfadkosten hinzugefügt werden).

Die Berechnung der Gefahrenkarte kann weiter verbessert werden, um die KI vor dummen Entscheidungen zu schützen (z. B. beim Betreten von Bereichen, denen man nur schwer entkommen kann, wenn sich ein anderer Spieler in der Nähe befindet).

Dies sollte bereits eine vernünftige defensive KI schaffen. Was ist also mit Beleidigung?

Wenn die KI erkennt, dass sie momentan einigermaßen sicher ist, sollte sie offensive Manöver planen: Sie sollte überlegen, wie sie die Gefahrenkarte um die anderen Spieler erhöhen kann, indem sie selbst Bomben platziert. Bei der Auswahl eines Ortes, an dem eine Bombe gepflanzt werden soll, sollten nahe gelegene Orte bevorzugt werden, damit sie sich nicht so weit bewegen müssen. Bombenstandorte sollten ebenfalls ignoriert werden, wenn die resultierende Gefahrenkarte keinen angemessenen Fluchtweg zulässt.

Philipp
quelle
Meine begrenzte Erfahrung mit dem Spielen ist, dass man normalerweise mehrere Bomben platzieren muss, um einen kompetenten Gegner zu töten - eine Strategie muss dies berücksichtigen. Ich habe mit ungefähr Ihrer Strategie gegen AIs gespielt. Sie sind ziemlich ineffektiv, wenn es darum geht, Sie zu töten, es sei denn, Sie können in die Enge getrieben werden.
Loren Pechtel
4

Ich denke, ich könnte alle möglichen Zustände für einen Spieltick generieren, aber mit vier Spielern und 5 Grundaktionen (4 Züge und Bombenplatz) ergibt sich 5 ^ 4 Zustände auf der ersten Ebene des Spielbaums.

Richtig! Sie müssen alle 5 ^ 4 (oder sogar 6 ^ 4, da Sie in 4 Richtungen gehen, anhalten und "eine Bombe legen"?) Aktionen für jeden Spieltick suchen. ABER wenn sich ein Spieler bereits für einen Zug entschieden hat, dauert es einige Zeit, bis der Zug ausgeführt wird (z. B. 10 Spiel-Ticks). Während dieser Zeit verringert sich die Anzahl der Möglichkeiten.

Dieser Wert steigt mit jedem nächsten Level exponentiell an. Vermisse ich etwas Gibt es Möglichkeiten, es zu implementieren, oder sollte ich einen völlig anderen Algorithmus verwenden?

Sie können eine Hash-Tabelle verwenden, um denselben Spielstatus "Teilbaum" nur einmal zu berechnen. Stellen Sie sich vor, Spieler A geht auf und ab, während alle anderen Spieler "warten", landen Sie im selben Spielzustand. Es ist das gleiche wie für "links-rechts" oder "rechts-links". Auch das Verschieben von "nach oben, dann nach links" und "nach links, dann nach oben" führt zum gleichen Zustand. Mit einer Hash-Tabelle können Sie die berechnete Punktzahl für einen bereits ausgewerteten Spielstatus "wiederverwenden". Dies reduziert die Wachstumsgeschwindigkeit erheblich. Mathematisch reduziert es die Basis Ihrer exponentiellen Wachstumsfunktion. Um eine Vorstellung davon zu bekommen, um wie viel sich die Komplexität verringert, betrachten wir die Bewegungen, die nur für einen Spieler möglich sind, im Vergleich zu erreichbaren Positionen auf der Karte (= verschiedene Spielzustände), wenn sich der Spieler nur nach oben / unten / links / rechts / anhalten bewegt .

Tiefe 1: 5 Züge, 5 verschiedene Zustände, 5 zusätzliche Zustände für diese Rekursion

Tiefe 2: 25 Züge, 13 verschiedene Zustände, 8 zusätzliche Zustände für diese Rekursion

Tiefe 3: 6125 Züge, 25 verschiedene Zustände, 12 zusätzliche Zustände für diese Rekursion

Um dies zu visualisieren, antworten Sie sich selbst: Welche Felder auf der Karte können mit einem Zug, zwei Zügen, drei Zügen erreicht werden. Die Antwort lautet: Alle Felder mit einem maximalen Abstand = 1, 2 oder 3 von der Startposition.

Wenn Sie eine HashTable verwenden, müssen Sie jeden erreichbaren Spielstatus (in unserem Beispiel 25 in Tiefe 3) nur einmal auswerten. Ohne eine HashTable müssen Sie sie mehrmals auswerten, was 6125 Auswertungen anstelle von 25 in Tiefenstufe 3 bedeuten würde. Das Beste: Sobald Sie einen HashTable-Eintrag berechnet haben, können Sie ihn in späteren Zeitschritten wiederverwenden ...

Sie können auch inkrementelle Vertiefungs- und Alpha-Beta-Bereinigungs-Teilbäume verwenden, die es nicht wert sind, genauer untersucht zu werden. Beim Schach reduziert dies die Anzahl der gesuchten Knoten auf etwa 1%. Eine kurze Einführung in das Alpha-Beta-Bereinigen finden Sie hier als Video: http://www.teachingtree.co/cs/watch?concept_name=Alpha-beta+Pruning

Ein guter Anfang für weitere Studien ist http://chessprogramming.wikispaces.com/Search . Die Seite bezieht sich auf Schach, aber die Such- und Optimierungsalgorithmen sind ziemlich gleich.

Ein anderer (aber komplexer) KI-Algorithmus - der für das Spiel besser geeignet wäre - ist "Temporal Difference Learning".

Grüße

Stefan

PS: Wenn Sie die Anzahl der möglichen Spielzustände reduzieren (z. B. sehr kleine Kartengröße, nur eine Bombe pro Spieler, sonst nichts), besteht die Möglichkeit, eine Bewertung für alle Spielzustände vorab zu berechnen.

--bearbeiten--

Sie können auch offline berechnete Ergebnisse der Minimax-Berechnungen verwenden, um ein neuronales Netzwerk zu trainieren. Oder Sie können sie verwenden, um handimplementierte Strategien zu bewerten / vergleichen. Zum Beispiel könnten Sie einige der vorgeschlagenen "Persönlichkeiten" und einige Heuristiken implementieren, die erkennen, in welchen Situationen welche Strategie gut ist. Daher sollten Sie Situationen (z. B. Spielzustände) "klassifizieren". Dies könnte auch von einem neuronalen Netzwerk erledigt werden: Trainieren Sie ein neuronales Netzwerk, um vorherzusagen, welche der handcodierten Strategien in der aktuellen Situation am besten funktioniert, und führen Sie sie aus. Dies sollte zu extrem guten Echtzeitentscheidungen für ein echtes Spiel führen. Viel besser als eine Suche mit niedrigem Tiefenlimit, die sonst durchgeführt werden kann, da es nicht so wichtig ist, wie lange die Offline-Berechnungen dauern (sie sind vor dem Spiel).

- bearbeiten # 2 -

Wenn Sie Ihre besten Züge nur alle 1 Sekunde neu berechnen, können Sie auch versuchen, eine höhere Ebene zu planen. Was meine ich damit? Sie wissen, wie viele Züge Sie in 1 Sekunde ausführen können. Sie können also eine Liste der erreichbaren Positionen erstellen (z. B. wenn dies 3 Züge in 1 Sekunde wären, hätten Sie 25 erreichbare Positionen). Dann könnten Sie wie folgt planen: Gehen Sie zu "Position x und platzieren Sie eine Bombe". Wie einige andere vorgeschlagen haben, können Sie eine "Gefahren" -Karte erstellen, die für den Routing-Algorithmus verwendet wird (wie geht man zu Position x? Welcher Pfad sollte bevorzugt werden [in den meisten Fällen sind einige Variationen möglich]). Dies ist im Vergleich zu einer großen HashTable weniger speicherintensiv, führt jedoch zu weniger optimalen Ergebnissen. Da jedoch weniger Speicher benötigt wird, kann dies aufgrund von Caching-Effekten schneller sein (bessere Verwendung Ihrer L1 / L2-Speicher-Caches).

ZUSÄTZLICH: Sie können Vorsuchen durchführen, die nur Züge für jeweils einen Spieler enthalten, um Variationen zu sortieren, die zum Verlust führen. Nehmen Sie deshalb alle anderen Spieler aus dem Spiel ... Speichern Sie, welche Kombinationen jeder Spieler wählen kann, ohne zu verlieren. Wenn nur Züge verloren gehen, suchen Sie nach den Zugkombinationen, bei denen der Spieler am längsten am Leben bleibt. Um diese Art von Baumstrukturen zu speichern / verarbeiten, sollten Sie ein Array mit folgenden Indexzeigern verwenden:

class Gamestate {
  int value;
  int bestmove;
  int moves[5];
};

#define MAX 1000000
Gamestate[MAX] tree;

int rootindex = 0;
int nextfree = 1;

Jeder Zustand hat einen Bewertungswert und wird beim Bewegen (0 = Stopp, 1 = Aufwärts, 2 = Rechts, 3 = Abwärts, 4 = Links) mit den nächsten Spielzuständen verknüpft, indem der Array-Index in "Baum" in Zügen [0 "gespeichert wird ] zu bewegen [4]. Um Ihren Baum rekursiv zu erstellen, könnte dies folgendermaßen aussehen:

const int dx[5] = { 0,  0, 1, 0, -1 };
const int dy[5] = { 0, -1, 0, 1,  0 };

int search(int x, int y, int current_state, int depth_left) {
  // TODO: simulate bombs here...
  if (died) return RESULT_DEAD;

  if (depth_left == 0) {
    return estimate_result();
  }

  int bestresult = RESULT_DEAD;

  for(int m=0; m<5; ++m) {
    int nx = x + dx[m];
    int ny = y + dy[m];
    if (m == 0 || is_map_free(nx,ny)) {
      int newstateindex = nextfree;
      tree[current_state].move[m] = newstateindex ;
      ++nextfree;

      if (newstateindex >= MAX) { 
        // ERROR-MESSAGE!!!
      }

      do_move(m, &undodata);
      int result = search(nx, ny, newstateindex, depth_left-1);
      undo_move(undodata);

      if (result == RESULT_DEAD) {
        tree[current_state].move[m] = -1; // cut subtree...
      }

      if (result > bestresult) {
        bestresult = result;
        tree[current_state].bestmove = m;
      }
    }
  }

  return bestresult;
}

Diese Art der Baumstruktur ist viel schneller, da die dynamische Zuweisung von Speicher sehr langsam ist! Das Speichern des Suchbaums ist jedoch auch ziemlich langsam ... Dies ist also eher eine Inspiration.

SDwarfs
quelle
0

Würde es helfen, sich vorzustellen, dass sich jeder abwechselt?

Technisch gesehen tun sie dies im zugrunde liegenden System tatsächlich, aber da die Dinge verschachtelt und überlappt sind, scheinen sie gleichzeitig zu laufen.

Denken Sie auch daran, dass Sie AI nicht nach jedem Animationsframe ausführen müssen . Viele erfolgreiche Casual Games führen den KI-Algorithmus nur etwa einmal pro Sekunde aus und geben den KI-gesteuerten Charakteren Informationen darüber, wohin sie gehen sollen oder was sie tun sollen. Diese Informationen werden dann zur Steuerung der KI-Charaktere verwendet auf den anderen Frames.

Raceimaztion
quelle
Ich berechne AI nicht in jedem Frame der Animation, sondern in jeder Sekunde. Jede Sekunde sammelt meine Umgebung Aktionen aller Spieler und sendet ihnen einen neuen aktualisierten Status.
Billda