Shuffle Bags Random () Willekeurig voelen

Een pseudo random number generator (PRNG) zoals de Willekeurig Klasse in C # is geen echte generator voor willekeurige getallen: het doel is om willekeurigheid met snelheid te benaderen. Dit betekent dat het vaak een ongelijke verdeling van waarden oplevert, wat niet is wat u wilt. In deze zelfstudie laat ik u zien hoe u dit probleem kunt oplossen met een shuffle tas.

Notitie: Hoewel deze tutorial C # gebruikt, zou je in bijna elke game-ontwikkelomgeving dezelfde technieken en concepten moeten kunnen gebruiken.


Invoering

Toen ik games begon te maken, gebruikte ik de standaard Willekeurig() methoden om variëteit in gameplay te creëren, groot te maken if / else voorwaarden totdat ik de gewenste resultaten heb ontvangen. Als de resultaten niet in balans waren zoals ik ze wilde, creëerde ik extra voorwaarden totdat ik vond dat het spelen leuk was. Het was tot voor kort dat ik me realiseerde dat er betere benaderingen zijn bij het creëren van een echt vermakelijke spelervaring.

Er is niets mis met het gebruik van de ingebouwde Willekeurig klasse: het probleem van het niet behalen van de gewenste resultaten komt voort uit de implementatie van de methoden. In dit artikel gebruiken we de "Shuffle Bag" -methode om te maken Willekeurig() voel meer willekeurig (en leuker), met behulp van Boggle-platen als een praktisch voorbeeld.


Het probleem

Heb je ooit gemerkt dat een uitdrukking als:

 int value = Random.Next (lowerBounds, upperBounds);

... geeft u een ongelijke verdeling van getallen?

Een generator voor willekeurige getallen die waarden tussen 0 en 1 kiest, maakt het niet uit als alle 1-en worden geretourneerd, dus als u een hebt gemaakt if / else blokkeren met behulp van de bovenstaande uitdrukking om een ​​filiaal te kiezen, krijgt u waarschijnlijk niet de resultaten die u verwacht.

 var rand = nieuw Willekeurig (); voor (int i = 0; i < 10; i++) Console.WriteLine(rand.Next(0, 2));

Er is niets technisch mis mee Random.Next (), maar het garandeert niet een mooie gelijkmatige verdeling van nummers. Dit betekent dat in veel gameplay-situaties, Willekeurig() is niet leuk.


Wat is een Shuffle Bag?

Een Shuffle Bag is een techniek om willekeur te beheersen om de door ons gewenste verdeling te creëren. Het idee is:

  • Kies een bereik van waarden met de gewenste verdeling.
  • Stop al deze waarden in een tas.
  • Schud de inhoud van de zak.
  • Trek de waarden één voor één uit tot u het einde bereikt.
  • Zodra u het einde hebt bereikt, begint u opnieuw en trekt u de waarden één voor één weer uit.

Een shuffle-tas implementeren

Het implementeren van een Shuffle Bag in C # is eenvoudig en de techniek kan eenvoudig in elke taal worden omgezet.

Aangezien het doel van dit artikel is om ons te concentreren op de implementatie van Shuffle Bags en niet op taalfuncties, zullen we niet kijken naar het gebruik van generieke geneesmiddelen. Ik raad echter ten sterkste het gebruik van generieke geneesmiddelen aan, omdat ze ons in staat stellen veilige datastructuren van het type te maken zonder zich te verbinden aan de werkelijke gegevenstypen. Met Generics kunt u dezelfde code gebruiken om Shuffle Bags te maken met veel verschillende soorten gegevens.

Hier is de basiscode:

 public class ShuffleBag private Random random = new Random (); privélijst gegevens; privé char currentItem; private int currentPosition = -1; private int Capacity krijg return data.Capacity;  public int Size krijg return data.Count;  openbare ShuffleBag (int initCapacity) data = new List (initCapacity); 

Het begin van de klasse stelt de instantievariabelen in, en de constructor initialiseert de gegevensinstantie-variabele in de initiële capaciteit van de programmeur (d.w.z. hoe groot de tas is om mee te beginnen).

 public void Toevoegen (char item, int amount) for (int i = 0; i < amount; i++) data.Add(item); currentPosition = Size - 1; 

De Toevoegen methode voegt gewoon de verkolen naar gegevens zo vaak als de programmeur aangeeft.

Merk op dat de huidige positie is ingesteld op het einde van de lijst, omdat we later van het einde zullen oversteken. Waarom vanaf het einde van de lijst? U kunt de Shuffle-tas vanaf het begin doorlopen, maar begin aan het einde en achteruit werken zorgt voor schonere code.

 public char Next () 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; 

De volgende methode is het vlees van deze techniek.

Als huidige positie is minder dan één, we resetten het om naar het einde van de lijst te wijzen en het eerste item uit de zak te retourneren. (Dit geeft de situatie weer waarin we alle items hebben doorlopen en nu opnieuw willen beginnen.)

Anders gebruiken we random.Next () om een ​​willekeurig item uit de tas te halen, ergens tussen het eerste item en het item op onze huidige positie. We wisselen dit willekeurig geselecteerde item uit met het item op onze huidige positie en verlagen vervolgens huidige positie door 1.

Ten slotte retourneren we het willekeurig geselecteerde item. Het resultaat is dat we items blijven plukken die we nog niet eerder hadden gepickt, terwijl we de tas schuifelen terwijl we verder gaan. Dit betekent dat de inhoud ervan in een andere volgorde staat wanneer we hem opnieuw willen doorkruisen.

Nu is het tijd om onze nieuw gecreëerde klas uit te proberen.


De Shuffle Bag-klasse gebruiken

Enkele jaren geleden heb ik een Boggle-kloon voor de iPhone gemaakt.


Afbeelding tegoed: Rich Brooks

Een probleem waar ik mee te maken kreeg, was het maken van dichte borden die slechts 16 letters gebruikten, maar de gebruiker toestonden honderden woorden te vormen met die 16 letters. Ik leerde over letterfrequenties en hoe ik het kon gebruiken om een ​​positieve gebruikerservaring te creëren.

Door de frequentie te gebruiken dat letters in Engelse tekst voorkomen, kunnen we een gewogen woordenboek construeren.

 privé statische WoordenboekletterFrequenties = nieuw Woordenboek 'E', 12.702, 'T', 9.056, 'A', 8.167, 'O', 7.507, 'I', 6.966, 'N', 6.769 , 'S', 6.327, 'H', 6.094, 'R', 5.987, 'D', 4.253, 'L', 4.025, 'C', 2.782, 'U', 2.758, 'M', 2.406, 'W', 2.306, 'F', 2.228, 'G', 2.015, 'Y', 1.974, ' P ', 1.929, ' B ', 1.492, ' V ', 0.978, ' K ', 0.772, ' J ', 0.153, ' X ', 0.150, ' Q ' , 0.095, 'Z', 0.074; // totaal: 99.965

Notitie: Q is een beetje anders behandeld dan de andere letters. Het behoudt de waarde van de letterfrequentietabel, maar het wordt weergegeven als Qu in veel woordspelletjes.

Nu kunnen we een exemplaar van onze Shuffle Bag-klasse maken, onze Shuffle Bag vullen met gegevens en dichte Boggle Boards maken.

 static void Main (string [] args) var shuffleBag = new ShuffleBag (88000); int amount = 0; foreach (var letter in letterFrequencies) amount = (int) letter.Value * 1000; shuffleBag.Add (letter.Key, aantal);  voor (int i = 0; i < 16; i++) Console.Write(shuffleBag.Next()); Console.WriteLine(); 

Notitie: Het belangrijkste om dit stukje code af te halen, is het bedrag. Een vermenigvuldiger van 1000 levert betere resultaten op dan een vermenigvuldiger van 10.

Voer de resultaten uit via een online oplosser. Hoeveel woorden vind je?


Conclusie

In dit artikel hebben we het probleem erkend met het gebruik van willekeurige waarden bij if / else voorwaarden, hebben we een oplossing geïntroduceerd met Shuffle Bags en een gebruik aangetoond door dichte Boggle Boards te maken. Met het gebruik van Shuffle Bags nemen wij de controle over Willekeurig() methoden en creëer een gelijkmatige verdeling van waarden die bijdragen aan een positieve spelervaring.