0
public static void goblinAttack()
{
    Random rdAttack = new Random();
    int attack = rdAttack.Next(1, 5);
               
    if (attack == 1)
    {
        //case1
    }
    if (attack == 2)
    {
        //case2
    }
    if (attack == 3)
    {
        //case3
    }
    if (attack == 4)
    {
        //case4
    }
    if (attack == 5)
    {
        //case5
    }
                
    //Help (possible way to empty int here?)

}

I have "goblinAttack" in a while loop. and I am trying to have int "rdattack" to be re rolled every time the "goblinAttack" function is called, so instead of "goblinAttack" always being set to the first random number, it will empty the integer so that it can re roll a new number every time. if that isn't even possible, is there another way to approach this? thank you for the help!

wohlstad
  • 12,661
  • 10
  • 26
  • 39
  • 1
    `rdAttack` could be a member of your class, and be `new`ed only once. As long as your method is `static` (maybe it can be turned into a non-static one ?), it should also be a `static` member. – wohlstad Aug 22 '23 at 05:10
  • 1
    Not sure what does "empty int" (nullable?) means but it will probably work better if the `rdAttack` be created once outside of this method and only used in it – Paweł Łukasik Aug 22 '23 at 05:11
  • Your `attack` variable is local to that method, so it *is* recreated and assigned every time you call that method. However, in .Net Framework creating a new Random instance in a tight loop may lead to the same random number being generated – Hans Kesting Aug 22 '23 at 05:33
  • 1
    FYI: `rdAttack.Next(1, 5)` produces values 1 to 4 (never 5). – Enigmativity Aug 22 '23 at 05:42

3 Answers3

2

If I am not wrong, you can achieve what you want by moving the Random initialization outside of the goblinAttack function and then using it to generate a new random number each time the function is called.

// Declare a class-level Random object so that it's not recreated every time
private static Random rdAttack = new Random();

public static void goblinAttack()
{
    int attack = rdAttack.Next(1, 5); 
    switch (attack)
    {
        case 1:
            // Handle case 1
            break;
        case 2:
            // Handle case 2
            break;
        case 3:
            // Handle case 3
            break;
        case 4:
            // Handle case 4
            break;
        case 5:
            // Handle case 5
            break;
        default:
            // Handle unexpected values if necessary
            break;
    }
}
Amit Mohanty
  • 387
  • 1
  • 9
1

Your problem is that you create a new object of the class Random every time you enter the function. First of all, this is wasteful: The garbage collector at some point must destroy all the lost instances of Random which will slow down your game. Try to avoid creating new objects and re-use existing ones as much as possible.

In this particular case, this is also the underlying reason that you get the same "random" number every time: Random is a misnomer and should have been called PseudoRandom or the like instead. Subsequent calls of Random.Next() for the same Random object yield a pre-determined sequence of numbers which just look as if they were random. There is a large number of sequences to choose from with the constructor's integer argument, when supplied. If you don't supply one, the class uses a timer tick, and that's the catch. Quoting from the Microsoft documentation (hint ;-) ):

However, on the .NET Framework only, because the clock has finite resolution, using the parameterless constructor to create different Random objects in close succession creates random number generators that produce identical sequences of random numbers.

"Finite resolution" means that if the time between calls to the function is smaller than the timer interval, the timer value used to choose the specific number sequence can be the same for two or more calls. This is probably the case here. Your Next() call always returns only the first number of the pre-determined sequence this specific new Random object produces; two Random objects producing the same sequence will return the same number when Next() is called on them for the first time.

The "proper" way to use Random would be to use the same object every time. For example, create a Random object before your loop and supply it to the function in each iteration. Then Next() indeed will return the "next" number in the pre-determined, random-looking sequence chosen by the (more or less unpredictable) timer value at the time of the instantiation of the one Random object. A bit further down in the mentioned article Microsoft recommends exactly that:

On the .NET Framework, initializing two random number generators in a tight loop or in rapid succession creates two random number generators that can produce identical sequences of random numbers. In most cases, this is not the developer's intent and can lead to performance issues, because instantiating and initializing a random number generator is a relatively expensive process.

Both to improve performance and to avoid inadvertently creating separate random number generators that generate identical numeric sequences, we recommend that you create one Random object to generate many random numbers over time, instead of creating new Random objects to generate one random number.

Addendum:

When I asked ChatGPT about your program it spotted an error that I didn't know about: The second parameter to the Next() call, even though it is called maxValue, is the exclusive upper bound. That is, Next(1,5) will produce 1, 2, 3 or 4, but never 5.

As an aside, when I then told ChatGPT that I always get the same "random" number, ChatGPT spotted the "same sequence" problem correctly as well.

Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
  • Please note that the latest version of .NET do NOT suffer from this issue. – Enigmativity Aug 22 '23 at 05:41
  • @Enigmativity There seems to be a difference between .NET *framework* and *core*. Even the 8.7 *framework* preview [suffers from this issue](https://learn.microsoft.com/en-us/dotnet/api/system.random?view=net-8.0). Core, by contrast, if I understand correctly, instantiates a static `Random` class automatically and uses its sequence as subsequent seeds for further `Random` instances. – Peter - Reinstate Monica Aug 22 '23 at 05:49
0

I don't know if I understand correctly but you could always set the number to 0?

Also to roll it again you would just call: attack = rdAttack.Next(1, 5);, so you roll another number...