Generischer Regel-Parser für RPG-Brettspielregeln - wie geht das?

19

Ich möchte einen generischen Regel-Parser für RPG-Systeme im Stift- und Papierstil erstellen. Eine Regel kann normalerweise 1 bis N Entitäten, 1 bis N Rollen eines Würfels und das Berechnen von Werten basierend auf mehreren Attributen einer Entität umfassen.

Beispielsweise:

Spieler hat STR 18, seine aktuell ausgerüstete Waffe gibt ihm einen Bonus von +1 STR, aber einen Malus von DEX -1. Er greift ein Monster an und die Spiellogik muss nun eine Reihe von Regeln oder Aktionen ausführen:

Der Spieler würfelt, wenn er zum Beispiel 8 oder mehr erhält (der Basisangriffswert, den er weitergeben muss, ist eines seiner Basisattribute!), Ist sein Angriff erfolgreich. Das Monster würfelt dann, um zu berechnen, ob der Angriff durch seine Rüstung geht. Wenn ja, wird der Schaden genommen, wenn der Angriff nicht geblockt wurde.

Neben einfachen mathematischen Regeln kann es auch Einschränkungen geben, z. B., dass diese nur für eine bestimmte Benutzerklasse gelten (z. B. Krieger gegen Zauberer) oder für andere Attribute. Das beschränkt sich also nicht nur auf mathematische Operationen.

Wenn Sie mit RPG-Systemen wie Dungeon und Dragons vertraut sind, wissen Sie, was ich vorhabe.

Mein Problem ist jetzt, dass ich keine Ahnung habe, wie ich genau das bestmögliche bauen kann. Ich möchte, dass die Leute in der Lage sind, jede Art von Regel aufzustellen und später einfach eine Aktion wie die Auswahl eines Spielers und eines Monsters durchzuführen und eine Aktion auszuführen (eine Reihe von Regeln wie ein Angriff).

Ich bitte weniger um Hilfe bei der Datenbank, sondern mehr darum, wie ich eine Struktur und einen Parser entwickeln kann, damit meine Regeln flexibel bleiben. Die Sprache der Wahl dafür ist übrigens PHP.

Edit I:

Lassen Sie mich mein Ziel präzisieren: Ich möchte eine benutzerfreundliche Oberfläche erstellen (für die niemand eine Programmiersprache lernen muss), um mehr oder weniger komplexe Spielregeln zu erstellen. Der einfache Grund: Persönliche Verwendung, um sich nicht ständig an alle Regeln erinnern zu müssen, wir spielen einfach nicht so oft und es ist ein Stopper, sie jedes Mal nachzuschlagen. Außerdem: Es scheint eine lustige Aufgabe zu sein, etwas zu tun und zu lernen. :)

Was ich bisher versucht habe: Nur über ein Konzept nachzudenken, anstatt Zeit zu verschwenden, eine falsche Architektur zu erstellen. Bisher bin ich auf die Idee gekommen, einem Benutzer zu erlauben, so viele Attribute zu erstellen, wie er möchte, und dann so viele Attribute zuzuweisen, wie er möchte. Eine Entität kann ein Spieler, ein Monster, ein Gegenstand, alles sein. Wenn Sie nun etwas berechnen, werden die Daten dem Regel-Parser zur Verfügung gestellt, so dass der Regel-Parser in der Lage sein sollte, Dinge zu tun, wenn Player.base_attack + dice (1x6)> Monster.armor_check dann Monster.health - 1; Die Frage hier ist, wie man diesen Parser erstellt.

Edit II:

Hier ist ein Beispiel für einen ziemlich einfachen Wert, aber um ihn richtig zu berechnen, müssen viele verschiedene Dinge und Variablen berücksichtigt werden:

Basisangriffsbonus (Laufzeit) Ihr Basisangriffsbonus (von der d20-Community allgemein als BAB bezeichnet) ist ein Angriffswurfsbonus, der sich aus der Charakterklasse und dem Level ergibt. Basisangriffsboni werden für verschiedene Charakterklassen mit unterschiedlichen Raten erhöht. Ein Charakter erhält einen zweiten Angriff pro Runde, wenn sein Basisangriffsbonus +6 erreicht, einen dritten mit einem Basisangriffsbonus von +11 oder höher und einen vierten mit einem Basisangriffsbonus von +16 oder höher. Basisangriffsboni, die aus verschiedenen Klassen gewonnen wurden, z. B. für einen Stapel mit mehreren Klassen. Der Basisangriffsbonus eines Charakters gewährt nach Erreichen von +16 keine weiteren Angriffe, kann nicht kleiner als +0 sein und erhöht sich nicht aufgrund von Klassenstufen, nachdem die Charakterstufe 20 erreicht hat. Für bestimmte Leistungen ist ein Mindestbonus für Basisangriffe erforderlich.

Sie können es hier lesen: http://www.dandwiki.com/wiki/Base_Attack_Bonus_(Term), einschließlich der Links zu Klassen und Kunststücken, die wiederum ihre eigenen Regeln haben, um die Werte zu berechnen, die für den Basisangriff erforderlich sind.

Ich begann zu glauben, dass es auch ziemlich schwierig sein wird, einen guten Regel-Parser zu erstellen, wenn man ihn so allgemein wie möglich hält.

Burzum
quelle
2
Ich habe heute Morgen genau über diese Art von Problem nachgedacht (nicht im Zusammenhang mit RPG, sondern mit Regelverarbeitungs-Engines) und versucht, mir nicht-staatliche Maschinenansätze für die Regelverarbeitung vorzustellen und herauszufinden, wie effektiv kombinatorische Parser bei der Erfüllung einer Aufgabe sind, die normalerweise von ausgeführt wird Zustandsmaschinen. Ich denke, es gibt eine reiche Möglichkeit für monadische Kombinatoren, die meisten Zustandsmaschinenprobleme sauberer anzugehen. Das klingt vielleicht nach Kauderwelsch, aber ich denke, an dieser Idee steckt etwas, nur meine 2 Cent. RPG-Systeme sind ein klassisches Problem, das Spaß macht. Ich mag das Codieren. Vielleicht probiere ich diesen Ansatz aus.
Jimmy Hoffa
1
@jk. Dieser Artikel erinnert mich an ein Muster, das ich für das Parsen von Befehlszeilenargumenten mochte, wobei ein Wörterbuch von Funcs verwendet wurde, das den Programmstatus basierend auf den Argumenten als Schlüssel für das Wörterbuch initialisiert. Überrascht fand ich diesen Beitrag von Yegge noch nie, sehr cool, danke, dass Sie darauf hingewiesen haben.
Jimmy Hoffa
4
Ich bin mir nicht sicher, warum dies als "keine echte Frage" abgeschlossen wurde. Es handelt sich um eine übergeordnete "Whiteboard" -Frage zum Entwickeln einer Anwendung mit bestimmten Anforderungen (RPG-Regelsystem). Ich habe für die Wiedereröffnung gestimmt, aber es werden noch 4 weitere Wiedereröffnungsstimmen benötigt, um wiedereröffnet zu werden.
Rachel
1
Ehrlich gesagt dachte ich, dass diese Seite genau für diese Art von konzeptuellen Fragen gedacht ist, während stackoverflow.com für Code- / Implementierungsprobleme gedacht ist.
Burzum

Antworten:

9

Was Sie verlangen, ist im Wesentlichen eine domänenspezifische Sprache - eine kleine Programmiersprache für einen engen Zweck, in diesem Fall zur Definition von P & P-RPG-Regeln. Das Entwerfen einer Sprache ist im Prinzip nicht schwierig, aber es gibt eine beträchtliche Menge an Vorkenntnissen, die Sie erwerben müssen, um überhaupt produktiv zu sein. Leider gibt es keine zentrale Referenz für dieses Zeug - Sie müssen es durch Versuch, Irrtum und viele Nachforschungen aufgreifen.

Suchen Sie zunächst einen Satz primitiver Operationen, mit denen andere Operationen implementiert werden können. Beispielsweise:

  • Hol dir oder setze eine Eigenschaft des Spielers, eines NPCs oder eines Monsters

  • Holen Sie sich das Ergebnis eines Würfelwurfs

  • Berechnen Sie arithmetische Ausdrücke

  • Bedingte Ausdrücke auswerten

  • Führen Sie eine bedingte Verzweigung durch

Entwerfen Sie eine Syntax, die Ihre Grundelemente ausdrückt. Wie werden Sie Zahlen darstellen? Wie sieht eine Aussage aus? Sind Anweisungen mit Semikolon abgeschlossen? Newline-terminiert? Gibt es eine Blockstruktur? Wie werden Sie es anzeigen: durch Symbole oder Einrückungen? Gibt es Variablen? Was ist ein legaler Variablenname? Sind Variablen veränderbar? Wie können Sie auf Objekteigenschaften zugreifen? Sind Objekte erstklassig? Können Sie sie selbst erstellen?

Schreiben Sie einen Parser, der Ihr Programm in einen abstrakten Syntaxbaum (AST) verwandelt. Erfahren Sie mehr über das Parsen von Anweisungen mit einem rekursiven Abstiegsparser. Erfahren Sie, wie ärgerlich es ist, arithmetische Ausdrücke mit rekursiver Herkunft zu analysieren, und ein Top-Down-Operator-Prioritätsparser (Pratt-Parser) kann Ihr Leben erleichtern und Ihren Code verkürzen.

Schreiben Sie einen Dolmetscher, der Ihren AST bewertet. Es kann einfach jeden Knoten im Baum lesen und das tun, was er sagt: a = bwird new Assignment("a", "b")wird vars["a"] = vars["b"];. Wenn es Ihnen das Leben erleichtert, konvertieren Sie den AST vor der Evaluierung in eine einfachere Form.

Ich empfehle, das Einfachste zu entwerfen, das funktioniert und lesbar bleibt. Hier ist ein Beispiel, wie eine Sprache aussehen könnte. Ihr Design hängt notwendigerweise von Ihren spezifischen Anforderungen und Vorlieben ab.

ATK = D20
if ATK >= player.ATK
    DEF = D20
    if DEF < monster.DEF
        monster.HP -= ATK
        if monster.HP < 0
            monster.ALIVE = 0
        end
    end
end

Alternativ erfahren Sie, wie Sie eine vorhandene Skriptsprache wie Python oder Lua in Ihre Anwendung einbetten und verwenden. Der Nachteil der Verwendung einer Allzwecksprache für eine domänenspezifische Aufgabe besteht darin, dass die Abstraktion undicht ist: Alle Funktionen und Fallstricke der Sprache sind noch vorhanden. Der Vorteil ist, dass Sie es nicht selbst implementieren müssen - und das ist ein erheblicher Vorteil. Denke darüber nach.

Jon Purdy
quelle
2
Keine schlechte Herangehensweise, aber ich bin immer skeptisch gegenüber DSLs. Es ist eine Menge Arbeit, sie in Ordnung zu bringen (insbesondere, wenn es sich um ein echtes DSL mit benutzerdefinierter Syntax und allem handelt, im Gegensatz zu nur einer fließenden API, die die Leute haben Ich habe angefangen, "DSL" zu nennen. Sie sollten sich also sicher sein, dass Sie das Beste daraus machen. Wenn ja, dann lohnt es sich. Oft denke ich, dass Leute versuchen wollen, ein DSL zu nutzen, wo sie es nur für eine kleine Regel-Engine verwenden werden. Hier ist meine Faustregel: Wenn die DSL-Implementierung + -Verwendung weniger Code als kein DSL ist, machen Sie es, ich glaube nicht, dass dies in diesem Fall der Fall wäre.
Jimmy Hoffa
1
@ JimmyHoffa: Fair genug. Es liegt in meiner Natur, nach sprachbasierten Lösungen zu greifen, insbesondere nach Spielen. Ich unterschätze wahrscheinlich die Schwierigkeit, etwas klein und funktionell zu machen, weil ich es oft gemacht habe. Dennoch scheint es in diesem Fall eine angemessene Empfehlung zu sein.
Jon Purdy
Ich denke, ich sollte sagen, es ist der richtige Ansatz für diese Art von Problem, aber das setzt voraus, dass die Person, die die Implementierung durchführt, jemand mit ausreichenden Fähigkeiten ist. Zum Lernen sind DSLs großartig für Junioren, aber für ein aktuelles Release-Produkt möchte ich niemals jemanden unter Senioren sehen, der ein DSL schreibt, und für Junioren sollte ein DSL-lösbares Problem auf eine andere Art und Weise gelöst werden.
Jimmy Hoffa
Nachdem ich dies gelesen habe, denke ich, dass ich dafür einfach Lua-Skripte auswerten kann. Der Nachteil hier wäre, dass ein Benutzer in der Lage sein muss, Lua-Skripte zu schreiben. Mein persönliches Ziel ist es, eine Schnittstelle zu schreiben, die ohne Programmierkenntnisse verwendet werden kann, wie der Rule Builder in Magento (E-Commerce-App). Weil ich möchte, dass die Leute ihre eigenen Regeln hinzufügen können. Ich implementiere nichts Kommerzielles, sondern nur ein Tool, mit dem ich und meine Freunde die Regeln des RPG-Systems eingeben können, das wir auf unregelmäßiger Basis spielen. Nach einiger Zeit ist es ein Schmerz, zu den Regeln zurückzukehren und sie anzuwenden ...
burzum
1
@burzum: Wie wäre es, wenn Ihre Benutzeroberfläche die Lua-Skripte generiert?
TMN
3

Ich würde damit beginnen, die verschiedenen "Phasen" jeder Aktion zu bestimmen.

Zum Beispiel könnte eine Kampfphase beinhalten:

GetPlayerCombatStats();
GetEnemyCombatStats();
GetDiceRoll();
CalculateDamage();

Jede dieser Methoden hätte Zugriff auf einige ziemlich generische Objekte, wie z. B. Playerund Monster, und würde einige ziemlich generische Überprüfungen durchführen, die andere Entitäten zum Ändern der Werte verwenden können.

Beispiel GetPlayerCombatStats(): Ihre Methode enthält möglicherweise Folgendes:

GetPlayerCombatStats()
{
    Stats tempStats = player.BaseStats;

    player.GetCombatStats(player, monster, tempStats);

    foreach(var item in Player.EquippedItems)
        item.GetCombatStats(player, monster, tempStats);
}

Auf diese Weise können Sie auf einfache Weise Entitäten mit bestimmten Regeln hinzufügen, z. B. eine Spielerklasse, ein Monster oder ein Ausrüstungsgegenstand.

Nehmen wir als weiteres Beispiel an, Sie wollten ein Schwert , mit dem Sie alles außer Tintenfisch töten können. Dadurch erhalten Sie +4 gegen alles, es sei denn, das Ding hat Tentakel. In diesem Fall müssen Sie Ihr Schwert fallen lassen und im Kampf eine -10 erhalten.

Ihre Ausrüstungsklasse für dieses Schwert könnte GetCombatStatsungefähr so ​​aussehen:

GetCombatStats(Player player, Monster monster, Stats tmpStats)
{
    if (monster.Type == MonsterTypes.Tentacled)
    {
        player.Equipment.Drop(this);
        tmpStats.Attack -= 10;
    }
    else
    {
        tmpStats.Attack += 4;
    }
}

Auf diese Weise können Sie die Kampfwerte auf einfache Weise ändern, ohne den Rest der Kampflogik kennen zu müssen. Außerdem können Sie der Anwendung auf einfache Weise neue Elemente hinzufügen, da nur die Details und die Implementierungslogik Ihrer Ausrüstung (oder einer anderen Einheit) berücksichtigt werden müssen in der Entitätsklasse selbst vorhanden sein.

Entscheidend ist, an welchen Stellen sich Werte ändern können und welche Elemente diese Werte beeinflussen. Sobald Sie diese haben, sollte der Aufbau Ihrer einzelnen Komponenten einfach sein :)

Rachel
quelle
Das ist der richtige Weg. In den meisten Pen & Paper-RPGs gibt es Berechnungsphasen, die in der Regel in Handbüchern festgehalten sind. Sie können die Reihenfolge der Bühne auch in eine geordnete Liste aufnehmen, um sie allgemeiner zu gestalten.
Hakan Deryal
Das klingt für mich ziemlich statisch und erlaubt es einem Benutzer nicht, einfach die erforderlichen Regeln in eine Benutzeroberfläche einzugeben / zu erstellen. Was Sie beschreiben, klingt eher nach fest codierten Regeln. Dies sollte auch durch eine Liste verschachtelter Regeln möglich sein. Ich möchte keine Regeln fest programmieren. Wenn ich alles im Code machen könnte, müsste ich diese Frage nicht stellen, das wäre einfach. :)
Burzum
@burzum Ja, dies bedeutet, dass die Regeln durch den Code definiert werden, macht ihn jedoch gegenüber dem Code sehr erweiterbar. Wenn Sie beispielsweise einen neuen Klassentyp, ein neues Ausrüstungsstück oder einen neuen Monstertyp hinzufügen möchten, müssen Sie nur die Klassenobjekte für diese Entität erstellen und die entsprechenden Methoden in Ihrer Klasse mit Ihrer Logik ausfüllen.
Rachel
@burzum Ich habe gerade die Bearbeitung zu deiner Frage gelesen. Wenn Sie eine Regel - Engine nur für UI verwenden wollen, würde ich machte einen Pool von Unternehmen berücksichtigen ( Player, Monster, Dice, usw.) und die Einrichtung etwas, mit dem Benutzer per Drag / Drop - Einheit Stück in einen „Gleichung“ -Bereich ermöglicht, das Ausfüllen Parameter Geben Sie die Entität an (z. B. Ausfüllen player.base_attack) und geben Sie einfache Operatoren an, wie die Teile zusammenpassen. Ich habe tatsächlich etwas in meinem Blog gepostet, das eine mathematische Gleichung analysiert , die Sie möglicherweise verwenden können.
Rachel
@Rachel, was Sie beschreiben, ist kinderleicht. Es gibt sogar viele Beispiele in freier Wildbahn, die RPG-ähnliches Zeug als Beispiele für das Unterrichten von OOP verwenden. Diese Objekte zu haben und mit ihnen zu arbeiten, ist der einfache Teil, ich könnte sie im Handumdrehen auf der Grundlage von Daten aus der Datenbank erstellen. Das Problem bei Ihrem Ansatz sind die fest verankerten Regeln wie GetEnemyCombatStats (). Was auch immer diese Methode tun würde, muss irgendwo über eine Benutzeroberfläche definiert und in einer Datenbank gespeichert werden. Ihr Artikel scheint dem github.com/bobthecow/Ruler oder dem github.com/Trismegiste/PhpRules ähnlich zu sein .
Burzum
0

Ich würde mir MapTool genauer ansehen, nämlich Rumbles 4. Ed. Framework . Es ist das beste System, das ich gesehen habe, um das einzurichten, wovon Sie sprechen. Leider ist das Beste immer noch schrecklich crufty. Ihr "Makro" -System hat sich im Laufe der Zeit weiterentwickelt.

Was einen "Regel-Parser" angeht, würde ich einfach bei der Programmiersprache bleiben, mit der Sie vertraut sind, auch wenn das PHP ist. Es wird keinen guten Weg geben, alle Regeln Ihres Systems zu kodieren.

Wenn Sie möchten, dass Ihre Benutzer IHREN EIGENEN Regelsatz schreiben können, möchten Sie Ihre eigene Skriptsprache implementieren. Benutzer schreiben ihre eigenen Aktionen auf hoher Ebene, Ihre PHP interpretiert diese in etwas, das sich tatsächlich auf die Datenbankwerte auswirkt, und dann gibt es eine Reihe von Fehlern, da es sich um ein schrecklich cruftiges System handelt, das im Laufe der Jahre auf den neuesten Stand gebracht wurde. Wirklich, Jon Purdys Antwort ist direkt am Ball.

Philip
quelle
0

Ich denke, dass Sie in der Lage sein müssen, abstrakt darüber nachzudenken, was in Ihrem Problemraum vor sich geht, sich ein Modell auszudenken und dann Ihr DSL darauf aufzubauen.

Beispielsweise können Sie die Entitäten Entität, Aktion und Ereignis auf der obersten Ebene haben. Würfelwürfe sind Ereignisse, die als Ergebnis von Handlungen auftreten. Aktionen würden Bedingungen haben, die bestimmen, ob sie in einer bestimmten Situation verfügbar sind, und ein "Skript" von Dingen, die auftreten, wenn die Aktion ausgeführt wird. Komplexer wäre die Möglichkeit, eine Abfolge von Phasen zu definieren, in denen unterschiedliche Aktionen stattfinden können.

Sobald Sie eine Art konzeptionelles Modell haben (und ich schlage vor, Sie schreiben es auf und / oder zeichnen Diagramme, um es darzustellen), können Sie verschiedene Möglichkeiten der Implementierung untersuchen.

Eine Möglichkeit besteht darin, das so genannte externe DSL zu definieren, in dem Sie Ihre Syntax definieren und ein Tool wie antlr verwenden, um es zu analysieren und Ihre Logik aufzurufen. Eine andere Möglichkeit besteht darin, die in einer Programmiersprache vorhandenen Funktionen zu verwenden, um Ihr DSL zu definieren. Sprachen wie Groovy und Ruby sind in diesem Bereich besonders gut.

Eine Falle, die Sie vermeiden müssen, ist das Mischen Ihrer Anzeigelogik mit Ihrem implementierten Spielmodell. Ich würde dafür stimmen, dass das Display Ihr Modell liest und angemessen anzeigt, anstatt Ihren Display-Code mit Ihrem Modell zu vermischen.

Peter Kelley
quelle