TVTower
TVTower copied to clipboard
KI Aufräumarbeiten
- Player per Funktion verfügbar machen
- Zeit über Player verfügbar - seltener API-Aufrufen
- Raumverlassen nicht loggen
- Funktion im Task "wieviele Minuten seit letzter Ausführung"
Die Taskfunktion wollte ich dafür nutzen, den Roomboard-Task "abzubrechen" wenn man er nicht zu lange zurückliegt. Ich würde das aber ungern beim Überschreiben des getSituationPriority machen, da das ziemlich oft aufgerufen wird. Mir schwebte vor im Task.activate zu prüfen, ob vielleicht doch keine Ausführung nötig ist (setCancel()). Aber das Abbrechen geht schief - denn der "goToRoom"-Job wird immer ausgeführt. Auch bei externen Abbrüchen, sollte man nicht erst zum falschen Raum gehen, bevor der Abbruch wirklich stattfindet.
Im ersten Commit hast du statt "minute" die Variable "mintue" genannt (und auch konsequent genutzt)
Ansonsten ist mir erstmal kein "Fehler" aufgefallen (beim Durchschauen).
Bedenke aber beim "cachen" der Zeiten (hour, day)... Das zwischen Aktualisierung der Variable und dem eigentlichen Nutzen im Code (wo du nun player.minute etc nutzt) Zeit vergangen sein kann..da ja die KI parallel zum Spiel laeuft (ausser sie synchronisiert sich zu den Ticks). Entsprechen kann beim Schnellvorlauf oder Rucklern die Zeit abweichen ...
Ich habe die ersten Commits zusammengefasst und einen für den Schreibfehler ergänzt. Das Problem mit dem Cachen der Zeit ist mir bewusst. Es gab einen Fall, wo das Cachen Probleme bereitet hat: Am Tagesbeginn wird das Budget berechnet - dort war der Player aber noch nicht aktualisiert. Ansonsten habe ich keine Fälle gesehen, wo eine eventuelle Abweichung eine Rolle spielen würde.
Diese lesenden Zugriffe sollten denke auch nicht soooo wichtig sein (Threadsicherheit, Performance). Wenn man da zu Beginn eines Funktionsaufrufes ein mehrfach genutztes "GetMoney()" cached sollte das eigentlich ausreichen.
Alternativ bietet sich hier aber auch an, OnMoneyChanged
zu nutzen :-p
function DefaultAIPlayer:OnMoneyChanged(value, reason, reference)
self.Budget:OnMoneyChanged(value, reason, reference)
for k,v in pairs(self.TaskList) do
v:OnMoneyChanged(value, reason, reference)
end
end
dort koennte ja einfach der gecached-te Wert aktualisiert werden ;-)
Beide Ansaetze koennen dahingehend Probleme machen, dass die "onMoneyChanged"-Events vlt. zurueckgestellt werden (wenn Lua selbst einen weiteren Tick "scheduled", also einplant, und der vorgezogen wuerde). In dringenden Faellen muesste man also nach dem Kauf von Sachen, den "money"-Wert aktiv nachfragen/Cache erneuern. Alternativ geht natuerlich immer ein "moneyCache" und "moneyCacheValid" ... was du "false" setzen kannst - und ein Spielereigenes "Lua:GetMoney()" in Lua, was den Cache erneuert, wenn invalide, und am Ende den Cachewert zurueckgibt. Damit wuerde nur nach Bedarf "TVT.GetMoney()" aufgerufen. Gleiches geht auch mit anderen gecached-ten Variablen.
Die Budgetbehandlung ist ohnehin noch so eine Sache. Die Aktualisierung der Task-Budgets ist mit dem aktuellen Ansatz nicht trivial, da die Tasks selbst beim Kauf schon den bezahlten Preis vom aktuellen Budget abziehen. Wenn man sich innerhalb einer Funktion einmal den Geldbetrag holt und dann mit diesem Wert weiterarbeitet, ist das meiner Ansicht nach konsequent und konsistent, schließlich werden ja anhand des einmal geholten Betrags Entscheidungen getroffen. Wenn sich in der Zwischenzeit der Wert ändert, müsste man eigentlich immer nochmal komplett von vorne anfangen.
Cacheinvalidierung ist allgemein ein sehr kritisches Problem. Für die KI bei TVTower braucht man es aber nicht zu übertreiben, da falsche Werte kaum Schaden anrichten. Mit der Faustregel "Der Task holt sich am Anfang die benötigten Werte und geht davon aus, dass sie stimmen, solange die Verarbeitung dauert" kommt man ziemlich weit.
Wenn es von Deiner Seite keine Einsprüche gibt, würde ich die Änderungen mergen.
Hmm ... du kannst das gerne so uebernehmen - aber schau noch mal drueber, ob du irgendwo "money" nutzt - im Sinne von "genau in dem Moment verfuegbar" (bspweise vor dem Filmkauf). Fuer Dinge wie "Budgetplanung" kann generell der gecachte genutzt werden. Alles andere sollte einen durch das "OnMoneyChanged"-aktuell gehaltenen Wert nutzen - oder wirklich "TVT:GetMoney()" aufrufen.
Davon abgesehen: gerne uebernehmen.
OK. Es gibt gar nicht so viele Verwendungen von GetMoney(). Einmalig bei einer Job-Initialisierung ist das kein Problem, das wird ja nicht häufig aufgerufen. Bei Prio-Berechnungen sieht es da schon anders aus. Das räume ich also noch etwas auf.
Super. Kannst dann danach gerne mergen.
Hm. So trivial ist das Ganze wohl doch nicht. Wenn die KI unmittelbar bei Spielbeginn startet, bekommt sie die moneyChanged-Events für das initiale Geld und den Kredit vom Boss. Wenn man aber Spieler 1 nach Spielstart als KI aktiviert, kommen diese Events nicht. OnDayBegins ist aber auch kein guter Zeitunkt für eine Synchronisation (player.money=TVT:GetMoney()), weil erst danach die changeEvents (mehrere) für die Fixkosten kommen und dann Beträge vom gerade synchronsierten Wert abziehen (die dort schon berücksichtigt waren).
Naja ... aber das mit dem Spieler 1 nach Spielstart... waere ja eh ein Bug, wenn da keine Events reinkommen ...
Ich habe doch nochmal einen separaten Commit zum Drüberschauen erstellt. Die Events für den Spieler 1 sind ja nur eins der Probleme. OnDayBegins in Kombination mit onMoneyChanges ist das zweite: Die FixkostenEvents kommen erst nach OnDayBegins rein. Zu diesem Zeitpunkt soll ja aber das Budget schon berechnet sein. Und irgendwann möchte ich den Wert auch explizit synchronisieren - da eignet sich OnDayBegins auch.
Mein Lösungsansatz:
- Manipulation des Cache-Wertes nur im DefaultPlayer
- Explizites Setzen bei OnDayBegins
- OnMoneyChangeEvents aktualisieren anhand des Diff-Wertes nur nach 0:10Uhr
- alle GetMoney-Aufrufe in den Tasks etc. gehen auf player.money
Du kannst auch neue Events einführen...also einen Event, der eben nach dem OnDayBegins und den Berechnungen kommt
OnNewDayBegun OnNewDayCalculationsDone ...
Wird ja nicht all zu oft aufgerufen und ist besser, als eine exotische Sonderbehandlung im Code der KIs.
Auch koennte der OnDayBegins ersetzt werden mit: OnDayEnds (neu hinzuzufuegen) ...Berechnungen. OnDayBegins
Also alles im Sinne von "before / after" oder "begin / done".
An die Events will ich lieber nicht ran. Ich habe auch nicht das Gefühl, dass das OnDayBegins-Event an der falschen Stelle kommt oder neue Events benötigt würden. Die Fixkosten-Money-Change-Events werden meiner Ansicht nach zu spät gesendet - nämlich nicht am Ende des Tages, sondern nach Begin des neuen Tages.
Mir wäre es fast das liebste, den Money-Aufräum-Punkt komplett separat zu behandeln.
Koennen wir gern gesondert behandeln.
Ich habe den ersten Commit auf den Master gepusht und lasse diesen PR als Erinnerung offen, dass für den Preis noch was gemacht werden solle.
Das Cachen des verfügbaren Geldes erfolgt im Zuge der aktuellen KI-Anpassungen.