0

So first off here's the code. The random declaration is on line 3 and the random usage is on line 15 or so.

public static void wildPokemonEncounter(string pokemonRegion)
{
    Pokemon wildPokemonEncountered = null;
    Random rnd = new Random();
    int possiblePokemon = 0;

    foreach (KeyValuePair<string, pokemonStats> kvp in GameReference.pokemonInformation)
    {
        if (kvp.Key != "Bublbasuar" && kvp.Key != "Squirtle" && kvp.Key != "Charmander")
        {
            if (pokemonRegion == "Plain")
            {

                if ((kvp.Value.typeOfPokemon == "Plant" || kvp.Value.typeOfPokemon == "Normal" || kvp.Value.typeOfPokemon == "Bug") && kvp.Value.evolutionNumber==1)
                {
                    possiblePokemon += 1;
                }
            }
        }
    }
    int whichPokemon = rnd.Next(1, possiblePokemon);
    int i = 1;
    Console.WriteLine(possiblePokemon+" "+ i + " " + whichPokemon);
    foreach (KeyValuePair<string, pokemonStats> kvp in GameReference.pokemonInformation)
    {
        if (kvp.Key != "Bulbasuar" && kvp.Key != "Squirtle" && kvp.Key != "Charmander")
        {
            if (pokemonRegion == "Plain")
            {
                pokemonStats thisPokemonsStats = GameReference.pokemonInformation[kvp.Key];
                if ((thisPokemonsStats.typeOfPokemon == "Plant" || thisPokemonsStats.typeOfPokemon == "Normal" || thisPokemonsStats.typeOfPokemon == "Bug") && thisPokemonsStats.evolutionNumber == 1)
                {
                    if (i == whichPokemon)
                    {
                        wildPokemonEncountered = new Pokemon(kvp.Key, kvp.Value);
                        slowTyper("`");
                        slowTyper("You found a Pokemon! Its a " + wildPokemonEncountered.name + "! Its level " + wildPokemonEncountered.level + " and of the type " + wildPokemonEncountered.typeOfPokemon + ".~");
                        Battle B = new Battle();
                        slowTyper("You enter into battle with the opposing Pokemon.");
                        Pokemon your_active_pokemon = null;
                        foreach (Pokemon pok in GameReference.pokemonInBag)
                        {
                            if (pok.is_Starting_Pokemon == true)
                            {
                                your_active_pokemon = pok;
                            }
                        }
                        B.PokemonBattle(wildPokemonEncountered, your_active_pokemon);
                        break;
                    }
                    else
                    {
                        i += 1;
                    }
                }
            }
        }
    }
}

I've tested just putting in a while true loop in order to check if for some reason the random just wasn't working in this area of the code but it worked. For some reason, though, every single time I run it, I get a four causing a Rattata to spawn. Any ideas?

  • 2
    This happens quite often. new Random() uses the time to initialize it. This is good enough, except you call it in a loop - and then get a lot of loops that will all give you exact the same sequence, an always get the first number from that. You better move that out. Given that your method is static, declare a static variable OUTSIDE and intiialize rnd ONCE when the class loads, then every call to Random() will give you a further number in the sequence. Right now it would give you the next number - after somewhere between 0 and 15ms, which is a LOT of loops. – TomTom Mar 03 '20 at 16:16
  • Note that this particular "feature" of `Random()` has been fixed in .Net Core. – Matthew Watson Mar 03 '20 at 16:17
  • @Pac0 - The `Random` class takes its seed from the a time dependent value if you use the default constructor. – Sean Mar 03 '20 at 16:18
  • 1
    @TomTom - it doesn't explain why he always gets 4 every time. `Random` is seeded based on the current time so there is a degree of randomness when we creates the object. – Sean Mar 03 '20 at 16:21
  • 1
    By the way, the `rnd.Next` function is called outside of the for loops and each time it is with a `new Random()` object. I can't see how this is a duplicate or how his problem is solved. Voting for re-opening. – Athanasios Kataras Mar 03 '20 at 16:21
  • I think, since the random number is calculated based on the function of time, what would happen is the random would be generated and start making random numbers. Then, x nanoseconds later, the code would reach the statement that actually uses said random. Every time the computer ran the code, the computer would reach that line at about the same time, causing the random to always give the same number. This is just a text based game, and not much else is running on the pc, so the load on the processor should be pretty consistent, allowing this to occur. – Andrewbandrew05 Mar 03 '20 at 16:36
  • OP has confirmed that initializing `Random` object just once resolves the problem. So even lacking a good [mcve], it's clear this is a duplicate of the standard "same seed for multiple `Random` objects" issue. – Peter Duniho Mar 03 '20 at 17:00

1 Answers1

2

There are multiple stackoverflow sources, mentioning that creating a new Random() too close in time, results in pretty much the same seed (I could not verify this from the documentation)

Random number generator only generating one random number

Others say that if a single millisecond has passed, you are ok: How much does new Random() depends on time?

In practice the only thing that I can think of that will solve your problem, is to indeed use a static Random variable like this:

private static Random rnd = new Random();

public static void wildPokemonEncounter(string pokemonRegion)
{
    Pokemon wildPokemonEncountered = null;

    int possiblePokemon = 0;

    foreach (KeyValuePair<string, pokemonStats> kvp in GameReference.pokemonInformation)
    {
        if (kvp.Key != "Bublbasuar" && kvp.Key != "Squirtle" && kvp.Key != "Charmander")
        {
            if (pokemonRegion == "Plain")
            {

                if ((kvp.Value.typeOfPokemon == "Plant" || kvp.Value.typeOfPokemon == "Normal" || kvp.Value.typeOfPokemon == "Bug") && kvp.Value.evolutionNumber==1)
                {
                    possiblePokemon += 1;
                }
            }
        }
    }
    int whichPokemon = rnd.Next(1, possiblePokemon);
    int i = 1;
    Console.WriteLine(possiblePokemon+" "+ i + " " + whichPokemon);
    foreach (KeyValuePair<string, pokemonStats> kvp in GameReference.pokemonInformation)
    {
        if (kvp.Key != "Bulbasuar" && kvp.Key != "Squirtle" && kvp.Key != "Charmander")
        {
            if (pokemonRegion == "Plain")
            {
                pokemonStats thisPokemonsStats = GameReference.pokemonInformation[kvp.Key];
                if ((thisPokemonsStats.typeOfPokemon == "Plant" || thisPokemonsStats.typeOfPokemon == "Normal" || thisPokemonsStats.typeOfPokemon == "Bug") && thisPokemonsStats.evolutionNumber == 1)
                {
                    if (i == whichPokemon)
                    {
                        wildPokemonEncountered = new Pokemon(kvp.Key, kvp.Value);
                        slowTyper("`");
                        slowTyper("You found a Pokemon! Its a " + wildPokemonEncountered.name + "! Its level " + wildPokemonEncountered.level + " and of the type " + wildPokemonEncountered.typeOfPokemon + ".~");
                        Battle B = new Battle();
                        slowTyper("You enter into battle with the opposing Pokemon.");
                        Pokemon your_active_pokemon = null;
                        foreach (Pokemon pok in GameReference.pokemonInBag)
                        {
                            if (pok.is_Starting_Pokemon == true)
                            {
                                your_active_pokemon = pok;
                            }
                        }
                        B.PokemonBattle(wildPokemonEncountered, your_active_pokemon);
                        break;
                    }
                    else
                    {
                        i += 1;
                    }
                }
            }
        }
    }
}

Hopefully someone can verify the one or the other and explain the same values for you.

Edit 1

Turn out both answers were correct based on the fiddle here

using System;
using System.Collections.Generic;

public class Program
{
    private static HashSet<int> randomValues = new HashSet<int>();

    public static void Main()
    {
        for(int i = 0; i < 2000; i++)
        {
            wildPokemonEncounter();
        }

        Console.WriteLine("1st iteration results");
        foreach(int rnd in randomValues) {
            Console.WriteLine(rnd);
        }

        randomValues.Clear();

        for(int i = 0; i < 20000; i++)
        {
            wildPokemonEncounter();
        }

        Console.WriteLine("2nd iteration results");
        foreach(int rnd in randomValues) {
            Console.WriteLine(rnd);
        }
    }

    //static Random rnd = new Random();

    public static void wildPokemonEncounter()
    {
        int possiblePokemon = 4;
        Random rnd = new Random();
        int whichPokemon = rnd.Next(1, possiblePokemon);
        if(!randomValues.Contains(whichPokemon)) {
            randomValues.Add(whichPokemon);
        }


    }
}

Results:

1st iteration results 3 2nd iteration results 3 1 2

It's just that it is so fast that all iterations are complete in the 1st millisecond.

Athanasios Kataras
  • 25,191
  • 4
  • 32
  • 61