Befehlsreihenfolge Architektur der Zwergenfestung

21

Was ist die eleganteste Art, ein Befehlsbestellsystem für AI zu implementieren? Wenn Sie beispielsweise in einer Zwergenfestung ein Waldstück zum Holzhacken markieren, gehen die Zwerge folgendermaßen vor:

  1. Gehe zum Baum
  2. Den Baum fällen
  3. Liefere Holz auf die Halde
  4. Geh zu einem anderen Baum
  5. und so weiter..

Ich habe bereits einen Stapelbefehl mit der Nummer. 1, die vom Ruhezustand zum Erreichen des Zielfelds des Baums übergeht.

Was ich befürchte, ist, wie dies chaotisch wird, wenn ich mehr Aufträge wie diesen erstelle:

Ein Haus bauen

  1. Gehe zum Vorrat
  2. Holz zum Bau bringen
  3. Geh zurück zu Vorratslager
  4. Bringen Sie Stein zum Baugebiet
  5. Animieren Sie das Gebäude-Sprite

Pflanzen

  1. Gehe zum Vorrat
  2. Saatgut auf den Hof bringen

Brauen

  1. Gehe zum Vorrat
  2. Anlage zum Stillstand bringen
  3. animiere das Brau-Sprite

Meine Frage ist also, wie ich ein Befehlsbestellsystem wie die Zwergenfestung implementiere und gleichzeitig Spaghetti-Code vermeide. Gibt es Datenstrukturen, die ich studieren muss? Muss ich die Befehlssequenz in einer separaten XML-Datei ablegen?

Jed T.
quelle
1
Die Zwergenfestung hat tatsächlich kein solches System. Zwerge erhalten jeweils eine Aufgabe und untätige Zwerge suchen nach etwas, das sie tun können. ("Hey, da ist ein Baum zum Hacken markiert - ich sollte ihn hacken!" / "Hey, da ist etwas Holz, das nicht auf Lager ist - ich sollte es zu einem bringen!")
user253751
1
Zwerge werden vom Spieler nicht zugewiesen, sondern vom System "zugewiesen". Dies ist genau die Architektur, die Jed T. oben beschreibt. Auftrag anlegen und das System ordnet einzelne Komponentenaufgaben zu, um diesen Auftrag zu erfüllen.
Attackfarm
2
Beachten Sie, dass dies als Task Allocation and Scheduling bezeichnet wird und in verschiedenen technischen Bereichen ausführlich untersucht wird. Sie werden viele Artikel finden, die sich mit diesem Problem befassen und von Interesse sein könnten.
TonioElGringo
@Attackfarm Das System entscheidet nicht alle Aufgaben im Voraus. Es werden auch nicht mehrere Aufgaben demselben Zwerg zugewiesen. Eine Aufgabe wird anfänglich zugewiesen, und wenn sie abgeschlossen ist, hat dies zur Folge, dass eine andere Aufgabe verfügbar gemacht wird.
user253751
2
Dies klingt nach einem hervorragenden Anwendungsfall für die zielgerichtete Aktionsplanung
Problematisch

Antworten:

27

Zuerst sehen Sie, dass Ihre Befehle in Form einer Liste vorliegen. Daher besteht Ihr erster Instinkt möglicherweise darin, diese Struktur neu zu erstellen, und jeder Zwerg durchläuft diese Liste nacheinander. Ich schlage jedoch vor, die Liste in Schritte aufzuteilen , wobei jeder Schritt eine oder mehrere Voraussetzungen hat. Anschließend führen Sie den gesamten Befehl in umgekehrter Reihenfolge aus . Lassen Sie mich mit einem Beispiel demonstrieren:

Holz schneiden

  • Trage ich Holz und lagere es? Ja, lass es fallen
  • Trage ich Holz? Ja, gehen Sie zu einem Vorratslager
  • Bin ich an einem baum Ja : Hacken Sie es
  • Nein zu allen oben : Geh zu einem Baum

Die Vorteile davon sind:

  • Sehr einfach zu implementieren
  • Flexibel - Sie können diese Liste frei zerlegen, Elemente hinzufügen, Elemente entfernen und Elemente kombinieren
  • Kein Staat - können Sie diese Liste von oben für jeden Zwerg in jedem Zustand führen, und der Zwerg wird genau das Richtige TM

Nachteile:

  • Es ist leicht, in Schleifen zu stecken, da es keinen Zustand gibt und kein Bewusstsein dafür, dass man stecken bleibt

Logischerweise können Sie diese Befehle als Flussdiagramm darstellen, das jedes Mal von oben ausgeführt wird. Was Sie tun, hängt davon ab, ob Sie bei jedem Schritt mit Ja / Nein antworten. Ob Sie dies in Code oder in einer externen Datei wie XML implementieren, liegt bei Ihnen.

congusbongus
quelle
2
Dies hat auch den Vorteil, dass der Status Befehle außer Kraft setzen kann. Habe ich Hunger? Wenn ja, lassen Sie alles fallen und stellen Sie die Aufgabe, zu "essen", wobei das Essen dem Holztransport ähnlich ist.
Ratschenfreak
7
@ratchetfreak "Ich weiß, dass die Sicherheit meiner Festung davon abhängt, dass ich gegen dieses Monster kämpfe, damit es die Zivilisten nicht angreift, aber meine Güte, mein Magen knurrte nur!" Versuchen Sie, es in dieser Hinsicht nicht zu sehr wie DF zu machen: P
Colonel Thirty Two
Ich denke, dies ähnelt dem, was verwendet (oder zumindest verwendet) wurde, was das fehlerhafte Artefakt von planepacked ermöglichte (dies lag an einem verbotenen Gegenstand, der die Schleife verursachte)
Destrictor
3
@ColonelThirtyTwo Wo ist das drin fun? ;)
Lasse
Ich empfehle dringend, keine deklarative symbolische Aktionsplanung zu verwenden. Das Debuggen ist grundsätzlich nicht möglich, und es kann leicht zu unerwünschtem Verhalten kommen. Es ist viel einfacher, die Aktionssequenzen für jede Aufgabe prozedural fest zu codieren.
mklingen
10

Wenn Sie Sequenzen ziemlich allgemein machen können, gibt es nicht viel von einem Spaghetti-Code.

Bei Lieferungen zB: WorkTask arbeitet mit einem WorkPlan. Workplan legt fest, welche Art von Ressourceneinheit aus welcher Art von Haus ausgewählt werden muss, welche Laufanimation verwendet wird, welche Arbeitsanimation verwendet wird, wie viel Zeit für die Arbeit benötigt wird und all diese Details. Am Ende könnte WorkTask also so aussehen:

  1. Finde% resource1% auf der Karte
  2. Gehe zu diesem Ort mit% animation_1%
  3. Vor Ort arbeiten mit% animation_2% für% time%
  4. Nehmen Sie% req_resource1% in% req_count1% count
  5. Gehe mit% animation% zu% home%
  6. Starte% animation_6% inside für% time_2%
  7. etc..

Wir setzen den beschriebenen Ansatz erfolgreich ein. Wir haben ~ 15 Aufgaben in unserem Spiel. Einige Höhepunkte:

  • Aufgaben geben Aktionen der Einheiten (gehen Sie dorthin, betreten Sie, verlassen Sie, gehen Sie hier, bleiben Sie, arbeiten Sie, gehen Sie)
  • Die Aktion endet entweder mit dem Status "Fertig" oder "Abgebrochen" und übergibt sie an die Aufgabe
  • Alles ist fest codiert (kein Parser, keine Interface-Methoden, keine Rückwärtskompatibilität)
  • Jede Task implementiert eine abstrakte Task-Klasse mit nur wenigen gängigen Methoden (Erstellen, Ausführen, Speichern, Laden).
  • Im Allgemeinen eine Aufgabe pro Modul, ähnliche Aufgaben befinden sich jedoch in einem Modul
  • Sehr ähnliche Aufgaben gehören zu einer Klasse und werden von wenigen IFs geregelt (Lieferung an Haus oder Lieferung an Einheit).
  • Jede Aufgabe erfordert eine ordnungsgemäße Sperrung und Entsperrung von Ressourcen (z. B. wenn die Einheit bei einem beliebigen Schritt stirbt, muss die gesperrte Ressource freigegeben werden).
Kromster sagt Unterstützung Monica
quelle
2
Dies ist das System, das wir in unserem zwergenhaften Spiel verwenden. Aufgaben werden durch Verhaltensbäume erledigt. Ressourcen werden durch Verhalten gesperrt und durch Fehler entsperrt. Es ist viel robuster und einfach zu debuggen als der von der Top-Antwort beschriebene Aktionsplanungsansatz
mklingen
5

Das ist also im Grunde ein topographisches Sortierproblem.

Sie haben eine Grafik, jeder Knoten ist eine Aufgabe, die erledigt werden muss, und einige Knoten hängen von anderen Knoten ab (dies wird durch eine Kante in der Grafik vom abhängigen Knoten zum abhängigen Knoten dargestellt). Wenn Sie alle Aufgaben ausführen möchten, müssen Sie eine EINZIGE topografische Reihenfolge der Knoten erstellen (die abhängigen Knoten befinden sich hinter den Knoten, von denen sie abhängen).

In der Regel gibt es viele solcher Anordnungen (da einige Knoten keine Abhängigkeiten aufweisen und an eine beliebige Stelle verschoben werden können, und einige Knoten dieselben Abhängigkeiten aufweisen und nicht voneinander abhängig sind, können sie also in beliebiger Reihenfolge untereinander und mit jedem Knoten in beliebiger Reihenfolge vorliegen) an einem beliebigen Ort abgelegt werden, nachdem die Abhängigkeiten ausgeführt wurden und bevor Knoten, die davon abhängen, ausgeführt werden.

Es ist auch möglich, dass es keine Möglichkeit gibt, eine grafische Topografie zu sortieren - dies geschieht, wenn das Diagramm Zyklen enthält (Sie haben kein Holz, um Holz zu erhalten, müssen Sie einen Baum fällen, um einen Baum zu fällen, müssen Sie eine Axt herstellen) brauche Holz). In diesem Fall sollte der Algorithmus dem Spieler wahrscheinlich anzeigen, dass diese Aufgaben nicht ausgeführt werden können.

Sie können Knoten auch Prioritäten hinzufügen, und die Aufgabe kann darin bestehen, unter allen Ordnungen, die die Abhängigkeiten erfüllen, eine solche Reihenfolge zu finden, bei der die Knoten mit der größeren Priorität zuerst ausgeführt werden.

Sie können auch wiederkehrende Aufgaben hinzufügen. Am einfachsten ist es wahrscheinlich, die Aufgabe mit Zeitüberschreitung bei jeder Ausführung erneut zum Diagramm hinzuzufügen.

Jetzt, wie man es löst - http://en.wikipedia.org/wiki/Topological_sorting

Sebastian Pidek
quelle