Shuffle Bags Random () Più sensato

Un generatore di numeri pseudo casuali (PRNG) come il Casuale La classe in C # non è un vero generatore di numeri casuali: il suo obiettivo è approssimare la casualità con la velocità. Ciò significa che restituirà spesso una distribuzione non uniforme dei valori, che potrebbe non essere quello che desideri. In questo tutorial, ti mostrerò come risolvere questo problema con a shuffle bag.

Nota: Sebbene questo tutorial usi C #, dovresti essere in grado di utilizzare le stesse tecniche e concetti in quasi tutti gli ambienti di sviluppo di giochi.


introduzione

Quando ho iniziato a creare giochi, ho usato lo standard Casuale() metodi per creare varietà nel gameplay, creando grandi dimensioni se altro condizioni fino a quando ho ricevuto i risultati desiderati. Se i risultati non fossero equilibrati nel modo in cui li volevo, creerei condizioni aggiuntive finché non sentissi che il gameplay era divertente. Non è stato fino a poco tempo fa ho capito che ci sono approcci migliori nel creare un'esperienza di gioco davvero divertente.

Non c'è niente di sbagliato nell'uso del built-in Casuale classe: il problema di non ottenere i risultati desiderati deriva dall'implementazione dei metodi. In questo articolo useremo il metodo "Shuffle Bag" per fare Casuale() si sentono più casuali (e più divertenti), usando le schede di Boggle come esempio pratico.


Il problema

Hai mai notato che un'espressione come:

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

... ti dà una distribuzione irregolare dei numeri?

Un generatore di numeri casuali che seleziona valori compresi tra 0 e 1 non si preoccupa se restituisce tutti gli 1, quindi se hai creato un se altro blocca usando l'espressione sopra per scegliere un ramo, probabilmente non otterrai i risultati che ti aspetti.

 var rand = new Random (); per (int i = 0; i < 10; i++) Console.WriteLine(rand.Next(0, 2));

Non c'è niente di tecnicamente sbagliato Random.Next (), ma non garantisce una distribuzione uniforme dei numeri. Ciò significa che in molte situazioni di gioco, Casuale() non è divertente.


Cos'è una borsa Shuffle?

Una Shuffle Bag è una tecnica per controllare la casualità per creare la distribuzione che desideriamo. L'idea è:

  • Scegli un intervallo di valori con la distribuzione desiderata.
  • Metti tutti questi valori in una borsa.
  • Mescolare il contenuto della borsa.
  • Estrarre i valori uno alla volta fino a raggiungere la fine.
  • Una volta raggiunta la fine, ricomincia, estraendo i valori uno alla volta.

Implementazione di una borsa Shuffle

Implementare una Shuffle Bag in C # è semplice e la tecnica può essere facilmente convertita in qualsiasi lingua.

Poiché lo scopo di questo articolo è di concentrarsi sull'implementazione di Shuffle Bags e non sulle funzionalità linguistiche, non considereremo l'uso dei generici. Tuttavia, raccomando vivamente l'uso di generici perché ci permettono di rendere le strutture di dati di tipo sicuro senza impegnarci in tipi di dati reali. Generics ti consente di utilizzare lo stesso codice per creare Shuffle Bags che contengono molti tipi diversi di dati.

Ecco il codice di base:

 public class ShuffleBag private Random random = new Random (); Elenco privato dati; codice di proprietà privata; private int currentPosition = -1; private int Capacity get return data.Capacity;  public int Size get return data.Count;  public ShuffleBag (int initCapacity) data = new List (initCapacity); 

L'inizio della classe imposta le variabili di istanza e il costruttore inizializza la variabile dell'istanza di dati sulla capacità iniziale del programmatore (ovvero quanto è grande il bagaglio con cui iniziare).

 pubblico vuoto Aggiungi (elemento char, quantità int) for (int i = 0; i < amount; i++) data.Add(item); currentPosition = Size - 1; 

Il Inserisci il metodo aggiunge semplicemente il carbonizzare a dati tante volte quante il programmatore specifica.

Si noti che il posizione attuale è impostato alla fine dell'elenco, poiché attraverseremo la fine più tardi. Perché dalla fine della lista? Puoi fare la traversata del Shuffle Bag dall'inizio, ma iniziare alla fine e lavorare all'indietro rende il codice più pulito.

 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; 

Il Il prossimo il metodo è la carne di questa tecnica.

Se posizione attuale è inferiore a uno, lo abbiamo resettato per puntare alla fine della lista e restituire il primo oggetto dalla borsa. (Questo copre la situazione in cui abbiamo attraversato tutti gli elementi e ora desidero ricominciare.)

Altrimenti, usiamo random.Next () scegliere un oggetto a caso dalla borsa, da qualche parte tra il primo oggetto e l'oggetto nella nostra posizione corrente. Scambiamo questo articolo selezionato casualmente con l'oggetto nella nostra posizione attuale e quindi diminuiamo posizione attuale per 1.

Alla fine, restituiamo l'oggetto selezionato casualmente. Il risultato è che continuiamo a raccogliere oggetti che non abbiamo raccolto prima, mentre mescoliamo la borsa mentre andiamo. Ciò significa che i suoi contenuti sono in un ordine diverso quando vogliamo attraversarlo di nuovo.

Ora è il momento di provare la nostra lezione appena creata.


Usando la classe Shuffle Bag

Diversi anni fa ho creato un clone Boggle per l'iPhone.


Immagine di credito: Rich Brooks

Un problema che ho affrontato è stato la creazione di schede dense che utilizzavano solo 16 lettere, ma ha permesso all'utente di formare centinaia di parole con quelle 16 lettere. Ho imparato a conoscere le frequenze delle lettere e come posso usarlo per creare un'esperienza utente positiva.

Usando la frequenza con cui le lettere appaiono nel testo inglese possiamo costruire un dizionario ponderato.

 Dizionario statico privato letterFrequencies = nuovo dizionario '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; // totale: 99,965

Nota: Q viene gestito in modo leggermente diverso rispetto alle altre lettere. Mantiene il valore dalla tabella delle frequenze delle lettere, ma appare come Qu in molti giochi di parole.

Ora possiamo creare un'istanza della nostra classe Shuffle Bag, riempire la nostra Shuffle Bag con i dati e creare folte Boggle Boards.

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

Nota: La cosa più importante da togliere a questo pezzo di codice è il quantità. Un moltiplicatore di 1000 restituisce risultati migliori di un moltiplicatore di 10.

Esegui i risultati attraverso un risolutore online. Quante parole trovi??


Conclusione

In questo articolo, abbiamo riconosciuto il problema nell'utilizzo di valori casuali con se altro condizioni, abbiamo introdotto una soluzione utilizzando Shuffle Bags e dimostrato un utilizzo creando fitte schede Boggle. Con l'uso di Shuffle Bags prendiamo il controllo di Casuale() metodi e creare una distribuzione uniforme dei valori che aiuti in un'esperienza di gioco positiva.