RPGボードゲームルールの汎用ルールパーサー-実行方法


19

ペンとペーパースタイルのRPGシステム用の汎用ルールパーサーを構築したいと思います。ルールには通常、1〜Nエンティティの1〜Nのサイコロの役割と、エンティティの複数の属性に基づいた値の計算が含まれます。

例えば:

プレイヤーはSTR 18を持っています。彼の現在装備している武器は、STR +1のボーナスを与えますが、DEX -1のマルスを与えます。彼はモンスターエンティティを攻撃し、一連のルールまたはアクションを実行するためにゲームロジックが必要になりました。

プレイヤーがサイコロを振る場合、たとえば8以上を取得した場合(渡す必要がある基本攻撃値は基本属性の1つです!)、攻撃は成功します。その後、モンスターはサイコロを振り、攻撃がその鎧を通過するかどうかを計算します。「はい」の場合、攻撃がブロックされていない場合、損害が発生します。

単純な数学ルールに加えて、特定のクラスのユーザー(たとえば、戦士vsウィザード)または他の属性にのみ適用するなどの制約もあります。したがって、これは数学的な操作だけに限定されません。

DungeonやDragonsのようなRPGシステムに精通しているなら、私が何をしているのかわかるでしょう。

私の問題は、これを最善の方法で正確に構築する方法がわからないということです。あらゆる種類のルールを設定し、後でプレイヤーとモンスターを選択するなどのアクションを実行し、アクション(攻撃などのルールセット)を実行できるようにしたいのです。

私は物事のデータベース側での助けを求めていませんが、ルールを柔軟に保つために構造とパーサーをどのように思い付くかについてもっと求めています。ちなみにこれに適した言語はphpです。

編集I:

目標を洗練させましょう:複雑なゲームルールを構築するために、ユーザーフレンドリーなインターフェイス(誰かがプログラミング言語を習得する必要がない)を作成したいと思います。単純な理由:個人的な使用は常にすべてのルールを覚える必要がないため、単純にそれほど頻繁にプレイしないので、毎回ルールを調べるのは止めになります。また、何かをして学ぶための楽しいタスクのように見えます。:)

私がこれまでに試したこと:間違ったアーキテクチャを構築する時間を無駄にするのではなく、コンセプトについて考えるだけです。これまでのところ、ユーザーが必要な数の属性を作成し、任意の種類のエンティティに必要な数の属性を割り当てることができるようにするという考えがあります。エンティティは、プレイヤー、モンスター、アイテム、あらゆるものです。ルールパーサーが何かを計算するとき、Player.base_attack + dice(1x6)> Monster.armor_check then Monster.health-1; ここでの質問は、そのパーサーの作成方法に関するものです。

編集II:

非常に基本的な値の例を次に示しますが、適切に計算するには、考慮すべきさまざまなものや変数がたくさんあります。

基本攻撃ボーナス(期間)基本攻撃ボーナス(通常、d20コミュニティではBABと呼ばれます)は、キャラクタークラスとレベルから派生した攻撃ロールボーナスです。基本攻撃ボーナスは、キャラクタークラスごとに異なるレートで増加します。キャラクターは、基本攻撃ボーナスが+6に達するとラウンドごとに2番目の攻撃を獲得し、3番目は基本攻撃ボーナスが+11以上、4番目は基本攻撃ボーナスが+16以上になります。マルチクラスキャラクターのスタックなど、さまざまなクラスから得られる基本攻撃ボーナス。キャラクターの基本攻撃ボーナスは、+ 16に達した後は攻撃を許可せず、+ 0より小さくすることはできず、キャラクターレベルが20に達した後のクラスレベルにより増加しません。特定の特技には、最低限の基本攻撃ボーナスが必要です。

ここでそれを読むことができます。http: //www.dandwiki.com/wiki/Base_Attack_Bonus_(Term)は、基本攻撃に必要な値を計算するための独自のルールを持つクラスと特技へのリンクを含みます。

できるだけ汎用的にすると、良いルールパーサーを完成させるのがかなり難しくなると思い始めました。



2
今朝、まさにこのタイプの問題(RPGではなくルール処理エンジン)について実際に考え、ルール処理への非ステートマシンアプローチと、コンビナティブパーサーがどのようにタスクを完了するのに非常に効果的かを考えていましたステートマシン。モナド結合子は、ほとんどのステートマシンの問題にもっときれいにアプローチできる可能性があると思います。それは冗談のように聞こえるかもしれませんが、このアイデアには何かがあると思います。ちょうど2セントです。RPGシステムは、私がコーディングするのが好きな古典的な楽しい練習問題です。おそらくこのアプローチを試してみます。
ジミー・ホッファ

1
@jk。この記事は、コマンドラインプログラムの引数解析に気に入ったパターンを思い出させます。sの辞書を使用Funcして、引数に基づいてプログラムの状態を初期化し、辞書のキーとして使用します。驚いたことに、これまでYeggeからの投稿を見つけたことがありませんでした。
ジミー・ホッファ

4
これがなぜ「本当の質問ではない」として閉じられたのか、私にはよく分かりません。特定の要件セット(RPGルールシステム)を持つアプリケーションを設計する方法についての、より高いレベルの「ホワイトボード」の質問。私はそれを再開することを投票しましたが、再開するには他の4つの再開票が必要です。
レイチェル

1
正直に言って、このサイトはまさにこの種の概念的な質問のためのものであり、stackoverflow.comはコード/実装の問題のためのものだと思いました。
burzum

回答:


9

求めているのは、本質的にドメイン固有の言語です。これは、狭い目的のための小さなプログラミング言語であり、この場合はP&P RPGルールを定義しています。言語の設計は原則として難しくはありませんが、生産性を高めるためにはかなりの量の先行知識が必要です。残念ながら、このようなものに対する中心的な参考資料はありません。試行錯誤、そして多くの研究を通じてそれを拾わなければなりません。

最初に、他の操作を実装できるプリミティブ操作のセットを見つけます。例えば:

  • プレイヤー、NPC、またはモンスターのプロパティを取得または設定します

  • サイコロの結果を取得する

  • 算術式を評価する

  • 条件式を評価する

  • 条件分岐を実行する

プリミティブを表現する構文を設計します。数字をどのように表現しますか?ステートメントはどのように見えますか?ステートメントはセミコロンで終了していますか?改行で終了しましたか?ブロック構造はありますか?記号またはインデントを使用して、どのように指定しますか?変数はありますか?有効な変数名とは何ですか?変数は変更可能ですか?オブジェクトのプロパティにどのようにアクセスしますか?オブジェクトはファーストクラスですか?自分で作成できますか?

プログラムを抽象構文ツリー(AST)に変換するパーサーを作成します。再帰降下パーサーを使用したステートメントの解析について学習します。再帰下降を使用した算術式の解析がどのように面倒であるか、トップダウン演算子優先パーサー(Prattパーサー)を使用すると作業が簡単になり、コードが短くなることを学習してください。

ASTを評価するインタープリターを作成します。これは単に、ツリー内の各ノードを読んで、それを言って何ができる:a = bとなりのnew Assignment("a", "b")となりvars["a"] = vars["b"];。生活が楽になる場合は、評価の前にASTをよりシンプルな形式に変換してください。

動作し、読みやすくなる最も単純なものを設計することをお勧めします。以下は、言語がどのように見えるかの例です。設計は、特定のニーズや好みに基づいて必然的に異なります。

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

あるいは、PythonやLuaなどの既存のスクリプト言語をアプリケーションに埋め込み、それを使用する方法を学びます。ドメイン固有のタスクに汎用言語を使用することの欠点は、抽象化が漏れやすいことです。言語のすべての機能と落とし穴がまだ存在しています。利点は、自分で実装する必要がないことです。これは大きな利点です。考慮して下さい。


2
悪いアプローチではありませんが、私は一貫してDSLに懐疑的です、彼らは正しいことをするために多くの仕事をしています「DSL」の呼び出しを開始したので、その価値を十分に発揮してください。多くの場合、人々は、小さなルールエンジンにのみ使用するDSLを試みたいと思います。私の経験則は次のとおりです。DSLの実装+使用法がDSLなしよりもコードが少ない場合は、それを実行します。この場合、そうなるとは思いません。
ジミー・ホッファ

1
@JimmyHoffa:結構です。言語ベースのソリューション、特にゲームに手を伸ばすのは私の性質です。何回もやったことがあるので、何かを小さく機能的にすることの難しさをおそらく過小評価しています。それでも、この場合は適切な推奨事項のようです。
ジョンパーディ

私はそれがこの種の問題に対する正しいアプローチであると言うべきだと思いますが、それは実装を行う人が十分なスキルを持つ人であることを前提としています。学習に関しては、DSLは後輩にとっては素晴らしいものですが、実際のリリース可能な製品については、上級レベル以下の誰かがDSLを書いているのを見たくはありません。後輩にとっては、DSLの解決可能な問題は別の方法で解決されるべきです。
ジミー・ホッファ

これを読んだ後、私は単純にそのためのluaスクリプトを評価できると思います。ここでの欠点は、ユーザーがluaスクリプトを作成できる必要があることです。私の個人的な目標は、magento(eコマースアプリ)のルールビルダーのように、プログラミングの知識がなくても使用できるインターフェイスを作成することです。人々が自分のルールを追加できるようにしたいからです。私は商用のものを実装しておらず、不規則なベースでプレイするRPGシステムのルールを自分と友人に入力させ、ルールに戻って適用する
ツール

1
@burzum:インターフェイスにluaスクリプトを生成させることはどうですか?
TMN

3

各アクションの異なる「フェーズ」を決定することから始めます。

たとえば、戦闘フェーズには以下が含まれます。

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

これらの各メソッドは、Playerやなどのかなり一般的なオブジェクトにアクセスできます。Monster、他のエンティティが値を変更するために使用できるかなり一般的なチェックを実行します。

たとえば、GetPlayerCombatStats()メソッドに次のようなものが含まれている場合があります。

GetPlayerCombatStats()
{
    Stats tempStats = player.BaseStats;

    player.GetCombatStats(player, monster, tempStats);

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

これにより、プレイヤークラス、モンスター、装備品などの特定のルールを持つエンティティを簡単に追加できます。

別の例として、Squid以外のすべてを殺す剣が欲しいと仮定します。これは、触手がない限り、すべてに対して+4を与えます。その場合、剣を落とし、戦闘で-10を取得する必要があります。

この剣の装備クラスには、GetCombatStats次のようなものがあります。

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

これにより、戦闘ロジックの残りの部分を知る必要なく、戦闘値を簡単に変更できます。また、機器(またはエンティティ)の詳細と実装ロジックのみであるため、アプリケーションに新しい要素を簡単に追加できます。エンティティクラス自体に存在する必要があります。

理解すべき重要なことは、値が変化する可能性のあるポイントと、それらの値に影響を与える要素です。これらを入手したら、個々のコンポーネントを簡単に構築できます。


それが道です。ほとんどのPen&Paper RPGには、計算の段階があり、一般にガイドブックに書かれています。ステージの順序を順序付きリストに入れて、ステージをより一般的にすることもできます。
ハカンデリアル

これはかなり静的に聞こえますが、ユーザーがUIで必要なルールを単純に入力/作成することはできませんか?あなたが説明するものは、ハードコーディングされたルールのように聞こえます。これは、ネストされたルールのリストを作成することでも実行できるはずです。ルールをハードコードしたくありません。コードですべてを行うことができれば、その質問をする必要はありません。それは簡単です。:)
burzum

@burzumはい、これはルールがコードによって定義されることを意味しますが、コードから非常に拡張可能になります。たとえば、新しいクラスタイプ、新しい機器ピース、または新しいモンスタータイプを追加する場合は、そのエンティティのクラスオブジェクトを作成し、クラスの適切なメソッドにロジックを入力するだけです。
レイチェル

@burzum私はあなたの質問の編集を読みました。あなたが唯一のUIを使用するためのルールエンジンをしたい場合は、私は、エンティティのプールを作る(検討するPlayerMonsterDice、など)、およびのパラメータを埋め、「式」領域にドラッグ/ドロップエンティティ片にユーザを可能にする何かを設定しますエンティティ(を入力するなどplayer.base_attack)、およびピースがどのように適合するかについて単純な演算子を指定します。私は実際にブログに投稿して、あなたが使用できる数学の方程式解析しています。
レイチェル

@Rachelの説明は非常に簡単なOOPであり、RPGのようなものをOOPを教える例として使用している多くの例さえあります。これらのオブジェクトを用意してそれらを操作するのは簡単な部分です。データベースのデータに基づいてその場でそれらを構築できます。あなたのアプローチの問題は、このメソッドがUIを介してどこかで定義され、dbに保存される必要がある場合、GetEnemyCombatStats()のようなハードコードされたルールです。あなたの記事は、そのgithub.com/bobthecow/Rulerまたはgithub.com/Trismegiste/PhpRulesに似ているようです
burzum

0

私は、特にランブルの第4版フレームワークであるmaptoolを見ていきます。それはあなたが話していることを設定するために私が見た中で最高のシステムです。残念ながら、最高はまだ恐ろしく無愛想です。彼らの「マクロ」システムは...と言う...時間とともに進化しました。

「ルールパーサー」に関しては、たとえそれがPHPであっても、あなたが使い慣れているプログラミング言語に固執するだけです。システムのすべてのルールをエンコードするのに良い方法はありません。

ユーザーが独自のルールセットを記述できるようにしたい場合は、独自のスクリプト言語の実装を検討しています。ユーザーは独自の高レベルのアクションをスクリプト化し、PHPはそれをデータベースの値に実際に影響する何かに解釈します。そして、それは何年にもわたって適所に設置されてきた恐ろしくcrなシステムであるため、大量のエラーをスローします。Jon Purdyの答えはまさにその通りです。


0

問題空間で何が起こるのかを抽象的に考え、ある種のモデルを考え出し、それに基づいてDSLを作成できる必要があると思います。

たとえば、エンティティエンティティ、アクション、およびイベントを最上位に持つことができます。サイコロは、アクションの結果として発生するイベントです。アクションには、特定の状況で利用可能かどうかを決定する条件と、アクションが実行されたときに発生するものの「スクリプト」があります。より複雑なことは、さまざまなアクションが発生する可能性のある一連のフェーズを定義できることです。

ある種の概念モデルを作成したら(そして、それを書き留めたり、図を描いてそれを表現することをお勧めします)、それを実装するさまざまな手段を検討し始めることができます。

1つの方法は、外部DSLと呼ばれるものを定義し、構文を定義し、antlrなどのツールを使用して構文を解析し、ロジックを呼び出すことです。もう1つの方法は、プログラミング言語に存在する機能を使用してDSLを定義することです。この分野では、GroovyやRubyなどの言語が特に適しています。

回避する必要があるトラップの1つは、表示ロジックを実装済みのゲームモデルと混合することです。ディスプレイにモデルを混在させて表示するのではなく、ディスプレイにモデルを読み取って適切に表示するように投票します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.