RPG棋盘游戏规则的通用规则解析器-怎么做?


19

我想为笔和纸风格的RPG系统构建一个通用的规则解析器。规则通常可以包含1到N个实体,一个骰子的1个到N个角色,并基于一个实体的多个属性来计算值。

例如:

玩家拥有STR 18,他目前装备的武器使他获得+1 STR的加值,但获得了DEX -1的恶意。他攻击一个怪物实体,并且现在需要运行游戏逻辑来运行一组规则或动作:

玩家掷骰子,如果他获得例如8或更高(他需要传递的基本攻击值是其基本属性之一!),则攻击成功。然后,怪物掷骰子以计算攻击是否通过了它的装甲。如果是,则造成伤害,否则攻击将被阻止。

除了简单的数学规则外,还可能存在一些限制,例如仅适用于特定类别的用户(例如,战士还是向导)或任何其他属性。因此,这不仅限于数学运算。

如果您熟悉诸如Dungeon和Dragons之类的RPG系统,您会知道我在做什么。

我的问题是,现在我不知道如何以最佳方式准确地构建它。我希望人们能够设置任何种类的规则,之后再简单地执行一个操作,例如选择一个玩家和一个怪物,然后执行一个操作(一组规则,例如攻击)。

在数据库方面,我寻求的帮助较少,但更多地是关于如何提出结构和解析器的要求,以保持规则的灵活性。顺便说一下,为此选择的语言是php。

编辑我:

让我完善我的目标:我想创建一个用户友好的界面(不需要别人学习编程语言)来构建或多或少的复杂游戏规则。原因很简单:个人使用并不需要一直记住所有规则,我们只是没有那么频繁地玩,每次查看它们都是一个障碍。另外:做和学习东西看起来很有趣。:)

到目前为止,我已经尝试过:只考虑一个概念,而不是浪费时间构建错误的体系结构。到目前为止,我的想法是允许用户创建所需数量的属性,然后将所需数量的属性分配给任何类型的实体。实体可以是玩家,怪物,物品或其他任何东西。现在,当计算某些内容时,数据将可用于规则解析器,以便规则解析器应能够执行以下操作,例如Player.base_attack + dice(1x6)> Monster.armor_check然后Monster.health-1; 这里的问题是关于如何创建该解析器的。

编辑二:

这是一个非常基本的值的示例,但是要正确地计算它,需要考虑很多不同的事物和变量:

基本攻击加成(期限)您的基本攻击加成(通常由d20社区称为BAB)是从角色等级和等级获得的攻击掷骰加成。不同角色类别的基础攻击加成会以不同的速率增加。角色的基本攻击加值达到+6时,每回合会获得第二次攻击;基本攻击加成为+11或更高的角色获得三分之一;基本攻击加成为+16或更高的角色获得第四点。从不同类别获得的基础攻击加成,例如,针对多类别角色的叠加。角色的基本攻击加成在达到+16后不再给予任何更多攻击,不能小于+0,并且在角色等级达到20后不会由于职业等级而增加。某些专长需要最低基础攻击加成。

您可以在这里http://www.dandwiki.com/wiki/Base_Attack_Bonus_(Term)上阅读它,包括指向类和专长的链接,这些类和专长又具有自己的规则来计算基础攻击所需的值。

我开始认为,使其尽可能地通用将使完成良好的规则解析器变得相当困难。



2
实际上,我今天早上确实在考虑这类问题(与RPG不相关,但与规则处理引擎有关),并试图考虑非状态机方法进行规则处理,以及组合解析器如何有效地完成通常由以下人员完成的任务:状态机。我认为单子组合器有很大的可能性更干净地解决大多数状态机问题。这听起来像胡言乱语,但我想这个主意只有我的2美分。RPG系统是我喜欢编码的一个经典的有趣练习问题,也许我会尝试这种方法。
吉米·霍法

1
@jk。那篇文章让我想起了我喜欢命令行模式参数解析的一种模式,它使用Funcs 字典,根据参数将程序状态初始化为字典的键。感到惊讶的是,我之前从未发现过Yegge的帖子,非常酷,谢谢您指出。
吉米·霍法

4
我不太确定为什么将其归为“不是一个真正的问题”。它是关于如何构建具有一组特定要求(RPG规则系统)的应用程序的更高级别的“白板”问题。我已投票决定将其重新打开,但仍然需要其他4次重新打开投票才能重新打开。
雷切尔2012年

1
老实说,我认为此站点正是针对此类概念性问题的,而stackoverflow.com被认为是针对代码/实现问题的。
burzum 2012年

Answers:


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,而不是人们拥有的一些流畅的API开始调用“ DSL”),因此您最好确定要使用它,如果值得的话,这是值得的。通常,我认为人们想尝试DSL,而他们只会将其用于小型规则引擎。这是我的经验法则:如果DSL实现+使用的代码少于没有DSL的代码,那就去吧,在这种情况下,我认为并非如此。
吉米·霍法

1
@JimmyHoffa:足够公平。寻求基于语言的解决方案(尤其是游戏)只是我的本性。我可能低估了制作小型且实用的东西的难度,因为我已经做了很多次了。不过,在这种情况下,这似乎是一个适当的建议。
乔恩·普迪

我想我应该说这是解决此类问题的正确方法,但这要取决于执行该技术的人员是具有足够技能的人员。对于学习而言,DSL对于初级用户非常有用,但是对于实际的可发布产品,我永远都不想看到任何高级以下的人编写DSL,对于初级用户,应该以其他方式解决DSL可解决的问题。
吉米·霍法

看完这篇文章后,我想我可以简单地评估一下lua脚本。不利之处在于用户必须能够编写lua脚本。我的个人目标是编写一个无需编程知识即可使用的界面,例如magento(电子商务应用程序)中的规则构建器。因为我希望人们能够添加自己的规则。我没有实施任何商业手段,只是让我和我的朋友输入我们在不规则的基础上玩的RPG系统规则并重新使用该规则并在一段时间后应用它们的工具……一段时间后很痛苦……
burzum

1
@burzum:如何让您的界面生成lua脚本?
TMN 2012年

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);
}

这使您可以轻松添加具有特定规则的任何实体,例如玩家职业,怪物或装备。

再举一个例子,假设您想要一把杀死除鱿鱼以外的所有东西,它能使您对所有事物+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 2012年

@burzum是的,这意味着规则将由代码定义,但是这使其可以从代码中非常扩展。例如,如果要添加新的类类型,新的设备件或新的怪兽类型,则只需要为该实体构建类对象,并使用逻辑在类中填充适当的方法即可。
雷切尔

@burzum我刚刚阅读了您的问题的编辑内容。如果你想只为UI使用规则引擎,我会考虑的实体(池PlayerMonsterDice,等),以及一些允许用户拖/放实体件设置成一个“方程式”区域,填写参数实体(例如player.base_attack),并指定简单的运算符以将各部分组合在一起。实际上,我的博客上发布了一些内容,可以解析您可能使用的数学方程式
雷切尔2012年

@Rachel描述的是简单易用的OOP,甚至还有很多使用RPG之类的示例作为示例来教授OOP。拥有这些对象并与它们一起工作很容易,我可以根据数据库中的数据即时构建它们。您的方法中的问题是像GetEnemyCombatStats()这样的硬性规则,无论此方法需要通过UI的什么地方定义并存储在db中。您的文章似乎类似于github.com/bobthecow/Rulergithub.com/Trismegiste/PhpRules
burzum 2012年

0

我将专门研究maptool的Rumble的第4版框架。这是我所见过的设置您所讨论内容的最佳系统。不幸的是,最好的仍然是残酷的。他们的“宏观”系统已经……随着时间的流逝而发展。

至于“规则解析器”,即使是PHP,我也会坚持使用任何您喜欢的编程语言。对系统的所有规则进行编码不会有什么好的方法。

现在,如果您希望用户能够编写自己的规则集,那么您正在考虑实现自己的脚本语言。用户编写自己的高级操作脚本,您的php会将其解释为实际上会影响数据库值的内容,然后抛出很多错误,因为这是一个可怕的,笨拙的系统,多年来已被广泛使用。的确,乔恩·普迪(Jon Purdy)的答案是正确的。


0

我认为您需要能够抽象地思考问题空间中将要发生的事情,提出某种模型,然后基于此建立DSL。

例如,您可能在顶层具有实体Entity,Action和Event。掷骰将是由于采取行动而发生的事件。动作将具有条件,这些条件确定在给定情况下它们是否可用以及采取该动作时发生的事情的“脚本”。更复杂的事情是定义一系列可以发生不同动作的阶段的能力。

一旦有了某种概念模型(我建议您写下来和/或绘制图表来表示它),就可以开始研究实现它的不同方法。

一种方法是定义所谓的外部DSL,在其中定义语法并使用antlr之类的工具来解析它并调用逻辑。另一种方法是使用编程语言中提供的功能来定义DSL。Groovy和Ruby之类的语言在此领域特别出色。

您需要避免的一个陷阱是将显示逻辑与已实现的游戏模型混合在一起。我决定让显示读取您的模型并适当显示,而不是将显示代码与模型混合在一起。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.