Jestem twórcą gier internetowych i mam problem z przypadkowymi liczbami. Powiedzmy, że gracz ma 20% szans na trafienie krytyczne mieczem. Oznacza to, że 1 na 5 trafień powinno być krytyczne. Problem polega na tym, że otrzymałem bardzo złe wyniki w życiu - czasami gracze otrzymują 3 trafienia krytyczne w 5 trafieniach, czasami żaden z 15 trafień. Bitwy są raczej krótkie (3-10 trafień), więc ważne jest, aby uzyskać dobry losowy rozkład.
Obecnie używam PHP mt_rand()
, ale przenosimy nasz kod do C ++, więc chcę rozwiązać ten problem w nowym silniku naszej gry.
Nie wiem, czy rozwiązaniem jest jakiś jednolity generator losowy, czy może zapamiętanie poprzednich stanów losowych, aby wymusić właściwy rozkład.
Odpowiedzi:
Zgadzam się z wcześniejszymi odpowiedziami, że prawdziwa losowość w małych seriach niektórych gier jest niepożądana - wydaje się zbyt niesprawiedliwa w niektórych przypadkach użycia.
Napisałem prostą implementację typu Shuffle Bag w Rubim i przeprowadziłem kilka testów. Wdrożenie zrobiło to:
Jest uważane za niesprawiedliwe na podstawie prawdopodobieństw granicznych. Na przykład dla prawdopodobieństwa 20% można ustawić 10% jako dolną granicę i 40% jako górną granicę.
Korzystając z tych granic, stwierdziłem, że w przypadku serii 10 trafień w 14,2% przypadków prawdziwa implementacja pseudolosowa dała wyniki wykraczające poza te granice . W około 11% przypadków 0 trafień krytycznych padło w 10 próbach. W 3,3% przypadków padło 5 lub więcej trafień krytycznych na 10. Oczywiście, używając tego algorytmu (z minimalną liczbą rzutów 5), znacznie mniejsza liczba (0,03%) „Fairish” przebiegów była poza zakresem . Nawet jeśli poniższa implementacja jest nieodpowiednia (z pewnością można zrobić sprytniejsze rzeczy), warto zauważyć, że często Twoi użytkownicy będą czuli, że jest to niesprawiedliwe w przypadku prawdziwego pseudolosowego rozwiązania.
Oto treść mojego
FairishBag
napisanego w języku Ruby. Cała implementacja i szybka symulacja Monte Carlo jest dostępna tutaj (streszczenie) .Aktualizacja: Korzystanie z tej metody zwiększa ogólne prawdopodobieństwo otrzymania trafienia krytycznego do około 22% przy zastosowaniu powyższych granic. Możesz to zrównoważyć, ustawiając jego „rzeczywiste” prawdopodobieństwo nieco niższe. Prawdopodobieństwo na poziomie 17,5% z modyfikacją fairish daje obserwowane prawdopodobieństwo długoterminowe na poziomie około 20% i sprawia, że przebiegi krótkoterminowe są sprawiedliwe.
źródło
Potrzebujesz torby do losowania . Rozwiązuje problem prawdziwego losowego bycia zbyt losowym dla gier.
Algorytm wygląda mniej więcej tak: umieszczasz 1 krytyczne i 4 niekrytyczne trafienia w torbie. Następnie losujesz ich kolejność w torbie i wybierasz je pojedynczo. Kiedy worek jest pusty, napełniasz go ponownie tymi samymi wartościami i losujesz. W ten sposób otrzymasz średnio 1 trafienie krytyczne na 5 trafień i maksymalnie 2 trafienia krytyczne i 8 niekrytycznych z rzędu. Zwiększ liczbę przedmiotów w torbie, aby uzyskać większą losowość.
Oto przykład implementacji (w Javie) i jej przypadki testowe, które napisałem jakiś czas temu.
źródło
Nie rozumiesz, co oznacza losowość.
Który z nich jest bardziej losowy?
Podczas gdy drugi wykres wygląda na bardziej równomiernie rozłożony, tym bardziej losowy jest w rzeczywistości pierwszy wykres. Ludzki umysł często widzi wzorce w przypadkowości, więc na pierwszym wykresie widzimy skupiska jako wzorce, ale tak nie jest - są po prostu częścią losowo wybranej próbki.
źródło
Biorąc pod uwagę zachowanie, o które prosisz, myślę, że losujesz niewłaściwą zmienną.
Zamiast wybierać losowo, czy to trafienie będzie krytyczne, spróbuj losować liczbę tur do następnego trafienia krytycznego. Na przykład, po prostu wybierz liczbę od 2 do 9 za każdym razem, gdy gracz otrzyma trafienie krytyczne, a następnie daj mu następny krytyczny po upływie tylu rund. Możesz także użyć metod kostek, aby zbliżyć się do normalnego rozkładu - na przykład otrzymasz następne trafienie krytyczne w turach 2D4.
Uważam, że ta technika jest używana w grach RPG, które mają również przypadkowe spotkania na całym świecie - losujesz licznik kroków, a po tylu krokach ponownie zostajesz trafiony. Wydaje się to o wiele bardziej sprawiedliwe, ponieważ prawie nigdy nie zostajesz trafiony przez dwa spotkania z rzędu - jeśli zdarzy się to choćby raz, gracze stają się zirytowani.
źródło
Najpierw zdefiniuj „właściwą” dystrybucję. Liczby losowe są, cóż, losowe - wyniki, które widzisz, są całkowicie zgodne z (pseudo) losowością.
Rozwijając to, zakładam, że chcesz mieć poczucie „sprawiedliwości”, więc użytkownik nie może przejść 100 obrotów bez sukcesu. Jeśli tak, śledziłbym liczbę niepowodzeń od ostatniego sukcesu i ważył wygenerowany wynik. Załóżmy, że chcesz, aby 1 na 5 rzutów zakończył się sukcesem. Więc losowo generujesz liczbę od 1 do 5, a jeśli jest to 5, świetnie.
Jeśli nie, zapisz awarię, a następnym razem wygeneruj liczbę od 1 do 5, ale dodaj powiedzmy floor (numFailures / 2). Więc tym razem mają szansę 1 na 5. Jeśli im się nie uda, następnym razem wygrywający przedział to 4 i 5; szansa na sukces 2 na 5. Dzięki tym wyborom, po 8 porażkach, na pewno się uda.
źródło
Co powiesz na zamianę mt_rand () na coś takiego?
(RFC 1149.5 określa 4 jako standardową liczbę losową zweryfikowaną przez IEEE).
Z XKCD .
źródło
Mamy nadzieję, że ten artykuł Ci pomoże: http://web.archive.org/web/20090103063439/http://www.gamedev.net:80/reference/design/features/randomness/
Ta metoda generowania „liczb losowych” jest powszechna w grach RPG / MMORPG.
Problem, który rozwiązuje, jest następujący (wyciąg):
źródło
To, czego chcesz, to nie liczby losowe, ale liczby, które wydają się przypadkowe człowiekowi. Inni już zasugerowali indywidualne algorytmy, które mogą Ci pomóc, na przykład Shuffle Bad.
Aby uzyskać dobrą szczegółową i obszerną analizę tej domeny, zobacz temat AI Game Programming Wisdom 2 . Cała książka jest warta przeczytania dla każdego twórcy gier, idea „pozornie losowych liczb” została omówiona w rozdziale:
Filtrowana losowość dla decyzji AI i logiki gry :
Streszczenie: Konwencjonalna mądrość sugeruje, że im lepszy generator liczb losowych, tym bardziej nieprzewidywalna będzie Twoja gra. Jednak według badań psychologicznych prawdziwa losowość w perspektywie krótkoterminowej często wydaje się ludziom zdecydowanie nieładna. W tym artykule pokazano, jak podejmować losowe decyzje sztucznej inteligencji i logikę gry dla graczy bardziej losowo, przy jednoczesnym zachowaniu silnej losowości statystycznej.
Może zainteresować Cię także inny rozdział:
Statystyka liczb losowych
Streszczenie: Liczby losowe są najczęściej używane przez sztuczną inteligencję i gry w ogóle. Ignorowanie ich potencjału oznacza uczynienie gry przewidywalną i nudną. Używanie ich w niewłaściwy sposób może być równie złe, jak ich ignorowanie. Zrozumienie, w jaki sposób generowane są liczby losowe, ich ograniczeń i możliwości, może usunąć wiele trudności związanych z ich używaniem w grze. Ten artykuł zawiera informacje na temat liczb losowych, ich generowania i metod oddzielania dobrych od złych.
źródło
Z pewnością każda generacja liczb losowych ma szansę na wyprodukowanie takich przebiegów? Nie dostaniesz wystarczająco dużego zestawu próbek w 3-10 rolkach, aby zobaczyć odpowiednie wartości procentowe.
Może to, czego chcesz, to próg miłosierdzia ... pamiętaj o ostatnich 10 rzutach, a jeśli nie mieli krytycznego trafienia, daj im freebie. Wygładź procy i strzały losowości.
źródło
Najlepszym rozwiązaniem może być play-testów z wieloma różnymi non systemów losowych i wybrać ten, który sprawia graczy najszczęśliwsze.
Możesz również wypróbować politykę wycofywania się dla tej samej liczby w danym spotkaniu, np. Jeśli gracz wyrzuci
1
w swojej pierwszej turze, zaakceptuj ją. Aby zdobyć kolejny1
, muszą rzucić 21
sekundy z rzędu. Aby uzyskać trzecią1
, potrzebują 3 z rzędu, w nieskończoność.źródło
Niestety to, o co prosisz, to w rzeczywistości generator liczb nielosowych - ponieważ chcesz, aby poprzednie wyniki były brane pod uwagę przy określaniu następnej liczby. Obawiam się, że nie tak działają generatory liczb losowych.
Jeśli chcesz, aby 1 na 5 trafień było krytyczne, po prostu wybierz liczbę od 1 do 5 i powiedz, że to trafienie będzie krytyczne.
źródło
mt_rand () jest oparty na implementacji Mersenne Twister , co oznacza, że daje jedną z najlepszych losowych dystrybucji, jakie można uzyskać.
Najwyraźniej to, czego chcesz, wcale nie jest przypadkowe, więc powinieneś zacząć od określenia dokładnie tego, czego chcesz. Prawdopodobnie zdasz sobie sprawę, że masz sprzeczne oczekiwania - że wyniki powinny być naprawdę przypadkowe i nieprzewidywalne, a jednocześnie nie powinny wykazywać lokalnych odchyleń od podanego prawdopodobieństwa - ale wtedy staje się przewidywalne. Jeśli ustawisz maksymalnie 10 bezkrytycznych z rzędu, to właśnie powiedziałeś graczom "jeśli masz 9 bez krytyków z rzędu, następny będzie krytyczny ze 100% pewnością" - możesz tak w ogóle nie przejmuj się przypadkowością.
źródło
Przy tak małej liczbie testów powinieneś spodziewać się takich wyników:
Prawdziwa losowość jest przewidywalna tylko w przypadku dużego zestawu, tak że całkiem możliwe jest rzucenie monetą i trafienie orzeł 3 razy z rzędu za pierwszym razem, jednak po kilku milionach rzutów otrzymasz około 50-50.
źródło
Widzę wiele odpowiedzi sugerujących śledzenie wcześniej wygenerowanych liczb lub tasowanie wszystkich możliwych wartości.
Osobiście nie zgadzam się, że 3 trafienia krytyczne z rzędu są złe. Nie zgadzam się też, że 15 nie-krytyków z rzędu jest złych.
Rozwiązałbym ten problem, modyfikując samą szansę na trafienie krytyczne po każdej liczbie. Przykład (aby zademonstrować pomysł):
Im dłużej nie dostaniesz trafienia krytycznego - tym większa masz szansę na trafienie krytycznej następnej akcji. Załączony przeze mnie reset jest całkowicie opcjonalny i wymagałoby przetestowania, aby stwierdzić, czy jest potrzebny, czy nie. Może być pożądane, ale nie musi, dać większe prawdopodobieństwo trafienia krytycznego za więcej niż jedną akcję z rzędu, po długim łańcuchu akcji bez krytyki.
Dorzucam tylko moje 2 centy ...
źródło
Kilka pierwszych odpowiedzi to świetne wyjaśnienia, więc skupię się tylko na algorytmie, który daje Ci kontrolę nad prawdopodobieństwem „złej passy”, nigdy nie będąc deterministycznym. Oto, co myślę, że powinieneś zrobić:
Zamiast podawać p , parametr rozkładu Bernoulliego, który jest Twoim prawdopodobieństwem trafienia krytycznego, podaj a i b , parametry rozkładu beta, „sprzężony poprzednik” rozkładu Bernoulliego. Musisz śledzić A i B , liczbę trafień krytycznych i niekrytycznych do tej pory.
Teraz, aby określić a i b , upewnij się, że a / (a + b) = p, szansa na trafienie krytyczne. Fajne jest to, że (a + b) określa ilościowo, jak blisko chcesz, aby A / (A + B) było ogólnie p.
Robisz samplowanie w ten sposób:
niech
p(x)
będzie funkcją gęstości prawdopodobieństwa rozkładu beta. Jest dostępny w wielu miejscach, ale możesz go znaleźć w GSL jako gsl_ran_beta_pdf.Wybierz trafienie krytyczne, próbkując z rozkładu Bernoulliego z prawdopodobieństwem p_1 / (p_1 + p_2)
Jeśli okaże się, że liczby losowe mają zbyt wiele „złych smugi” skalować się i B , ale w granicach, jak w i b idź do nieskończoności, trzeba będzie podejście torba losowe opisane wcześniej.
Jeśli to wdrożysz, daj mi znać, jak to idzie!
źródło
Jeśli chcesz mieć dystrybucję, która zniechęca do powtarzania wartości, możesz użyć prostego algorytmu odrzucania powtórzeń.
na przykład
Ten kod odrzuca powtarzające się wartości w 95% przypadków, co sprawia, że powtórzenia są mało prawdopodobne, ale nie niemożliwe. Statystycznie jest to trochę brzydkie, ale prawdopodobnie przyniesie oczekiwane rezultaty. Oczywiście nie zapobiegnie to dystrybucji takiej jak „5 4 5 4 5”. Możesz stać się bardziej wyszukany i odrzucić przedostatniego (powiedzmy) 60% przypadków, a trzeciego od końca (powiedzmy) 30%.
Nie polecam tego jako dobrego projektu gry. Po prostu sugeruję, jak osiągnąć to, czego chcesz.
źródło
Nie jest do końca jasne, czego chcesz. Możliwe jest utworzenie takiej funkcji, że przy pierwszych 5 jej wywołaniach zwraca liczby 1-5 w losowej kolejności.
Ale to nie jest przypadkowe. Gracz będzie wiedział, że w następnych 5 atakach otrzyma dokładnie jedną 5. Może to być jednak to, czego chcesz, aw takim przypadku po prostu musisz to samemu zakodować. (utwórz tablicę zawierającą liczby, a następnie przetasuj je)
Alternatywnie możesz nadal stosować swoje obecne podejście i założyć, że obecne wyniki są spowodowane złym generatorem losowym. Pamiętaj, że nie ma nic złego w aktualnych numerach. Wartości losowe są losowe. czasami otrzymujesz 2, 3 lub 8 tej samej wartości z rzędu. Ponieważ są przypadkowe. Dobry generator liczb losowych gwarantuje tylko, że średnio wszystkie liczby będą zwracane równie często.
Oczywiście, jeśli korzystałeś ze złego generatora losowego, może to wypaczyć wyniki, a jeśli tak, po prostu przełączenie na lepszy generator losowy powinno rozwiązać problem. (Sprawdź bibliotekę Boost.Random, aby uzyskać lepsze generatory)
Alternatywnie możesz zapamiętać ostatnie N wartości zwróconych przez funkcję losową i zważyć wynik przez nie. (prosty przykład brzmiałby: „dla każdego wystąpienia nowego wyniku istnieje 50% szansa, że powinniśmy odrzucić wartość i uzyskać nową”
Gdybym miał zgadywać, powiedziałbym, że najlepszym rozwiązaniem jest trzymanie się „rzeczywistej” losowości. Upewnij się, że używasz dobrego generatora losowego, a następnie kontynuuj tak, jak robisz to teraz.
źródło
Możesz utworzyć listę zawierającą liczby od 1 do 5 i posortować je według losowości. Następnie przejrzyj utworzoną listę. Masz gwarancję, że trafisz na każdy numer przynajmniej raz ... Kiedy skończysz z pierwszą piątką, po prostu utwórz kolejne 5 liczb ...
źródło
Polecam progresywny system procentowy, jakiego używa Blizzard: http://www.shacknews.com/onearticle.x/57886
Generalnie rzucasz RNG, a następnie porównujesz go z wartością, aby określić, czy się powiedzie, czy nie. To może wyglądać tak:
Wszystko, co musisz zrobić, to dodać stopniowe zwiększanie podstawowej szansy ...
Jeśli chcesz, aby był bardziej fantazyjny, możesz łatwo dodać więcej. Możesz ograniczyć kwotę, jaką progressiveChance może uzyskać, aby uniknąć stuprocentowej krytycznej szansy lub zresetować ją w niektórych wydarzeniach. Możesz również uzyskać progresywny wzrost szansy w mniejszych ilościach przy każdym wzmocnieniu z czymś w rodzaju progressiveChance + = (1 - progressiveChance) * SCALE, gdzie SCALE <1.
źródło
Cóż, jeśli trochę interesujesz się matematyką, prawdopodobnie możesz wypróbować rozkład wykładniczy
Na przykład, jeśli lambda = 0,5, oczekiwana wartość to 2 (przeczytaj ten artykuł!), Oznacza, że najprawdopodobniej będziesz uderzać / krytyka / cokolwiek co 2 turę (np. 50%, co?). Ale przy takim rozkładzie prawdopodobieństwa na pewno spudłujesz (lub zrobisz odwrotnie niż cokolwiek) na 0-tej turze (tej, w której zdarzenie już miało miejsce, a turn_counter został zresetowany), masz około 40% szans na trafienie w następnej turze, około 65% szansa na zrobienie tego w drugiej turze (następna po następnej), około 80% na trafienie w trzecią i tak dalej.
Głównym celem tej dystrybucji jest to, że jeśli ktoś ma 50% szansy na trafienie i nie trafi 3 razy z rzędu, to na pewno (no cóż, ponad 80% szansy i zwiększa się ona z każdą kolejną turą) trafi. Prowadzi to do bardziej „uczciwych” wyników, zachowując ponad 50% szans na niezmienionym poziomie.
Korzystając z 20% szans na trafienie krytyczne, masz
Nadal wynosi około 0,2% (w porównaniu do tych 5%) szans na 3 trafienia krytyczne + 2 trafienia niekrytyczne w 5 kolejnych turach. I jest 14% szans na 4 następujące po sobie odpowiedzi niekrytyczne, 5% z 5, 1,5% na 6, 0,3% na 7, 0,07% na 8 kolejnych niekrytycznych. Założę się, że to „sprawiedliwsze” niż 41%, 32%, 26%, 21% i 16%.
Mam nadzieję, że nadal się nie nudzisz.
źródło
A co z uzależnieniem szansy na trafienie krytyczne od ostatnich ataków N. Jednym prostym schematem jest pewnego rodzaju łańcuch markov: http://en.wikipedia.org/wiki/Markov_chain, ale kod i tak jest bardzo prosty.
Oczywiście musisz zrobić swoje obliczenia matematyczne, ponieważ szansa na trafienie krytyczne jest niższa niż szansa na trafienie krytyczne, gdy wiesz, że minęło wystarczająco dużo tur od ostatniego
źródło
OP,
Prawie, jeśli chcesz, aby było sprawiedliwe, nie będzie to przypadkowe.
Problemem twojej gry jest faktyczna długość meczu. Im dłuższe jest dopasowanie, tym mniej losowości zobaczysz (krytyki będą wynosić 20%) i zbliży się do zamierzonych wartości.
Masz dwie opcje, wstępnie oblicz ataki na podstawie poprzednich rzutów. Które otrzymasz jednego trafienia krytycznego co 5 ataków (w oparciu o twoje 20%), ale możesz ustawić losową kolejność, w jakiej występuje.
listOfFollowingAttacks = {Hit, Hit, Hit, Miss, Crit};
To jest wzór, którego chcesz. Więc niech wybierze losowo z tej listy, dopóki nie będzie pusta, utworzą ją ponownie.
To wzór, który stworzyłem dla mojej gry, działa całkiem nieźle, do tego, co chcę.
twoją drugą opcją byłoby zwiększenie szansy na trafienie krytyczne, prawdopodobnie zobaczysz bardziej równą liczbę na końcu wszystkich ataków (zakładając, że twoje mecze kończą się dość szybko). Im mniej% szans, tym więcej RNG dostaniesz.
źródło
Patrzysz na rozkład liniowy, kiedy prawdopodobnie chcesz mieć rozkład normalny.
Jeśli pamiętasz, że w młodości grałeś w D&D, zostałeś poproszony o rzucenie wieloma n-stronnymi kostkami, a następnie zsumuj wyniki.
Na przykład rzut 4 x 6-stronną kostką różni się od rzutu 1 x 24-ścienną kostką.
źródło
City of Heroes ma w rzeczywistości mechanikę zwaną „streakbreakerem”, która rozwiązuje dokładnie ten problem. Sposób, w jaki to działa, polega na tym, że po serii chybień o długości odpowiadającej najniższemu prawdopodobieństwu trafienia w ciągu, następny atak jest gwarantowany jako trafienie. Na przykład, jeśli przegapisz atak z ponad 90% szansą na trafienie, twój następny atak zostanie automatycznie trafiony, ale jeśli twoja szansa na trafienie jest niższa, np. 60%, będziesz musiał mieć kilka następujących po sobie chybień, aby wywołać "przełamacz serii" (I nie znam dokładnych liczb)
źródło
ten jest naprawdę przewidywalny ... ale nigdy nie możesz być pewien.
źródło
A co z ważeniem wartości?
Na przykład, jeśli masz 20% szans na trafienie krytyczne, wygeneruj liczbę od 1 do 5, gdzie jedna liczba oznacza trafienie krytyczne, lub liczbę od 1 do 100, gdzie 20 oznacza trafienie krytyczne.
Ale dopóki pracujesz z liczbami losowymi lub pseudolosowymi, nie ma sposobu, aby potencjalnie uniknąć wyników, które obecnie widzisz. Taka jest natura losowości.
źródło
Reakcja na: „Problem polega na tym, że otrzymałem bardzo złe wyniki w prawdziwym życiu - czasami gracze otrzymują 3 trafienia krytyczne w 5 trafieniach, czasami żaden z 15 trafień”.
Masz szansę gdzieś między 3 a 4% nie uzyskać nic w 15 trafieniach ...
źródło
Proponuję następującą „losowo opóźnioną kość odrzucenia”:
in-array
) początkowo wypełnioną wartościami od 0 do n-1, a drugą (out-array
) pustąin-array
in-array
doout-array
out-array
powrotem doin-array
Ma to tę właściwość, że będzie „reagować” wolniej, im większe będzie n . Na przykład, jeśli chcesz mieć 20% szansy, ustawienie n na 5 i trafienie na 0 jest „mniej losowe” niż ustawienie n na 10 i trafienie na 0 lub 1, co spowoduje, że 0 do 199 z 1000 będzie prawie nie do odróżnienia od prawdziwej losowości na małej próbce. Będziesz musiał dostosować n do rozmiaru próbki.
źródło
Oblicz wstępnie losowe trafienie krytyczne dla każdego gracza.
źródło
Myślę, że być może używasz złej funkcji dystrybucji losowej. Prawdopodobnie nie chcesz równego podziału liczb. Zamiast tego wypróbuj normalną dystrybucję, aby trafienia krytyczne stały się rzadsze niż „zwykłe” trafienia.
Pracuję z Javą, więc nie jestem pewien, gdzie można znaleźć coś dla C ++, które daje losowe liczby z normalną dystrybucją, ale coś tam musi być.
źródło