Un generator de numere pseudo aleatoare (PRNG), cum ar fi Întâmplător
Clasa în C # nu este un adevărat generator de numere aleatoare: scopul său este de a aproxima întâmplare cu viteza. Acest lucru înseamnă că va reveni adesea o distribuție neuniformă a valorilor, ceea ce poate nu este ceea ce doriți. În acest tutorial, vă voi arăta cum să rezolvați această problemă cu un shuffle sac.
Notă: Deși acest tutorial utilizează C #, ar trebui să puteți utiliza aceleași tehnici și concepte în aproape orice mediu de dezvoltare a jocului.
Când am început să creez jocuri, am folosit standardul Întâmplător()
metode pentru a crea varietate în gameplay, creând mari dacă / altceva
până când primesc rezultatele dorite. Dacă rezultatele nu ar fi echilibrate așa cum le-am dorit, aș crea condiții suplimentare până când am simțit că jocul era distractiv. Abia de curând mi-am dat seama că există abordări mai bune în crearea unei experiențe cu adevărat distractive în joc.
Nu este nimic în neregulă cu utilizarea built-in-ului Întâmplător
clasa: problema de a nu obține rezultatele dorite rezultă din implementarea metodelor. În acest articol vom folosi metoda "Shuffle Bag" Întâmplător()
simti mai aleator (si mai distractiv), folosind panouri Boggle ca un exemplu practic.
Ați observat vreodată că o expresie ca:
valoarea int = Random.Next (valori inferioare, valori superioare);
... vă oferă o distribuție neuniformă a numerelor?
Un generator de numere aleatoare care alege valori intre 0 si 1 nu ii pasa daca returneaza toate 1s, deci daca ai creat un dacă / altceva
blocați utilizând expresia de mai sus pentru a alege o ramură, probabil că nu obțineți rezultatele pe care le așteptați.
var rand = nou Random (); pentru (int i = 0; i < 10; i++) Console.WriteLine(rand.Next(0, 2));
Nu este nimic tehnic în neregulă cu Random.Next ()
, dar nu garantează o distribuție uniformă a numerelor. Aceasta înseamnă că în multe situații de joc, Întâmplător()
nu este distractiv.
Un shuffle Bag este o tehnică de control al alegerii pentru a crea distribuția pe care o dorim. Ideea este:
Implementarea unei pungi de shuffle în C # este simplă și tehnica poate fi ușor convertită în orice limbă.
Întrucât scopul acestui articol este de a ne concentra asupra punerii în aplicare a pachetelor Shuffle și nu a caracteristicilor lingvistice, nu vom examina utilizarea de medicamente generice. Cu toate acestea, recomand cu fermitate utilizarea genericelor deoarece acestea ne permit să realizăm structuri de date de tip sigur, fără a se angaja în tipuri de date actuale. Genericul vă permite să utilizați același cod pentru a crea pungi de shuffle care dețin mai multe tipuri diferite de date.Iată codul de bază:
clasa publică ShuffleBag private Random aleator = nou Random (); Lista privatădate; private char currentItem; private int currentPosition = -1; private int Capacitate get return data.Capacity; public int Mărime get return data.Count; ShuffleBag public (int initCapacity) data = lista nouă (initCapacity);
Începutul clasei stabilește variabilele de instanță și constructorul inițializează variabila instanței de date la capacitatea inițială a programatorului (adică cât de mare va fi punga).
public void Adăugați (elementul char, suma int) for (int i = 0; i < amount; i++) data.Add(item); currentPosition = Size - 1;
Adăuga
metoda adaugă pur și simplu carboniza
la date
de câte ori specifică programatorul.
Rețineți că pozitie curenta
este setat la sfârșitul listei, deoarece vom trece mai târziu de la final. De ce de la sfârșitul listei? S-ar putea face ca sacul Shuffle sa treaca de la inceput, dar incepand de la sfarsit si lucrand in spate, face ca un cod mai curat.
public char Următorul () if (currentPosition < 1) currentPosition = Size - 1; currentItem = data[0]; return currentItem; var pos = random.Next(currentPosition); currentItem = data[pos]; data[pos] = data[currentPosition]; data[currentPosition] = currentItem; currentPosition--; return currentItem;
Următor →
metoda este carnea acestei tehnici.
Dacă pozitie curenta
este mai mică decât una, ne reinițializăm pentru a indica sfârșitul listei și a returna primul element din geantă. (Aceasta acoperă situația în care am trecut prin toate articolele și acum doriți să începeți din nou.)
În caz contrar, vom folosi random.Next ()
pentru a alege un articol aleatoriu din sac, de undeva între primul element și elementul din poziția actuală. Schimbăm acest element selectat aleatoriu cu elementul în poziția curentă și apoi scădăm pozitie curenta
de 1.
În cele din urmă, vom returna elementul selectat aleatoriu. Rezultatul este că vom continua să alegem obiecte pe care nu le-am ales înainte, în timp ce amestecăm sacul pe măsură ce mergem. Aceasta înseamnă că conținutul său este într-o ordine diferită atunci când dorim să o traversăm din nou.
Acum este timpul să încercăm clasa nouă creată.
Cu câțiva ani în urmă am creat o clonă Boggle pentru iPhone.
O problemă cu care am confruntat a fost crearea de panouri dense care au folosit doar 16 litere, dar le-a permis utilizatorului să formeze sute de cuvinte cu cele 16 litere. Am aflat despre frecvențele scrisorilor și cum aș putea să le folosesc pentru a crea o experiență pozitivă pentru utilizatori.
Folosind frecvența în care literele apar în textul englezesc, putem construi un dicționar ponderat.
private static Dicționar letterFrequencies= un nou dicționar 'E', 12.702, 'T', 9.056, 'A', 8.167, 'O', 7.507, 'I', 6.966 , 'S', 6.327, 'H', 6.094, 'R', 5.987, 'D', 4.253, 'L', 4.025 'U', 2.758, 'M', 2.406, 'W', 2.306, 'F', 2.228, P ', 1.929, ' B ', 1.492, ' V ', 0.978, ' K ', 0.772, , 0,095, 'Z', 0,074; // total: 99,965
Notă: Q este tratat un pic diferit de celelalte litere. Acesta păstrează valoarea din tabelul de frecvență a literelor, dar apare ca Qu în multe jocuri de cuvinte.
Acum putem crea o instanță a clasei noastre Shuffle Bag, umpleți sacul Shuffle cu date și creați dulapuri bogate de Boggle.
static void principal (șir [] args) var shuffleBag = nou ShuffleBag (88000); suma int = 0; foreach (litera var în litereFrecvențe) amount = (int) letter.Value * 1000; shuffleBag.Add (letter.Key, suma); pentru (int i = 0; i < 16; i++) Console.Write(shuffleBag.Next()); Console.WriteLine();
Notă: Cel mai important lucru pe care trebuie să-l îndepărtați de acest cod este acela Cantitate
. Un multiplicator de 1000 returnează rezultate mai bune decât un multiplicator de 10.
Rulați rezultatele printr-un solver online. Câte cuvinte găsești?
În acest articol, am recunoscut problema în utilizarea valorilor aleatoare cu dacă / altceva
am introdus o soluție folosind Shuffle Bags și am demonstrat o utilizare prin crearea de panouri dense de Boggle. Folosind Shuffle Bags, preluăm controlul Întâmplător()
metode și să creeze o distribuție uniformă a valorilor care ajută la o experiență de joc pozitivă.