Shuffle Vesker Gjør tilfeldig () Føl deg mer tilfeldig

En pseudo random number generator (PRNG) som Tilfeldig Klasse i C # er ikke en sann tilfeldig talegenerator: Målet er å tilnærme tilfeldighet med hastighet. Dette betyr at det ofte vil returnere en ujevn verdifall, noe som kanskje ikke er det du vil ha. I denne veiledningen vil jeg vise deg hvordan du løser dette problemet med en shuffle bag.

Merk: Selv om denne opplæringen bruker C #, bør du kunne bruke de samme teknikkene og konseptene i nesten hvilket som helst spillutviklingsmiljø.


Introduksjon

Da jeg først begynte å lage spill, brukte jeg standarden Tilfeldig() metoder for å skape variasjon i gameplay, skape stor hvis / annet forhold til jeg fikk mine ønskede resultater. Hvis resultatene ikke var balansert slik jeg ønsket dem, ville jeg skape flere forhold til jeg følte at spillingen var morsom. Det var ikke før jeg innså at det er bedre tilnærminger i å skape en virkelig underholdende spillopplevelse.

Det er ikke noe galt med bruk av den innebygde Tilfeldig klasse: problemet med ikke å få de ønskede resultatene stammer fra implementeringen av metodene. I denne artikkelen bruker vi "Shuffle Bag" -metoden for å lage Tilfeldig() føl deg mer tilfeldig (og mer moro), ved hjelp av Boggle boards som et praktisk eksempel.


Problemet

Har du noen gang lagt merke til at et uttrykk som:

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

... gir deg en ujevn fordeling av tall?

En tilfeldig talgenerator som velger verdier mellom 0 og 1 bryr seg ikke om den returnerer alle 1s, så hvis du har opprettet en hvis / annet blokkere ved hjelp av det ovennevnte uttrykket for å velge en filial, får du sannsynligvis ikke de resultatene du forventer.

 var rand = ny tilfeldig (); for (int i = 0; i < 10; i++) Console.WriteLine(rand.Next(0, 2));

Det er ikke noe teknisk feil med Random.Next (), men det garanterer ikke en fin jevn fordeling av tall. Dette betyr at i mange spill situasjoner, Tilfeldig() er ikke morsomt.


Hva er en Shuffle Bag?

En Shuffle Bag er en teknikk for å kontrollere tilfeldighet for å skape den fordelingen vi ønsker. Tanken er:

  • Velg en rekke verdier med ønsket distribusjon.
  • Sett alle disse verdiene i en pose.
  • Bland inn posens innhold.
  • Trekk verdiene ut en etter en til du når slutten.
  • Når du kommer til enden, starter du over, drar verdiene ut en etter en igjen.

Implementere en Shuffle Bag

Implementering av en Shuffle Bag i C # er enkel, og teknikken kan enkelt konverteres til hvilket som helst språk.

Siden formålet med denne artikkelen er å fokusere på implementeringen av Shuffle-poser og ikke språkfunksjoner, ser vi ikke på bruken av generikk. Jeg anbefaler imidlertid sterkt bruk av generikk fordi de tillater oss å lage type sikre datastrukturer uten å forplikte seg til faktiske datatyper. Generics lar deg bruke samme kode for å lage Shuffle-poser som inneholder mange forskjellige typer data.

Her er grunnkoden:

 offentlig klasse ShuffleBag privat tilfeldig tilfeldig = ny tilfeldig (); privat liste data; privat char currentItem; privat int currentPosition = -1; privat int Kapasitet get return data.Capacity;  offentlig int Størrelse get return data.Count;  offentlig ShuffleBag (int initCapacity) data = ny liste (initCapacity); 

Begynnelsen av klassen setter opp instansvariablene, og konstruktøren initialiserer datatilfellevariabelen til programmererens innledende kapasitet (dvs. hvor stor posen skal begynne med).

 offentlig tomrom Legg til (char item, int amount) for (int i = 0; i < amount; i++) data.Add(item); currentPosition = Size - 1; 

De Legg til Metoden legger ganske enkelt til røye til data så mange ganger som programmøren spesifiserer.

Legg merke til at nåværende posisjon er satt til slutten av listen, som vi vil krysse fra slutten senere. Hvorfor fra slutten av listen? Du kan få Shuffle Bag å gå fra begynnelsen, men starter på slutten og arbeider bakover, gir renere kode.

 offentlig charme Neste () hvis (nåværende posisjon < 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 neste Metoden er kjøttet i denne teknikken.

Hvis nåværende posisjon er mindre enn en, tilbakestiller vi den for å peke til slutten av listen og returnere det første elementet fra posen. (Dette dekker situasjonen der vi har krysset gjennom alle elementene og nå ønsker å starte igjen.)

Ellers bruker vi random.Next () å plukke et tilfeldig element fra posen, fra et sted mellom det første elementet og varen på vår nåværende posisjon. Vi bytter dette tilfeldig valgte elementet med varen i vår nåværende stilling, og deretter reduseres nåværende posisjon med 1.

Til slutt returnerer vi det tilfeldig valgte elementet. Resultatet er at vi fortsetter å plukke varer vi ikke har plukket før, mens du sparer posen mens vi går. Dette betyr at innholdet er i en annen rekkefølge når vi vil krysse den igjen.

Nå er det på tide å prøve ut vår nyopprettede klasse.


Bruke Shuffle Bag Class

For flere år siden opprettet jeg en Boggle klone for iPhone.


Bilde kreditt: Rich Brooks

Et problem jeg møtte var å lage tette brett som bare brukte 16 bokstaver, men tillot brukeren å danne hundrevis av ord med de 16 bokstavene. Jeg lærte om brevfrekvenser, og hvordan jeg kunne bruke den til å skape en positiv brukeropplevelse.

Ved å bruke frekvensen som bokstaver vises i engelsk tekst kan vi konstruere en vektet ordbok.

 private statiske ordbokstavsfrekvenser = ny ordbok 'E', 12.702, 'T', 9.056, 'A', 8.167, 'O', 7.507, 'I', 6.966, 'N', 6.769 , 'S', 6,337, '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; // totalt: 99.965

Merk: Q håndteres litt annerledes enn de andre bokstavene. Den beholder verdien fra brevfrekvensbordet, men det ser ut som Qu i mange ordspill.

Nå kan vi lage en forekomst av vår Shuffle Bag-klassen, fylle vår Shuffle Bag med data og lage tette Boggle Boards.

 statisk tomrom Hoved (streng [] args) var shuffleBag = ny ShuffleBag (88000); int mengde = 0; foreach (var brev i letterFrequencies) amount = (int) letter.Value * 1000; shuffleBag.Add (letter.Key, amount);  for (int i = 0; i < 16; i++) Console.Write(shuffleBag.Next()); Console.WriteLine(); 

Merk: Det viktigste å ta bort fra dette stykke kode er beløp. En multiplikator på 1000 returnerer bedre resultater enn en multiplikator på 10.

Kjør resultatene gjennom en online-løsning. Hvor mange ord finner du?


Konklusjon

I denne artikkelen erkjente vi problemet ved å bruke tilfeldige verdier med hvis / annet betingelser, introduserte vi en løsning ved hjelp av Shuffle Bags, og viste en bruk ved å skape tette Boggle Boards. Med bruk av Shuffle Bags tar vi kontroll over Tilfeldig() metoder og skape en jevn fordeling av verdier som hjelper i en positiv spillopplevelse.