Ogólny parser reguł dla reguł gry planszowej RPG - jak to zrobić?

19

Chcę zbudować ogólny parser reguł dla systemów RPG typu długopis i papier. Reguła może zwykle obejmować od 1 do N jednostek 1 do N ról kostki i obliczanie wartości na podstawie wielu atrybutów jednostki.

Na przykład:

Gracz ma STR 18, jego obecnie wyposażona broń daje mu premię +1 STR, ale minus DEX -1. Atakuje potwora, a logika gry jest teraz wymagana do uruchomienia zestawu reguł lub akcji:

Gracz rzuca kośćmi, jeśli zdobędzie np. 8 lub więcej (podstawowa wartość ataku, którą musi przekazać, jest jednym z jego podstawowych atrybutów!) Jego atak się powiedzie. Następnie potwór rzuca kośćmi, aby obliczyć, czy atak przejdzie przez jego zbroję. Jeśli tak, obrażenia są przyjmowane, jeśli nie, atak został zablokowany.

Oprócz prostych reguł matematycznych mogą obowiązywać ograniczenia, takie jak zastosowanie tylko do określonej klasy użytkownika (na przykład wojownik kontra czarodziej) lub dowolnego innego atrybutu. Nie ogranicza się to tylko do operacji matematycznych.

Jeśli znasz systemy RPG, takie jak Dungeon i Dragons, będziesz wiedział, co planuję.

Mój problem polega teraz na tym, że nie mam pojęcia, jak dokładnie zbudować to w najlepszy możliwy sposób. Chcę, aby ludzie mogli ustanowić dowolną regułę, a później po prostu wykonać akcję, taką jak wybór gracza i potwora, i uruchomić akcję (zestaw reguł, takich jak atak).

Mniej proszę o pomoc po stronie bazy danych rzeczy, ale więcej o tym, jak wymyślić strukturę i parser, aby zachować elastyczność moich reguł. Nawiasem mówiąc, wybranym językiem jest php.

Edytuj I:

Pozwól mi doprecyzować mój cel: chcę stworzyć przyjazny interfejs użytkownika (który nie wymaga od kogoś nauki języka programowania), aby zbudować mniej lub bardziej złożone reguły gry. Prosty powód: użytek osobisty, aby nie musieć pamiętać wszystkich zasad przez cały czas, po prostu nie gramy tak często i za każdym razem jest to przeszkodą. Ponadto: wygląda na fajne zadanie do zrobienia i nauczenia się czegoś. :)

To, czego do tej pory próbowałem: myślenie o koncepcji zamiast marnowania czasu na budowanie złej architektury. Do tej pory mam pomysł, aby zezwolić użytkownikowi na tworzenie tylu atrybutów, ile chce, a następnie przypisywać tyle atrybutów, ile chcą do dowolnego rodzaju encji. Istotą może być gracz, potwór, przedmiot, cokolwiek. Teraz podczas obliczania czegoś dane są udostępniane analizatorowi reguł, aby analizator reguł mógł wykonywać takie czynności, jak Player.base_attack + dice (1x6)> Monster.armor_check, a następnie Monster.health - 1; Pytanie dotyczy tego, jak utworzyć ten analizator składni.

Edycja II:

Oto przykład dość podstawowej wartości, ale aby ją poprawnie obliczyć, należy wziąć pod uwagę wiele różnych rzeczy i zmiennych:

Podstawowy bonus do ataku (Termin) Podstawowy bonus do ataku (powszechnie określany jako BAB przez społeczność d20) to bonus do rzutu ataku uzyskany z klasy postaci i poziomu. Premie podstawowych ataków rosną w różnym tempie dla różnych klas postaci. Postać zyskuje drugi atak na rundę, gdy jego podstawowy bonus do ataku osiągnie +6, trzeci z bazową premią do ataku +11 lub wyższą, a czwarty z podstawową premią do ataku +16 lub wyższą. Bazowe bonusy do ataku uzyskane z różnych klas, np. Dla postaci wieloklasowej, stosu. Podstawowa premia do ataku postaci nie zapewnia już więcej ataków po osiągnięciu +16, nie może być mniejsza niż +0 i nie zwiększa się z powodu poziomów klasy, gdy poziom postaci osiągnie 20. W przypadku niektórych atutów wymagana jest minimalna premia do ataku podstawowego.

Możesz go przeczytać tutaj http://www.dandwiki.com/wiki/Base_Attack_Bonus_(Term), w tym linki do klas i atutów, które ponownie mają swoje własne zasady obliczania wartości wymaganych do ataku podstawowego.

Zacząłem myśleć, że utrzymanie tak ogólnej, jak to tylko możliwe, sprawi, że bardzo trudno będzie wykonać dobry parser reguł.

burzum
źródło
2
Właściwie to myślałem dziś rano dokładnie o tego rodzaju problemach (niezwiązanych z RPG, ale silnikami przetwarzania reguł) i próbowałem wymyślić niepaństwowe podejście maszyny do przetwarzania reguł oraz o tym, jak parseratory kombinacyjne są tak skuteczne w wykonaniu zadania zwykle wykonywanego przez maszyny stanowe. Wydaje mi się, że monadyczne kombinatory mają bardziej przejrzyste podejście do większości problemów z automatami stanowymi. To może brzmieć jak bełkot, ale myślę, że w tym pomyśle jest coś, tylko moje 2 centy. Systemy RPG to klasyczny problem z zabawą, który lubię kodować, być może wypróbuję to podejście.
Jimmy Hoffa
1
@jk. ten artykuł przypomina mi wzorzec, który lubiłem do analizy argumentów programu wiersza poleceń, używając słownika Funcs, który inicjuje stan programu na podstawie argumentów jako kluczy do słownika. Zaskoczony, że nigdy nie znalazłem tego postu z Yegge, bardzo fajnie, dziękuję za zwrócenie na niego uwagi.
Jimmy Hoffa
4
Nie jestem do końca pewien, dlaczego zostało to zamknięte jako „nie prawdziwe pytanie”. Jest to pytanie „tablicy” na wyższym poziomie dotyczące sposobu zaprojektowania aplikacji, która ma określony zestaw wymagań (system reguł RPG). Głosowałem za ponownym otwarciem, ale nadal będzie potrzebować 4 innych głosów, aby ponownie otworzyć.
Rachel
1
Szczerze mówiąc, myślałem, że ta strona jest właśnie dla tego rodzaju pytań koncepcyjnych, podczas gdy stackoverflow.com jest uważany za problemy z kodem / implementacją.
burzum

Odpowiedzi:

9

To, o co prosisz, to w zasadzie język specyficzny dla domeny - mały język programowania do wąskiego celu, w tym przypadku określający reguły RPG P&P. Projektowanie języka w zasadzie nie jest trudne, ale istnieje znaczna wiedza z góry, którą musisz zdobyć, aby w ogóle być produktywnym. Niestety, nie ma centralnego odniesienia do tych rzeczy - musisz to sprawdzić poprzez próbę, błąd i wiele badań.

Najpierw znajdź zestaw operacji pierwotnych, za pomocą których można wdrożyć inne operacje. Na przykład:

  • Zdobądź lub ustaw własność gracza, NPC lub potwora

  • Uzyskaj wynik rzutu kością

  • Oceń wyrażenia arytmetyczne

  • Oceń wyrażenia warunkowe

  • Wykonaj rozgałęzienie warunkowe

Zaprojektuj składnię, która wyraża Twoje prymitywy. Jak będziesz reprezentować liczby? Jak wygląda oświadczenie? Czy instrukcje są zakończone średnikiem? Zakończył się termin nowej linii? Czy istnieje struktura bloków? Jak to wskażesz: poprzez symbole lub wcięcia? Czy są zmienne? Co stanowi legalną nazwę zmiennej? Czy zmienne są zmienne? Jak uzyskasz dostęp do właściwości obiektów? Czy obiekty są najwyższej klasy? Czy możesz je samodzielnie stworzyć?

Napisz analizator składni, który zamienia program w abstrakcyjne drzewo składniowe (AST). Dowiedz się więcej o parsowaniu instrukcji za pomocą rekurencyjnego analizatora składni. Dowiedz się, w jaki sposób analizowanie wyrażeń arytmetycznych z rekurencyjnym zejściem jest denerwujące, a analizator pierwszeństwa od góry (parser Pratta) może ułatwić Ci życie i skrócić kod.

Napisz tłumacza, który ocenia Twoją AST. Może po prostu odczytać każdy węzeł w drzewie i zrobić to, co mówi: a = bstaje new Assignment("a", "b")się vars["a"] = vars["b"];. Jeśli to ułatwi ci życie, przekształć AST w prostszą formę przed oceną.

Polecam zaprojektowanie najprostszej rzeczy, która zadziała i pozostanie czytelna. Oto przykład, jak może wyglądać język. Twój projekt z konieczności będzie się różnić w zależności od twoich konkretnych potrzeb i preferencji.

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

Możesz także dowiedzieć się, jak osadzić istniejący język skryptowy, taki jak Python lub Lua, i używać go. Wadą używania języka ogólnego do zadań specyficznych dla domeny jest to, że abstrakcja jest nieszczelna: wszystkie funkcje i problemy tego języka są nadal obecne. Plusem jest to, że nie musisz sam go wdrażać - i to jest znacząca zaleta. Rozważ to.

Jon Purdy
źródło
2
Nie jest to złe podejście, ale konsekwentnie sceptycznie podchodzę do DSL, jest wiele pracy, aby je dobrze (zwłaszcza jeśli mówisz o prawdziwej DSL z niestandardową składnią i wszystkim innym, w przeciwieństwie do niektórych płynnych interfejsów API, które ludzie mają zaczął nazywać „DSL”), więc lepiej upewnij się, że użyjesz tego, do cholery, jeśli tak, to warto. Często myślę, że ludzie chcą wypróbować DSL, w którym będą używać go tylko do małego silnika reguł. Oto moja ogólna zasada: jeśli implementacja DSL + użycie jest mniejszą ilością kodu niż brak DSL, idź do niego, nie sądzę, aby tak było w tym przypadku.
Jimmy Hoffa
1
@JimmyHoffa: Wystarczająco sprawiedliwe. Po prostu sięgam po rozwiązania oparte na języku, zwłaszcza w grach. Prawdopodobnie nie doceniam trudności w stworzeniu czegoś małego i funkcjonalnego, ponieważ robiłem to wiele razy. W tym przypadku wydaje się to jednak odpowiednią rekomendacją.
Jon Purdy,
Chyba powinienem powiedzieć, że jest to właściwe podejście do tego rodzaju problemu, ale zależy to od tego, czy osoba wykonująca implementację jest osobą o wystarczających umiejętnościach. Do nauki DSL są świetne dla juniorów, ale w przypadku rzeczywistego produktu, który można wypuścić, nigdy nie chciałbym, aby ktokolwiek poniżej poziomu wyższego szczebla pisał DSL, a dla juniorów problem z DSL powinien być rozwiązany w inny sposób.
Jimmy Hoffa
Po przeczytaniu tego, myślę, że mógłbym po prostu ewaluować skrypty Lua. Minusem jest to, że użytkownik musi być w stanie pisać skrypty lua. Moim osobistym celem jest napisanie interfejsu, którego można używać bez wiedzy programistycznej, takiego jak konstruktor reguł w magento (aplikacja e-commerce). Ponieważ chcę, aby ludzie mogli dodawać własne reguły. Nie wdrażam niczego komercyjnego, po prostu narzędzie pozwalające mi i moim przyjaciołom wejść w zasady systemu RPG, na którym gramy na nieregularnych podstawach, a powrót do zasad i ich stosowanie to po pewnym czasie ból ...
burzum
1
@burzum: A co z tym, że Twój interfejs generuje skrypty Lua?
TMN
3

Zacznę od określenia różnych „faz” każdej akcji.

Na przykład faza walki może obejmować:

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

Każda z tych metod miałaby dostęp do niektórych dość ogólnych obiektów, takich jak Playeri Monster, i przeprowadziłaby pewne dość ogólne kontrole, których inne podmioty mogą użyć do zmodyfikowania wartości.

Na przykład możesz mieć coś, co wygląda następująco GetPlayerCombatStats():

GetPlayerCombatStats()
{
    Stats tempStats = player.BaseStats;

    player.GetCombatStats(player, monster, tempStats);

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

Pozwala to łatwo dodać dowolną jednostkę z określonymi regułami, na przykład klasę gracza, potwora lub element ekwipunku.

Jako kolejny przykład, załóżmy, że chciałeś Miecza Zabijającego Wszystko Z wyjątkiem Kałamarnicy , co daje ci +4 do wszystkiego, chyba że to ma macki, w takim przypadku musisz upuścić miecz i zdobyć -10 w walce.

Twoja klasa wyposażenia dla tego miecza może GetCombatStatswyglądać mniej więcej tak:

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

Umożliwia to łatwą modyfikację wartości walki bez konieczności poznawania reszty logiki walki oraz pozwala na łatwe dodawanie nowych elementów do aplikacji, ponieważ tylko szczegóły i logika implementacji sprzętu (lub dowolnego innego podmiotu) muszą istnieć w samej klasie encji.

Kluczową sprawą do ustalenia jest to, w których punktach wartości mogą się zmieniać i jakie elementy wpływają na te wartości. Po ich zbudowaniu poszczególne elementy powinny być łatwe :)

Rachel
źródło
To jest właściwa droga. W większości gier RPG na pióra i papier istnieją etapy w obliczeniach, które zazwyczaj są napisane w przewodnikach. Możesz także umieścić kolejność etapów na uporządkowanej liście, aby była bardziej ogólna.
Hakan Deryal
Brzmi dla mnie dość statycznie i nie pozwala użytkownikowi po prostu wprowadzić / zbudować wymaganych reguł w interfejsie użytkownika? To, co opisujesz, brzmi bardziej jak zakodowane zasady. Powinno to być również wykonalne poprzez listę zagnieżdżonych reguł. Nie chcę kodować żadnych reguł. Gdybym mógł zrobić wszystko w kodzie, nie musiałbym zadawać tego pytania, byłoby to łatwe. :)
burzum
@burzum Tak, to znaczy, że reguły będą definiowane przez kod, ale czyni to go bardzo rozszerzalnym z kodu. Na przykład, jeśli chcesz dodać nowy typ klasy, nowy element ekwipunku lub nowy typ potwora, musisz po prostu zbudować obiekty klasy dla tego bytu i wypełnić logiką odpowiednie metody w swojej klasie.
Rachel
@burzum Właśnie przeczytałem edycję twojego pytania. Jeśli chcesz silnik reguł tylko dla UI użytku, chciałbym rozważyć pulę podmiotów ( Player, Monster, Dice, etc), oraz tworzenie czegoś, co pozwala użytkownikom na drag / drop jednostki sztuk w obszar „równanie”, wypełnianie parametrów encja (np. wypełnianie player.base_attack) i określ proste operatory, jak pasują do siebie elementy. Mam na blogu coś, co analizuje równanie matematyczne , którego możesz użyć.
Rachel
@Rachel to, co opisujesz, jest bardzo łatwe OOP, istnieje nawet wiele przykładów na wolności, które wykorzystują RPG jako przykłady do nauczania OOP. Posiadanie tych obiektów i praca z nimi to prosta część, mógłbym je budować w locie na podstawie danych z bazy danych. Problemem w twoim podejściu są sztywne reguły, takie jak GetEnemyCombatStats (), cokolwiek ta metoda będzie musiała być gdzieś zdefiniowana za pomocą interfejsu użytkownika i zapisana w db. Twój artykuł wydaje się podobny do tego github.com/bobthecow/Ruler lub tego github.com/Trismegiste/PhpRules .
burzum
0

Chciałbym przyjrzeć maptool konkretnie 4-ci ramowej ed Rumble za . To najlepszy system, jaki widziałem do konfigurowania tego, o czym mówisz. Niestety, najlepsze jest nadal okropnie kruche. Ich system „makro” ... powiedzmy ... ewoluował z czasem.

Jeśli chodzi o „parser reguł”, po prostu trzymałbym się dowolnego języka programowania, w którym czujesz się komfortowo, nawet jeśli to jest PHP. Nie będzie dobrego sposobu na zakodowanie wszystkich reguł twojego systemu.

Teraz, jeśli chcesz, aby użytkownicy mogli pisać ICH WŁASNY zestaw reguł, to zastanawiasz się nad implementacją własnego języka skryptowego. Użytkownicy piszą swoje własne działania na wysokim poziomie, twój php interpretuje to w coś, co faktycznie wpływa na wartości bazy danych, a następnie generuje wiele błędów, ponieważ jest to okropnie chrupiący system, który przez lata był uginany od stóp do głów. Naprawdę, odpowiedź Jona Purdy'ego jest tuż przy piłce.

Philip
źródło
0

Myślę, że musisz być w stanie myśleć abstrakcyjnie o tym, co będzie w twojej przestrzeni problemów, wymyślić jakiś model, a następnie oprzeć na nim swoją DSL.

Na przykład możesz mieć encję encji, akcję i zdarzenie na najwyższym poziomie. Rzuty byłyby zdarzeniami występującymi w wyniku akcji. Akcje miałyby warunki, które określają, czy są one dostępne w danej sytuacji oraz „skrypt” rzeczy, które pojawiają się, gdy akcja jest podejmowana. Bardziej złożone rzeczy to zdolność do zdefiniowania sekwencji faz, w których mogą wystąpić różne działania.

Kiedy masz już jakiś model konceptualny (i sugeruję, aby go zapisać i / lub narysować diagramy, aby go przedstawić), możesz zacząć szukać różnych sposobów jego wdrożenia.

Jedną z dróg jest zdefiniowanie tak zwanego zewnętrznego DSL, w którym definiujesz składnię i używasz narzędzia takiego jak antlr do parsowania go i wywoływania logiki. Inną drogą jest użycie udogodnień dostępnych w języku programowania do zdefiniowania DSL. Języki takie jak Groovy i Ruby są szczególnie dobre w tej przestrzeni.

Jedną pułapką, której musisz unikać, jest mieszanie logiki wyświetlania z zaimplementowanym modelem gry. Głosowałbym za tym, aby wyświetlacz odczytał Twój model i wyświetlał odpowiednio, zamiast mieszania kodu wyświetlacza zmieszanego z modelem.

Peter Kelley
źródło