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.
fonte
Func
s 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.Respostas:
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 = b
torna-senew 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.
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.
fonte
Eu começaria determinando as diferentes "fases" de cada ação.
Por exemplo, uma fase de combate pode envolver:
Cada um desses métodos teria acesso a alguns objetos bastante genéricos, como the
Player
e theMonster
, 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: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
GetCombatStats
algo parecido com isto: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 :)
fonte
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 preenchimentoplayer.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.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.
fonte
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.
fonte