Analisador de regras genérico para regras de jogos de tabuleiro RPG - como fazer isso?


19

Quero criar um analisador de regras genérico para sistemas RPG com estilo de caneta e papel. Uma regra pode envolver normalmente 1 a N entidades 1 a N funções de um dado e calcular valores com base em vários atributos de uma entidade.

Por exemplo:

O jogador tem STR 18, sua arma atualmente equipada lhe dá um bônus de +1 STR, mas um malus de DEX -1. Ele ataca uma entidade monstro e a lógica do jogo agora é necessária para executar um conjunto de regras ou ações:

O jogador lança os dados, se receber, por exemplo, 8 ou mais (o valor de ataque base que ele precisa passar é um dos seus atributos básicos!), Seu ataque é bem-sucedido. O monstro então lança os dados para calcular se o ataque passa por sua armadura. Se sim, o dano é recebido, se não o ataque foi bloqueado.

Além das simples regras matemáticas, também pode haver restrições, como aplicar apenas a uma determinada classe de usuário (guerreiro vs assistente, por exemplo) ou qualquer outro atributo. Portanto, isso não se limita apenas às operações matemáticas.

Se você estiver familiarizado com sistemas de RPG como Dungeon e Dragons, saberá o que estou fazendo.

Meu problema agora é que não tenho idéia de como criar exatamente isso da melhor maneira possível. Quero que as pessoas sejam capazes de estabelecer qualquer tipo de regra e, mais tarde, simplesmente executar uma ação como selecionar um jogador e um monstro e executar uma ação (conjunto de regras como um ataque).

Estou pedindo menos ajuda com o lado do banco de dados, mas mais sobre como criar uma estrutura e um analisador para manter minhas regras flexíveis. A linguagem de escolha para isso é php por sinal.

Editar I:

Deixe-me refinar meu objetivo: quero criar uma interface amigável (que não exija que alguém aprenda uma linguagem de programação) para criar regras de jogo mais ou menos complexas. O motivo simples: uso pessoal para não precisar lembrar de todas as regras o tempo todo, simplesmente não jogamos com tanta frequência e é uma parada para procurá-las sempre. Além disso: Parece uma tarefa divertida de fazer e aprender alguma coisa. :)

O que eu tentei até agora: apenas pensando em um conceito, em vez de perder tempo construindo uma arquitetura errada. Até agora, tenho a ideia de permitir que um usuário crie quantos atributos quiser e, em seguida, atribua quantos atributos quiser a qualquer tipo de entidade. Uma entidade pode ser um jogador, um monstro, um item, qualquer coisa. Agora, ao calcular algo, os dados são disponibilizados ao analisador de regras, para que ele possa fazer coisas como se Player.base_attack + dice (1x6)> Monster.armor_check e Monster.health - 1; A questão aqui é sobre como criar esse analisador.

Edição II:

Aqui está um exemplo de valor bastante básico, mas para calculá-lo corretamente, existem muitas coisas e variáveis ​​diferentes a serem consideradas:

Bônus de Ataque Base (Termo) Seu bônus de ataque base (normalmente chamado de BAB pela comunidade d20) é um bônus de rolagem de ataque derivado da classe e nível do personagem. Os bônus de ataque base aumentam em taxas diferentes para diferentes classes de personagens. Um personagem ganha um segundo ataque por rodada quando seu bônus de ataque básico atingir +6, um terceiro com bônus de ataque básico de +11 ou superior e um quarto com bônus de ataque básico de +16 ou superior. Bônus de ataque base ganhos em diferentes classes, como para um personagem multiclasse, pilha. O bônus de ataque básico de um personagem não concede mais ataques após atingir +16, não pode ser inferior a +0 e não aumenta devido aos níveis de classe após o nível de personagem atingir 20. Um bônus mínimo de ataque básico é necessário para certos feitos.

Você pode lê-lo aqui http://www.dandwiki.com/wiki/Base_Attack_Bonus_(Term), incluindo os links para classes e talentos que têm novamente suas próprias regras para calcular os valores necessários para o ataque base.

Comecei a pensar que mantê-lo o mais genérico possível também tornará muito difícil a realização de um bom analisador de regras.



2
Na verdade, eu estava pensando exatamente sobre esse tipo de problema hoje de manhã (não relacionado ao RPG, mas com mecanismos de processamento de regras) e tentando pensar em abordagens de máquinas não estatais para o processamento de regras e em como os analisadores combinatórios são tão eficazes para concluir uma tarefa geralmente executada por máquinas de estado. Penso que existe uma rica possibilidade de combinadores monádicos abordarem a maioria dos problemas das máquinas de estado de maneira mais limpa. Isso pode parecer bobagem, mas acho que há algo nessa ideia, apenas meus 2 centavos. Os sistemas de RPG são um problema clássico de prática divertida que eu gosto de codificar, talvez eu tente essa abordagem.
Jimmy Hoffa

1
@jk. esse artigo me lembra um padrão que eu gostei para a análise de argumentos do programa de linha de comando, usando um dicionário Funcs que inicializa o estado do programa com base nos argumentos como chaves do dicionário. Surpreso por nunca ter encontrado o post de Yegge antes, muito legal, obrigado por apontá-lo.
Jimmy Hoffa

4
Não sei ao certo por que isso foi encerrado como "não é uma pergunta real". É uma questão de "quadro branco" de nível superior sobre como arquitetar um aplicativo que possui um conjunto específico de requisitos (sistema de regras de RPG). Votei em reabri-lo, mas ainda serão necessários outros 4 votos para reabri-lo.
22412 Rachel

1
Honestamente, pensei que este site é exatamente para esse tipo de perguntas conceituais, enquanto o stackoverflow.com é considerado por problemas de código / implementação.
22712 burzum

Respostas:


9

O que você está pedindo é essencialmente uma linguagem específica de domínio - uma pequena linguagem de programação para um propósito restrito, neste caso, definindo as regras de R&P da P&P. Projetar um idioma não é, em princípio, difícil, mas há uma quantidade considerável de conhecimento inicial que você precisa adquirir para ser produtivo. Infelizmente, não existe uma referência central para esse material - você precisa buscá-lo por tentativa, erro e muita pesquisa.

Primeiro, encontre um conjunto de operações primitivas pelas quais outras operações possam ser implementadas. Por exemplo:

  • Obter ou definir uma propriedade do jogador, um NPC ou um monstro

  • Obtenha o resultado de um rolo de matriz

  • Avaliar expressões aritméticas

  • Avaliar expressões condicionais

  • Executar ramificação condicional

Crie uma sintaxe que expresse suas primitivas. Como você representará números? Como é uma declaração? As declarações são terminadas em ponto e vírgula? Nova linha finalizada? Existe estrutura de blocos? Como você o indicará: através de símbolos ou recuo? Existem variáveis? O que constitui um nome de variável legal? As variáveis ​​são mutáveis? Como você acessará as propriedades dos objetos? Os objetos são de primeira classe? Você pode criá-los você mesmo?

Escreva um analisador que transforma seu programa em uma árvore de sintaxe abstrata (AST). Aprenda sobre a análise de instruções com um analisador de descida recursiva. Aprenda sobre como a análise de expressões aritméticas com descida recursiva é irritante, e um analisador de precedência de operador de cima para baixo (analisador Pratt) pode facilitar sua vida e diminuir seu código.

Escreva um intérprete que avalie seu AST. Ele pode simplesmente ler cada nó na árvore e fazer o que ele diz: a = btorna-se new Assignment("a", "b")torna-sevars["a"] = vars["b"]; . Se facilitar a sua vida, converta o AST em uma forma mais simples antes da avaliação.

Eu recomendo projetar a coisa mais simples que funcionará e permanecerá legível. Aqui está um exemplo de como um idioma pode ser. Seu design será necessariamente diferente com base em suas necessidades e preferências específicas.

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

Como alternativa, aprenda como incorporar uma linguagem de script existente, como Python ou Lua, ao seu aplicativo e use-a. A desvantagem de usar uma linguagem de uso geral para uma tarefa específica de domínio é que a abstração está com vazamento: todos os recursos e truques da linguagem ainda estão presentes. O lado positivo é que você não precisa implementá-lo sozinho - e isso é um ponto significativo. Considere isso.


2
Não é uma abordagem ruim, mas sou sempre cético em relação às DSLs, elas dão muito trabalho para acertar (especialmente se você está falando de uma DSL verdadeira com sintaxe personalizada e tudo mais, em vez de apenas uma API fluente que as pessoas têm começou a chamar "DSL" (s) DSL), então é melhor você ter certeza de que vai usar isso, se você vale a pena. Muitas vezes, acho que as pessoas querem tentar uma DSL em que só a usarão para um pequeno mecanismo de regras. Aqui está minha regra de ouro: se a implementação DSL + o uso for menos código do que nenhum DSL, vá em frente, acho que não seria assim neste caso.
21812 Jimmy Hoffa

1
@ JimmyHoffa: Justo. É da minha natureza buscar soluções baseadas em idiomas, especialmente para jogos. Provavelmente subestimo a dificuldade de fazer algo pequeno e funcional porque já o fiz muitas vezes. Ainda assim, parece uma recomendação apropriada neste caso.
22412 Jon Purdy

Acho que devo dizer que é a abordagem correta para esse tipo de problema, mas isso se deve à pessoa que está executando a implementação sendo alguém com habilidade suficiente. Para aprender, as DSLs são ótimas para juniores, mas para produtos liberáveis ​​reais, eu nunca gostaria de ver alguém abaixo do nível sênior escrevendo uma DSL, e para uma junior um problema solucionável da DSL deve ser resolvido de uma maneira diferente.
21712 Jimmy Hoffa

Depois de ler isso, acho que poderia simplesmente avaliar os scripts lua para isso. A desvantagem aqui seria que um usuário precisa ser capaz de escrever scripts lua. Meu objetivo pessoal é escrever uma interface que possa ser usada sem conhecimento de programação, como o criador de regras no magento (aplicativo de comércio eletrônico). Porque eu quero que as pessoas possam adicionar suas próprias regras. Eu não estou implementando nada comercial, apenas uma ferramenta para deixar eu e meus amigos entrar nas regras do sistema de RPG que jogamos em uma base irregular e voltar às regras e aplicá-las é uma dor depois de um tempo ...
burzum

1
@burzum: Que tal ter sua interface gerar os scripts lua?
TMN

3

Eu começaria determinando as diferentes "fases" de cada ação.

Por exemplo, uma fase de combate pode envolver:

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

Cada um desses métodos teria acesso a alguns objetos bastante genéricos, como the Playere the Monster, e executaria algumas verificações bastante genéricas que outras entidades podem usar para modificar os valores.

Por exemplo, você pode ter algo parecido com isso incluído no seu GetPlayerCombatStats()método:

GetPlayerCombatStats()
{
    Stats tempStats = player.BaseStats;

    player.GetCombatStats(player, monster, tempStats);

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

Isso permite que você adicione facilmente qualquer entidade com regras específicas, como uma classe de jogador, monstro ou peça de equipamento.

Como outro exemplo, suponha que você queira uma Espada de Matar Tudo, exceto a Lula , o que lhe dá +4 contra tudo, a menos que essa coisa tenha tentáculos; nesse caso, você deve soltar a espada e obter -10 no combate.

Sua classe de equipamento para esta espada pode ter GetCombatStatsalgo parecido com isto:

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

Isso permite que você modifique facilmente os valores de combate sem precisar conhecer o restante da lógica de combate, e permite adicionar facilmente novas peças ao aplicativo, porque apenas os detalhes e a lógica de implementação do seu equipamento (ou qualquer entidade que seja) apenas precisa existir na própria classe de entidade.

O principal a descobrir é em que pontos os valores podem mudar e quais elementos influenciam esses valores. Depois de ter esses, a construção de seus componentes individuais deve ser fácil :)


Esse é o caminho a percorrer. Na maioria dos RPGs de caneta e papel, existem fases nos cálculos, geralmente escritas em guias. Você também pode colocar a ordem do estágio em uma lista ordenada para torná-lo mais genérico.
Hakan Deryal 22/10/12

Isso me parece bastante estático e não permite que um usuário simplesmente insira / construa as regras necessárias em uma interface do usuário? O que você descreve soa mais como regras codificadas. Isso também deve ser possível tendo uma lista de regras aninhadas. Eu não quero codificar nenhuma regra. Se eu pudesse fazer tudo no código, não precisaria fazer essa pergunta, isso seria fácil. :)
burzum

@burzum Sim, isso significa que as regras seriam definidas pelo código, no entanto, tornam-no muito extensível a partir do código. Por exemplo, se você deseja adicionar um novo tipo de classe, uma nova peça de equipamento ou um novo tipo de monstro, basta criar os objetos de classe para essa entidade e preencher os métodos apropriados em sua classe com sua lógica.
Rachel

@burzum Acabei de ler a edição da sua pergunta. Se você quer um motor de regras apenas para uso UI, eu consideraria fazer um pool de entidades ( Player, Monster, Dice, etc), ea criação de algo que permite aos usuários peças entidade arrastar / soltar em uma área "equação", preenchendo os parâmetros de a entidade (como preenchimento player.base_attack) e especifique operadores simples sobre como as peças se encaixam. Na verdade, tenho algo publicado no meu blog que analisa uma equação matemática que você pode usar.
Rachel

@ Rachel, o que você descreve é ​​OOP fácil, existem muitos exemplos que usam RPG como material para ensinar OOP. Tendo esses objetos e trabalhando com eles é a parte mais fácil, eu poderia construí-los rapidamente, com base nos dados do banco de dados. O problema em sua abordagem são as regras rígidas, como GetEnemyCombatStats (), o que quer que esse método faça precise ser definido em algum lugar por meio de uma interface do usuário e armazenado em um banco de dados. Seu artigo parece ser semelhante ao github.com/bobthecow/Ruler ou ao github.com/Trismegiste/PhpRules .
burzum

0

Eu daria uma olhada no maptool especificamente na estrutura do 4º ed de Rumble . É o melhor sistema que eu já vi para configurar o que você está falando. Infelizmente, o melhor ainda é terrivelmente sujo. O sistema "macro" deles ... digamos ... evoluiu com o tempo.

Quanto a um "analisador de regras", eu continuaria com qualquer linguagem de programação com a qual você se sinta confortável, mesmo que seja PHP. Não haverá nenhuma maneira de codificar todas as regras do seu sistema.

Agora, se você deseja que seus usuários possam escrever SEU PRÓPRIO conjunto de regras, está pensando em implementar sua própria linguagem de script. Os usuários criam suas próprias ações de alto nível, seu php interpreta isso em algo que realmente afeta os valores do banco de dados e, em seguida, gera vários erros, porque é um sistema horrivelmente sujo que foi colocado no lugar ao longo dos anos. Realmente, a resposta de Jon Purdy está certa.


0

Eu acho que você precisa ser capaz de pensar abstratamente sobre o que as coisas terão no seu espaço problemático, criar algum tipo de modelo e depois basear sua DSL nisso.

Por exemplo, você pode ter a entidade entidade, ação e evento no nível superior. Os rolos de matriz seriam eventos que ocorrem como resultado de ações. As ações teriam condições que determinariam se elas estão disponíveis em uma determinada situação e um "script" das coisas que ocorrem quando a ação é executada. Coisas mais complexas seriam a capacidade de definir uma sequência de fases em que diferentes ações possam ocorrer.

Depois de ter algum tipo de modelo conceitual (e eu sugiro que você o anote e / ou desenhe diagramas para representá-lo), você pode começar a procurar diferentes meios de implementá-lo.

Uma rota é definir o que é chamado de DSL externo, onde você define sua sintaxe e usa uma ferramenta como antlr para analisá-la e chamar sua lógica. Outra rota é usar os recursos presentes em uma linguagem de programação para definir sua DSL. Idiomas como Groovy e Ruby são particularmente bons nesse espaço.

Uma armadilha que você precisa evitar é misturar sua lógica de exibição com o modelo de jogo implementado. Eu votaria para que a tela lesse seu modelo e a exibisse adequadamente, em vez de misturar seu código de exibição misturado ao seu modelo.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.