[Deutsch] KI-Scripte

  • Das Kernstück jeden guten Computerspiels ist die KI der Nicht-Spieler-Gegner. In Gilde 2 ist die KI eher ... unterdurchschnittlich. Wobei es sicher Beispiele für noch schwächere Computergegner gibt, die allein durch Cheats zur (mittelmäßigen) Herausforderung werden.


    Auch in Gilde 2 hat die KI bei entsprechender Schwierigkeit ein paar unfaire Tricks auf Lager, aber als Meister des Spiels ist es doch recht leicht, selbst mit diesen fertig zu werden. Glücklicherweise ist es verhältnismäßig einfach der KI via Scripten ein paar zusätzliche Tricks beizubringen. Das wird die KI nicht grundlegend besser machen, aber immerhin etwas.


    Die KI Scripte, die sowohl von allen Computer-Dynastien, als auch von KI-verwalteten Spieler-Betrieben benutzt werden, befinden sich unter Scripts/AI


    Dort gibt es 4 unterschiedliche Arten von Ordnern:


    Professionsspezifisch (z.B: Church_ev, Hospital, Robber ...), Dynastie-Spezifisch (BaseTree), Maßnahmen, die die Gunst beeinflussen (Favor) und dann noch einen Ordner OfficeSession. Ich bin aber ganz ehrlich, dieser letzte Ordner scheint keinen spürbaren Effekt zu haben, daher kann ich dazu nicht wirklich etwas sagen, möglicherweise sind die Dateien mit Renaissance obsolet?


    Es gibt natürlich noch eine ganze Reihe von hardcoded KI-Lines, z.B. die KI-Verwaltung (die sich nur indirekt über Properties in den Scripts/Buildings/-Scripten beeinflussen lässt), aber im Groben kann man sagen, dass die KI nur das im Spiel unternehmen kann, was sich in diesen Ordnern befindet. Und ja, gemessen am Umfang des Spiels ist das ziemlich wenig.


    Die Professionsspezifischen Scripte werden nur von den Sims aufgerufen, die die Profession haben, bzw. wenn ein Dynastiecharakter sich innerhalb eines von der KI gesteuerten Betriebe befindet, dann auch die. Das ist übrigens nicht als Unterstützung für den Spieler gedacht, sondern ist die, wenig elegante, Lösung, wie man Dynastiecharaktere der KI dazu bringt, im Betrieb mitanzupacken. Und weil Betriebe für Schattendynastien gleichzeitig das Zuhause sind, und Kinder sich viel zuhause aufhalten, arbeiten Kinder von Schattendynastien oft im Betrieb mit. Für das Script werden sie einfach als zusätzliche steuerbare Arbeitskräfte betrachtet.


    BaseTree ist nochmals unterteilt in CheckGuildHouse, CheckMyrmidon, CheckSquad, CheckDynasty - je nach Kategorie der Scripte. Scripte, die das Verhalten von Dyn-Charakteren beeinflussen, finden sich also unter CheckDynasty, Schergen (und deren Anweisungen) bei CheckMyrmidon.


    Wie funktionieren aber nun die Scripte?


    Eigentlich ist es ganz einfach. Zunächst brauchen wir ein KI-Objekt. Bei den professionsspezifischen Scripten ist das bereits festgelegt, unter BaseTree finden sich aber sowohl Schergen, Kinder, Dynastien als auch Dynastiemitglieder. Das heißt bei Scripts/AI/BaseTree/CheckDynasty.lua wird zunächst erstmal ein Mitglied für eine Aktion ausgewählt.


    Die Scripte sind unterteilt in function Weight() und function Execute()


    In Weight wird grundsätzlich festgelegt, ob das Script ausgeführt wird (Execute) oder nicht. Und das passiert über eine Zufallsabfrage. Wird ein Wert von 100 zurückgegeben, wird das Script auf jedenfall ausgeführt. Ob ein Script bei einem Wert von über 100 öfter ausgeführt wird, weiß ich nicht, CheckDynasty.lua legt das aber zumindest nahe. Wird ein Wert unter 100 angegeben und ist dieser kleiner als Rand(100), dann wird das Script schlicht nicht ausgeführt. Damit ein KI-Script nicht permanent ausgeführt wird, kann man einen internen RepeatTimer setzen, z.B. SetRepeatTimer("dynasty", "AI_CheckPartyMember", 0.75), der dann via

    Code
    if not ReadyToRepeat("dynasty", "AI_CheckPartyMember") then
    		return 0
    	end


    abgefangen wird.


    Die interessante Frage ist nun, was passiert, wenn ein Wert kleiner als Rand(100) zurückgegeben wird.


    Möglichkeit 1: Die Aktion wird beendet, ggf. versucht die KI beim nächsten CheckDynasty.lua eine neue Aktion. (Für mich sehr wahrscheinlich)
    Möglichkeit 2: Wenn ein Script 0 zurückgibt, wird das nächste Script, das sich innerhalb des selben Ordners befindet, getestet, solange, bis ein Execute stattfindet. (Wäre möglich)


    Wählt eine Dynastie also einen Angriff, sagen wir medium, stehen dem System eine ganze Reihe möglicher Scripte zur Verfügung. (di_HaveAStabbingGaze.lua, di_InspectBuisness, di_TortureCharacter etc.)
    Funktioniert das System nach Möglichkeit 1, würde ein zufälliges Script aus allen ausgewählt, das entweder ausgeführt wird, oder den Angriff beendet.
    Funktioniert das System nach Möglichkeit 2, geht das System entweder systematisch durch alle Scripte, oder wählt auch hier ein zufälliges Script und sollte kein Execute zustande kommen, wird ein weiteres gewählt, bis entweder alle Scripte 0 zurückgeben oder ein Execute zustande kommt.


    Beide Möglichkeiten hätten ihre Tücken meiner Meinung nach, 1 ist ressourcenschonender, aber unter Umständen kommt so erst recht spät ein Angriff zustande, während es bei 2 natürlich unter Umständen sehr viele Durchläufe geben kann. Vielleicht funktioniert das Spiel auch ganz anders, aber das hier sind die meiner Meinung nach einzigen logischen Erklärungen.


    Die meisten KI-Scripte starten ein Script als Execute(), z.B. via MeasureRun("SIM", "diute_Target", "UseToadExcrements") oder
    SetProperty("SIM", "SpecialMeasureId", -MeasureGetID("WinBelievers"))


    Welche Variante besser ist weiß ich nicht, vielleicht wird MeasureRun sofort ausgeführt und ein Property erst, wenn der Sim idle ist, bzw. nur produziert, oder es wird stündlich überprüft, ob es SpecialMeasureId-Properties gibt ... Ich stehe lieber auf der sicheren Seite mit MeasureRun.


    Wie ihr seht, wird dann meist das normale Measure-Script gestartet. Innerhalb der Measure-Scripte könnt ihr aber differenzieren, falls ihr bei KI-gesteuerten Charakteren ein spezielles Verhalten wollt, oder Auswahlboxen für Spieler nicht benötigt. Dazu eignen sich z.B. if IsStateDriven() then oder if BuildingGetAISetting("WorkBuilding","Produce_Selection")>0 then




    Am Besten klickt ihr euch einfach mal durch ein paar Scripts, dann wird das Meiste von allein klar. Wollt ihr ein neues KI-Verhalten hinzufügen, müsst ihr bloß ein neues Script im richtigen Ordner anlegen, ein Eintrag in Measures.dbt ist nicht nötig.

  • Werden die DBT-Einträge in "AIScripts.dbt" und "AICharacterTypes" irgendwo ausgewertet? Die scheinen mir ja der Ausgangspunkt zu sein für die AI-Verarbeitung. Ich verstehe die aber grad so, dass lediglich die drei Ordner "CheckDynsasty", "CheckGuildHouse" und "CheckMyrmidon" abgearbeitet werden.


    Bezüglich der Skriptauswahl. Eine Gewichtung von ansonsten gleichwertigen Skripten kann n.m.E. nur so sinnvoll erfolgen:

    • Gleichwertige Skripte ermitteln (z.B. in CheckDynasty/Attack die Skripte High, Medium und Low)
    • Weight() auf den Skripten aufrufen, um Gewichtungen zu ermitteln. (z.B. 10, 50, 100)
    • Gewichtungen zusammenaddieren (10 + 50 + 100 = 160 = n)
    • Jedem Skript wird ein Intervall zwischen 0 und n zugeordnet, der seiner Gewichtung entspricht (high=1--10, medium=11--60, low=61--160).
    • Zufällig wird eine Zahl zwischen 1--n (x = Rand(n) + 1) ermittelt.
    • Es wird dasjenige Skript ausgeführt, in dessen Intervall x liegt. Oder Im Fall eines gleichnamigen Unterordners wird das Verfahren für die nächste Skript-Ebene fortgesetzt.

    Das Ganze sollte sich in C++ auch einigermaßen effizient implementieren lassen.

  • An so eine Gewichtung hatte ich auch schon mal gedacht, aber schien mir dann sehr aufwendig, wenn erst alle Weights ermittelt werden. Aber wie gesagt, ich kenne den SourceCode nicht, vielleicht ist es genau so ...


    Die erwähnten DBTs kenne ich, aber ich habe noch keine Script gesehen, dass darauf irgendwie reagiert. D.h. wenn überhaupt, dann werden diese Einträge irgendwo im Source verwendet für Hardcoded-Aktionen.

  • Beim durchforsten und ausprobieren konnte ich noch keine realen Fortschritte erzielen.


    Ich hatte vor, ein "MsgNewsNoWait" an alle Spieler zu schicken, um die Ausführung eines Skriptes zu erkennen. Allerdings wird das nicht ausgeführt. Auch dort, wo ein "LogMessage" im Skript steht, finde ich keine Einträge im Log.


    Wie lassen sich KI-Skripte also testen -- also die Ausführung erzwingen und erkennen?

  • Kaum macht man es richtig... Ich hatte ausgerechnet die MessageClass verwendet, die ich im Spiel gefiltert habe. Ich hänge meinen Test-BaseTree als ZIP an. Alle weiteren Dateien habe ich gelöscht, um die Auswirkungen genauer beobachten zu können.


    Hier ist das Ergebnis im Spiel:


    Und meine Interpretation:

    • Die Datei "AIScripts.dbt" wird völlig ignoriert. Es werden einfach alle Skripte ausgeführt, die direkt im BaseTree liegen. Unterordner benötigen ein gleichnamiges Skript für die Entscheidung, ob die darin enthaltenen Skripte ausgeführt werden.
    • Ein Skript mit Weight() == 0 wird in keinem Fall ausgeführt. Das gilt auch, wenn kein weiteres Skript vorhanden ist.
    • Die Weight-Funktionen der Skripte werden tatsächlich zuerst aufgerufen und anschließend verarbeitet. Jeder Durchlauf führt auch tatsächlich ein Skript aus (Ausnahme: Alle Weight-Funktionen ergeben 0).
    • Insgesamt gehe ich davon aus, dass tatsächlich der Algorithmus aus meinem vorigen Kommentar verwendet wird.
    • Die AI-Dynastien (farbig und Schatten) werden sequentiell (nacheinander) und ohne Pause abgearbeitet. Die Abarbeitung ist dabei so schnell, dass selbst mehrere Sleep(100) nur wenig Druck aus den Nachrichten nehmen.
    • Auf die Aktuelle Dynastie wird in den vorhandenen Skripten per Alias "dynasty" zugegriffen. Welche Aliase sonst verfügbar sind und wie Daten an unterliegende Skripte weitergereicht werden, ist mir noch unklar. @Fajeth weißt du da mehr?
  • Außer dem alias "dynasty" gilt noch der feste Alias "SIM" für das ausführende Objekt, sofern es sich um ein sim-Objekt handelt.


    diese beiden werden auf jedenfall weitergegeben. Dann gibt es noch "Victim" im Fall von Angriffen, auch dieser bleibt bestehen. Also evtl. bleiben alle übergeordneten aliasse bestehen, aber das weiß ich nicht genau