Come migliorare questa generazione di numeri casuali nel mio contesto?


11

Nel mio gioco c'è una parola nella parte superiore dello schermo, le lettere piovono dall'alto e l'utente deve toccare le lettere per completare la parola.

Attualmente sto generando lettere in modo casuale (in realtà numeri e numeri casuali sono l'indice per l'array di lettere. Ad esempio: 0 = a, 1 = b) ma il problema è che ci vuole troppo tempo per ottenere tutte le lettere richieste per completare il parola.

Quello che voglio è che i numeri casuali che sto generando dovrebbero generare le lettere richieste più spesso in modo che il giocatore non debba spendere tutto il giorno per completare una parola.

Ho provato i seguenti metodi:

  1. Rileva tutte le lettere nella parola (la parola è sempre lunga 6 lettere), genera la matrice di indici di lunghezza 6, assegna ciascun indice della matrice a un numero casuale da lettera-2 a lettera + 2 e alla fine seleziona casualmente un indice dall'array per mostrare.

  2. Avere una variabile del selettore il cui valore è compreso nell'intervallo [0..2], generato casualmente, se il selettore == 0 rileva quindi le lettere che compongono la parola e sceglie casualmente una lettera, altrimenti prende casualmente qualsiasi alfabeto da az.

Entrambi questi metodi non mi hanno fornito alcun aiuto. Sarò molto felice se mi puoi aiutare.

Grazie per aver letto questo, spero che tu abbia capito la domanda e sto aspettando la risposta.


2
"entrambi questi metodi non mi hanno fornito alcun aiuto" Perché no? Cosa non ha funzionato con questi metodi?
Vaillancourt

Non so perché, ma ci vuole ancora troppo tempo come 1 minuto per ottenere tutti gli alfabeti richiesti.
Daniyal Azram,

@DaniyalAzram probabilmente dovresti aumentare ancora di più la frequenza se queste lettere non appaiono abbastanza spesso, poiché sembra che sia questo il tuo problema.
JFA,

Risposte:


21

In realtà non vuoi una distribuzione casuale. Lo sottolineo esplicitamente, perché ciò che consideriamo "casuale" per il design di solito non è vera casualità.

Ora, con questo in mente, aggiungiamo alcuni valori di modifica: queste sono cose con cui giocherellare fino a quando il design sembra "giusto".

ChooseLetter() {
    const float WordLetterProbability = 0.5f;
    if (Random.NextFloat01() < WordLetterProbability) {
        // Pick a random letter in the word
    }
    else {
        // Pick a random letter *not* in the word
    }
}

La probabilità controlla la probabilità che una determinata chiamata a ChooseLetter ti dia una lettera di parole: a 0,5, riceverai una lettera di parole all'incirca ogni due volte. A 0,25, uno su quattro sarà una lettera di parole, ecc.

Questo è ancora un po 'semplicistico - poiché la casualità è, beh, casuale , in realtà non hai una garanzia su quanto tempo passerai tra le lettere di parole. (In teoria, puoi andare per sempre senza una lettera, è molto improbabile.) Invece, possiamo aggiungere un fattore per aumentare la probabilità di una lettera ogni volta che non ne riceviamo una:

const float BaseWordLetterProbability = 0.5f;
const float WordLetterProbabilityIncrease = 0.25f;
float WordLetterProbability = BaseWordLetterProbability;
ChooseLetter() {
    if (Random.NextFloat01() < WordLetterProbability) {
        // Pick a random letter in the word
        WordLetterProbability = BaseWordLetterProbability;
    }
    else {
        // Pick a random letter *not* in the word
        WordLetterProbability += WordLetterProbabilityIncrease;
    }
}

Ok, quindi ora non passiamo mai più di due lettere senza una parola. (Perché, dopo due mancati, abbiamo una probabilità 1.0 di ottenere una lettera di parole.)

Infine, dobbiamo considerare come funziona la scelta della lettera in una parola. Per fornire al giocatore le lettere di cui ha effettivamente bisogno, dobbiamo rimuovere le lettere dal set man mano che le ricevono.

Ad esempio, se la parola è "test" e il giocatore ha già "s", non vogliamo dare loro un altro, perché non ne hanno bisogno!

Da qui, il resto sta modificando per adattarsi al tuo design.


Wow! come ti è venuto in mente?: p domani mattina testerò il tuo metodo e penso che funzionerà, fornirò più feedback una volta testato questo metodo. Grazie mille per tale risposta.
Daniyal Azram,

4
Le statistiche! Sia come un game designer o un programmatore, le statistiche sono molto apprendimento vale la pena. Per lo meno, la comprensione della probabilità (e come combinarli) è estremamente utile.
ACEfanatic02

1
Vieni a suggerire la stessa cosa. Bella risposta. Inoltre, tieni presente che una soluzione come questa potrebbe essere un modo per introdurre livelli di difficoltà. Facile = 0,5, medio = 0,25, duro = 0,10. ecc.
Tasos,

Non sono d'accordo che questo non sia casuale. Questo è casuale, è solo una distribuzione a tratti mentre di solito pensiamo alla distribuzione uniforme. E per aggiungere alla tua idea andrei oltre il semplice "Scegli una lettera casuale nella parola", la distribuirei per le lettere che si presentano maggiormente. Ad esempio, "Mississippi" ha bisogno di più se ne scelgo più.
Blaine,


21

Potresti ponderare la probabilità di tutte le tue lettere in base alla frequenza con cui si presentano nella lingua in cui si trovano le tue parole. Una buona linea guida è l'insieme scrabble . La versione inglese, ad esempio, ha 12 E ma solo una Z e una Q.

Un modo semplice per implementare questo è mettere tutte le lettere in una stringa consecutiva con ogni lettera che appare tutte le volte che lo si desidera e quindi fare in modo che l'RNG prenda una lettera da una posizione casuale. Esempio di pseudocodice:

const String letters = "AAAAAAAAABBCCDDDDEEEEEEEEEEEEFFGGGHHIIIIIIIIIJ/*...and so on...*/"

char randomLetter = letters[randomIntegerBetween(0, letters.length - 1)];

2
+1 Questo è un buon concetto, ma sospetto che ci sia un'implementazione più elegante.
Evorlor,


Per una distribuzione più dettagliata, è possibile memorizzare una tabella di frequenze delle lettere ridimensionata in modo che si sommino a 1, generino un numero casuale da 0 a 1 e si sovrapponga alla tabella sottraendo la frequenza di ciascuna lettera dal numero casuale fino a quando non viene diventa zero o negativo. Puoi anche ottimizzarlo ordinando prima le lettere più comuni nella tabella. Oppure usa qualcosa come il metodo alias per evitare di eseguire il looping completo sulla tabella.
Ilmari Karonen,

4
Questo non risolverebbe il problema. Il problema è che l'utente ha bisogno di lettere specifiche per completare il gioco. Di 'che hanno bisogno di una Z? Cosa poi? Ciò renderebbe le lettere rare più difficili per rendere l'utente ancora più frustrato.
AmazingDreams,

@AmazingDreams sottolinea bene, ma possiamo modificarlo un po 'così assegnerò più peso agli alfabeti richiesti e meno peso agli altri. Devo dire che questo è un ottimo concetto da seguire.
Daniyal Azram,

4

Ecco un modo per migliorarlo usando un singolo parametro kche puoi modificare.

Invece di scegliere semplicemente una lettera casuale:

  1. scegli una lettera a caso A
  2. scegli un numero casuale X
  3. se X > k e A non è presente [list of remaining needed letters], riprovare a 1.

Più piccola kè, più spesso la lettera finale Asarà quella effettivamente necessaria.

Per modificare l'algoritmo, gioca con qualsiasi valore k, ad es k = 0.5 . Se ritieni che il gioco sia troppo difficile, prova 0.4invece, finché non trovi un valore ragionevole. Questo ti dà anche un'impostazione di difficoltà , che ad esempio potresti voler aumentare man mano che il giocatore avanza nel gioco.


Grazie per la risposta, ma questa sembra proprio la risposta di ACEfanatic02.
Daniyal Azram,

3

Un modo semplice per garantire che le lettere richieste vengano visualizzate entro un determinato tempo consiste nell'utilizzare riempire un array con le lettere nella parola e il resto dell'alfabeto (forse ripetute), quindi mescolare casualmente l'array (c ++ ha std :: random_shuffle nella libreria standard, se stai usando una lingua diversa non è difficile da implementare).

Se si desidera che le lettere nella parola vengano visualizzate più rapidamente, inserire più copie delle lettere nella matrice.


0

Se stai usando C ++ puoi usare una distribuzione già esistente http://en.cppreference.com/w/cpp/numeric/random/piecewise_constant_distribution

Inoltre ha ammortizzato la complessità temporale costante che è migliore dei metodi ingenui. esempio:

#include <iostream>
#include <string>
#include <map>
#include <random>
#include <numeric>

int main()
{
    constexpr double containedLetterWeight = 3.0;
    constexpr int iterations = 10000;
    std::string word = "DISTRIBUTION";

    std::mt19937 rng(123);

    std::vector<double> values('Z' - 'A' + 2);
    std::iota(values.begin(), values.end(), 0.0);

    std::vector<double> weights('Z' - 'A' + 1, 1.0);
    for(const auto& c : word) weights[c - 'A'] = containedLetterWeight;

    std::piecewise_constant_distribution<> dist(values.begin(), values.end(), weights.begin());

    std::map<char, int> results;
    for(int n = 0; n < iterations; ++n)
    {
        ++results[static_cast<char>(dist(rng)) + 'A'];
    }
    for(const auto& p : results)
    {
        std::cout << p.first << ' ' << static_cast<float>(p.second) / iterations << '\n';
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.