Parser di regole generico per le regole del gioco da tavolo RPG - come si fa?


19

Voglio creare un parser di regole generico per sistemi RPG in stile penna e carta. Una regola può coinvolgere generalmente da 1 a N entità da 1 a N ruoli di un dado e il calcolo di valori basati su più attributi di un'entità.

Per esempio:

Il giocatore ha STR 18, la sua arma attualmente equipaggiata gli dà un bonus di +1 STR ma un malus di DEX -1. Attacca un'entità mostruosa e ora è necessaria la logica del gioco per eseguire una serie di regole o azioni:

Il giocatore lancia i dadi, se ottiene per esempio 8 o più (il valore di attacco base che deve passare è uno dei suoi attributi di base!) Il suo attacco ha successo. Il mostro quindi lancia i dadi per calcolare se l'attacco passa attraverso la sua armatura. Se sì, il danno viene preso se non l'attacco è stato bloccato.

Oltre alle semplici regole matematiche possono anche avere vincoli come l'applicazione solo a una determinata classe di utenti (ad esempio guerriero vs mago) o qualsiasi altro attributo. Quindi questo non si limita solo alle operazioni matematiche.

Se hai familiarità con i sistemi di gioco di ruolo come Dungeon e Dragons saprai cosa sto facendo.

Il mio problema è ora che non ho idea di come costruirlo esattamente nel modo migliore. Voglio che le persone siano in grado di impostare qualsiasi tipo di regola e successivamente semplicemente fare un'azione come selezionare un giocatore e un mostro ed eseguire un'azione (insieme di regole come un attacco).

Chiedo meno aiuto per quanto riguarda il database, ma di più su come elaborare una struttura e un parser affinché mantenga flessibili le mie regole. A proposito, la lingua preferita è php.

Modifica I:

Consentitemi di perfezionare il mio obiettivo: voglio creare un'interfaccia intuitiva (che non richiede a nessuno di imparare un linguaggio di programmazione) per costruire regole di gioco più o meno complesse. Il semplice motivo: l'uso personale non ha bisogno di ricordare tutte le regole in ogni momento, semplicemente non ci giochiamo spesso ed è un tappo cercarle ogni volta. Inoltre: sembra un compito divertente da fare e imparare qualcosa. :)

Quello che ho provato finora: pensare a un concetto invece di perdere tempo a costruire un'architettura sbagliata. Finora ho l'idea di consentire a un utente di creare tutti gli attributi che vogliono e quindi assegnare tutti gli attributi che vogliono a qualsiasi tipo di entità. Un'entità può essere un giocatore, un mostro, un oggetto, qualsiasi cosa. Ora, quando si calcola qualcosa, i dati vengono resi disponibili al parser delle regole in modo che il parser delle regole sia in grado di fare cose come Player.base_attack + dice (1x6)> Monster.armor_check quindi Monster.health - 1; La domanda qui è su come creare quel parser.

Modifica II:

Ecco un esempio di valore piuttosto semplice ma per calcolarlo correttamente ci sono molte cose e variabili diverse da tenere in considerazione:

Bonus attacco base (termine) Il bonus attacco base (comunemente definito BAB dalla comunità d20) è un bonus al tiro di attacco derivato dalla classe e dal livello del personaggio. I bonus di attacco base aumentano a velocità diverse per diverse classi di personaggi. Un personaggio ottiene un secondo attacco per round quando il suo bonus di attacco di base raggiunge +6, un terzo con un bonus di attacco di base di +11 o superiore, e un quarto con un bonus di attacco di base di +16 o superiore. Bonus di attacco base ottenuti da diverse classi, ad esempio per un personaggio multiclasse, pila. Il bonus di attacco base di un personaggio non concede più attacchi dopo aver raggiunto +16, non può essere inferiore a +0 e non aumenta a causa dei livelli di classe dopo che il livello di personaggio ha raggiunto il 20 °. Per alcuni talenti è richiesto un bonus di attacco base minimo.

Puoi leggerlo qui http://www.dandwiki.com/wiki/Base_Attack_Bonus_(Term) compresi i collegamenti a classi e talenti che hanno di nuovo le proprie regole per calcolare i valori richiesti per l'attacco di base.

Ho iniziato a pensare che mantenerlo il più generico possibile renderà anche abbastanza difficile ottenere un buon parser di regole.



2
In realtà stavo pensando esattamente a questo tipo di problema stamattina (non correlato al gioco di ruolo, ma ai motori di elaborazione delle regole) e provando a pensare agli approcci della macchina non statale all'elaborazione delle regole e a come i parser combinatori sono così efficaci nel completare un compito normalmente svolto da macchine a stati. Penso che ci sia una ricca possibilità per i combinatori monadici di affrontare la maggior parte dei problemi delle macchine a stati in modo più chiaro. Può sembrare incomprensibile, ma penso che ci sia qualcosa in questa idea, solo i miei 2 centesimi. I sistemi di gioco di ruolo sono un classico problema di pratica divertente che mi piace programmare, forse proverò questo approccio.
Jimmy Hoffa,

1
@jk. quell'articolo mi ricorda uno schema che mi è piaciuto per l'analisi dell'argomento del programma da riga di comando, usando un dizionario di Funcs che inizializza lo stato del programma basato sugli argomenti come chiavi del dizionario. Sorpreso non ho mai trovato quel post di Yegge prima, molto bello, grazie per averlo sottolineato.
Jimmy Hoffa,

4
Non sono davvero sicuro del perché questa sia stata chiusa come "non una vera domanda". Si tratta di una domanda "lavagna" di livello superiore su come progettare un'applicazione che abbia una serie specifica di requisiti (sistema di regole del gioco di ruolo). Ho votato per riaprirlo, ma per riaprire saranno ancora necessari altri 4 voti.
Rachel,

1
Onestamente, ho pensato che questo sito fosse esattamente per questo tipo di domande concettuali mentre stackoverflow.com è pensato per problemi di codice / implementazione.
burzum,

Risposte:


9

Quello che stai chiedendo è essenzialmente un linguaggio specifico del dominio, un piccolo linguaggio di programmazione per uno scopo ristretto, in questo caso che definisce le regole del gioco di ruolo P&P. Progettare una lingua non è in linea di principio difficile, ma c'è una notevole quantità di conoscenze iniziali che devi acquisire per essere assolutamente produttivo. Sfortunatamente, non esiste un riferimento centrale per queste cose: devi prenderle attraverso prove, errori e molte ricerche.

Innanzitutto, trova una serie di operazioni primitive in base alle quali è possibile implementare altre operazioni. Per esempio:

  • Ottieni o imposta una proprietà del giocatore, un NPC o un mostro

  • Ottieni il risultato di un tiro di dado

  • Valuta le espressioni aritmetiche

  • Valuta le espressioni condizionali

  • Esegue la ramificazione condizionale

Progetta una sintassi che esprima i tuoi primitivi. Come rappresenterai i numeri? Che aspetto ha una dichiarazione? Le dichiarazioni sono punto e virgola terminate? Newline-terminato? C'è una struttura a blocchi? Come lo indicherai: attraverso simboli o rientri? Ci sono variabili? Cosa costituisce un nome di variabile legale? Le variabili sono mutabili? Come accederai alle proprietà degli oggetti? Gli oggetti sono di prima classe? Puoi crearli tu stesso?

Scrivi un parser che trasforma il tuo programma in un albero di sintassi astratto (AST). Informazioni sull'analisi delle istruzioni con un parser di discesa ricorsivo. Scopri come l'analisi delle espressioni aritmetiche con la discesa ricorsiva è fastidiosa e un parser di precedenza operatore top-down (parser Pratt) può semplificarti la vita e ridurre il codice.

Scrivi un interprete che valuti il ​​tuo AST. Può semplicemente leggere ogni nodo dell'albero e fare ciò che dice: a = bdiventa new Assignment("a", "b")diventa vars["a"] = vars["b"];. Se ti semplifica la vita, converti l'AST in una forma più semplice prima della valutazione.

Consiglio di progettare la cosa più semplice che funzioni e rimanga leggibile. Ecco un esempio di come potrebbe apparire una lingua. Il tuo design differirà necessariamente in base alle tue esigenze e preferenze specifiche.

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

In alternativa, scopri come incorporare un linguaggio di script esistente come Python o Lua nella tua applicazione e utilizzalo. Il rovescio della medaglia dell'uso di un linguaggio di uso generale per un'attività specifica del dominio è che l'astrazione presenta delle perdite: tutte le caratteristiche e i trucchi del linguaggio sono ancora presenti. Il vantaggio è che non è necessario implementarlo da soli, e questo è un vantaggio significativo. Tieni conto di questo.


2
Non è un brutto approccio ma sono costantemente scettico nei confronti di DSL, sono un sacco di lavoro da fare bene (specialmente se stai parlando di un vero DSL con sintassi personalizzata e tutto, al contrario di solo alcune API fluenti che le persone hanno ho iniziato a chiamare "DSL", quindi è meglio essere sicuri di usarlo, se lo fai, ne vale la pena. Spesso penso che le persone vogliano provare un DSL in cui lo useranno solo per un piccolo motore di regole. Ecco la mia regola empirica: se l'implementazione di DSL + l'utilizzo è meno codice di nessun DSL, provaci, non penso che sarebbe così in questo caso.
Jimmy Hoffa,

1
@JimmyHoffa: abbastanza giusto. È solo nella mia natura cercare soluzioni basate sul linguaggio, specialmente per i giochi. Probabilmente sottovaluto la difficoltà di rendere qualcosa di piccolo e funzionale perché l'ho fatto molte volte. Tuttavia, sembra una raccomandazione appropriata in questo caso.
Jon Purdy,

Immagino che dovrei dire che è l'approccio giusto a questo tipo di problema, ma si basa sul fatto che la persona che sta eseguendo l'implementazione è qualcuno con abilità sufficienti. Per quanto riguarda l'apprendimento, i DSL sono ottimi per i giovani, ma per i prodotti realmente rilasciabili non vorrei mai vedere nessuno sotto il livello senior scrivere un DSL, e per un minore un problema risolvibile del DSL dovrebbe essere risolto in un modo diverso.
Jimmy Hoffa,

Dopo aver letto questo penso che potrei semplicemente valutare gli script lua per quello. Il rovescio della medaglia qui sarebbe che un utente deve essere in grado di scrivere script lua. Il mio obiettivo personale è quello di scrivere un'interfaccia che può essere utilizzata senza conoscenze di programmazione, come il generatore di regole in Magento (app e-commerce). Perché voglio che le persone possano aggiungere le proprie regole. Non sto implementando nulla di commerciale, solo uno strumento per consentire a me e ai miei amici di entrare nelle regole del sistema di gioco di ruolo che giochiamo su una base irregolare e tornare alle regole e applicarle è un dolore dopo qualche tempo ...
Burzum

1
@burzum: che ne dici di avere la tua interfaccia per generare gli script lua?
TMN,

3

Vorrei iniziare determinando le diverse "fasi" di ciascuna azione.

Ad esempio, una fase di combattimento potrebbe comportare:

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

Ognuno di questi metodi avrebbe accesso ad alcuni oggetti abbastanza generici, come il Playere il Monster, ed eseguirà alcuni controlli abbastanza generici che altre entità possono usare per modificare i valori.

Ad esempio, potresti avere qualcosa di simile a questo incluso nel tuo GetPlayerCombatStats()metodo:

GetPlayerCombatStats()
{
    Stats tempStats = player.BaseStats;

    player.GetCombatStats(player, monster, tempStats);

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

Ciò ti consente di aggiungere facilmente qualsiasi entità con regole specifiche, come una Classe giocatore, un mostro o un pezzo Equipaggiamento.

Come altro esempio, supponiamo che tu volessi una Spada di uccidere tutto tranne il calamaro , che ti dà +4 contro tutto, a meno che quella cosa non abbia tentacoli, nel qual caso devi far cadere la spada e ottenere un -10 in combattimento.

La tua classe di equipaggiamento per questa spada potrebbe avere un GetCombatStatsaspetto simile al seguente:

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

Ciò ti consente di modificare facilmente i valori di combattimento senza dover conoscere il resto della logica di combattimento e ti consente di aggiungere facilmente nuovi pezzi all'applicazione perché i dettagli e la logica di implementazione del tuo Equipaggiamento (o qualunque entità sia) solo deve esistere nella stessa classe di entità.

Le cose chiave da capire è in quali punti possono cambiare i valori e quali elementi influenzano tali valori. Una volta che hai quelli, costruire i tuoi singoli componenti dovrebbe essere facile :)


Questa è la strada da percorrere. Nella maggior parte dei giochi di ruolo Pen & Paper, ci sono fasi nei calcoli, che sono generalmente scritte nelle guide. Puoi anche mettere l'ordinamento di stage in un elenco ordinato per renderlo più generico.
Hakan Deryal,

Questo suona abbastanza statico per me e non consente a un utente di inserire / creare semplicemente le regole richieste in un'interfaccia utente? Quello che descrivi suona più come regole hardcoded. Questo dovrebbe essere fattibile anche con un elenco di regole nidificate. Non voglio codificare nessuna regola. Se potessi fare tutto nel codice non avrei bisogno di porre quella domanda, sarebbe facile. :)
burzum,

@burzum Sì, questo significa che le regole sarebbero definite dal codice, tuttavia lo rende molto estensibile dal codice. Ad esempio, se desideri aggiungere un nuovo tipo di classe, un nuovo pezzo di equipaggiamento o un nuovo tipo di mostro, devi solo costruire gli oggetti di classe per quell'entità e compilare i metodi appropriati nella tua classe con la tua logica.
Rachel,

@burzum Ho appena letto la modifica della tua domanda. Se si desidera che un motore di regole solo per l'uso dell'interfaccia utente, vorrei prendere in considerazione facendo un pool di entità ( Player, Monster, Dice, ecc), e la creazione di qualcosa che permette agli utenti di pezzi entità drag / drop in un'area "equazione", compilando i parametri di l'entità (come la compilazione player.base_attack) e specificare operatori semplici su come i pezzi si incastrano. In realtà ho pubblicato qualcosa sul mio blog che analizza un'equazione matematica che potresti essere in grado di usare.
Rachel,

@Rachel quello che descrivi è OOP semplicissimo, ci sono anche molti esempi in natura che usano giochi di ruolo come esempi per insegnare a OOP. Avere questi oggetti e lavorare con loro è la parte facile, potrei costruirli al volo sulla base dei dati del database. Il problema nel tuo approccio sono le regole hardcorded come GetEnemyCombatStats () qualunque cosa questo metodo debba essere definito da qualche parte tramite un'interfaccia utente e memorizzato in un db. Il tuo articolo sembra essere simile a quello github.com/bobthecow/Ruler o quel github.com/Trismegiste/PhpRules .
Burzum,

0

Darei un'occhiata a maptool, in particolare il quarto framework di Rumble . È il miglior sistema che abbia mai visto per configurare ciò di cui stai parlando. Sfortunatamente il meglio è ancora orribilmente croccante. Il loro sistema "macro" si è ... diciamo ... evoluto nel tempo.

Per quanto riguarda un "parser di regole", mi limiterei a seguire qualsiasi linguaggio di programmazione tu sia a tuo agio, anche se questo è PHP. Non ci sarà alcun buon modo per codificare tutte le regole del tuo sistema.

Ora, se vuoi che i tuoi utenti siano in grado di scrivere IL PROPRIO set di regole, allora stai cercando di implementare il tuo linguaggio di scripting. Gli utenti scrivono le loro azioni di alto livello, il tuo php lo interpreta in qualcosa che influisce effettivamente sui valori del database, e quindi lancia un sacco di errori perché è un sistema orribilmente fragile che è stato messo in piedi nel corso degli anni. Davvero, la risposta di Jon Purdy è proprio sulla palla.


0

Penso che devi essere in grado di essere in grado di pensare in modo astratto a quali saranno le cose nel tuo spazio problematico, inventare una sorta di modello e quindi basare il tuo DSL su quello.

Ad esempio, potresti avere entità entità, azione ed evento al livello più alto. I tiri di dado sarebbero eventi che si verificano a seguito di azioni. Le azioni avrebbero condizioni che determinano se sono disponibili in una determinata situazione e uno "script" di cose che si verificano quando viene intrapresa l'azione. Cose più complesse sarebbero la capacità di definire una sequenza di fasi in cui possono verificarsi diverse azioni.

Una volta che hai una sorta di modello concettuale (e ti suggerisco di scriverlo e / o disegnare diagrammi per rappresentarlo) puoi iniziare a esaminare diversi modi per implementarlo.

Un percorso è definire quello che viene chiamato DSL esterno in cui si definisce la sintassi e si utilizza uno strumento come antlr per analizzarlo e chiamare la propria logica. Un altro percorso consiste nell'utilizzare le strutture presenti in un linguaggio di programmazione per definire il tuo DSL. Lingue come Groovy e Ruby sono particolarmente buone in questo spazio.

Una trappola che devi evitare è quella di mescolare la logica di visualizzazione con il modello di gioco implementato. Vorrei che il display leggesse il tuo modello e lo visualizzasse in modo appropriato piuttosto che mescolare il tuo codice di visualizzazione mescolato con il tuo modello.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.