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ł.
Func
s, 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.Odpowiedzi:
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 = b
stajenew 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.
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.
źródło
Zacznę od określenia różnych „faz” każdej akcji.
Na przykład faza walki może obejmować:
Każda z tych metod miałaby dostęp do niektórych dość ogólnych obiektów, takich jak
Player
iMonster
, 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()
: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
GetCombatStats
wyglądać mniej więcej tak: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 :)
źródło
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łnianieplayer.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ć.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.
źródło
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.
źródło