Press "Enter" to skip to content

Lesson 15.1 – Getting random numbers for the game

Lesson Objectives

At the end of this lesson, you will know…

  • How to create random numbers – the easy way, and the good way

 

This game, like many games, needs to get random numbers.

We’ll use random numbers to determine how much damage a player does to a monster, how much damage the monster does to the player, and which items from the monster loot table the player will receive after defeating a monster.

However, there is something important that you need to consider when using random numbers.

The .Net framework has a built-in random number generating class, called “Random”. Unfortunately, it doesn’t produce numbers that are really random. In a simple game like this, that’s not a big problem. But if you were serious about building a good game, you’d want to use a better technique.

The problem with better technique is that it uses some complex objects and logic to generate the random numbers. These are way more complex than I can describe in a tutorial for beginners. In fact, some of the things in it are difficult for advanced programmers to understand.

So, if you want to use the better version, you’ll just have to take my word that it works.

I have both versions below. You can select either one to use in your version of the game.

 

Creating a random number generator

Step 1: Start Visual Studio Express 2013 for Desktop, and open the solution.

 

Step 2: Right-click on the Engine project and add a new class named RandomNumberGenerator.cs.

 

Step 3: Decide which method you want to use, and paste the code below into the RandomNumberGenerator class.

The code for the easy way:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace Engine
{
    public static class RandomNumberGenerator
    {
        private static Random _generator = new Random();
 
        public static int NumberBetween(int minimumValue, int maximumValue)
        {
            return _generator.Next(minimumValue, maximumValue + 1);
        }
    }
}

In the simple version, we create a Random object (_generator) and use its Next() method to get a random value between the minimum and maximum values passed in as parameter.

 

The code for the more random way:

using System;
using System.Security.Cryptography;
namespace Engine
{
    // This is the more complex version
    public static class RandomNumberGenerator
    {
        private static readonly RNGCryptoServiceProvider _generator = new RNGCryptoServiceProvider();
        public static int NumberBetween(int minimumValue, int maximumValue)
        {
            byte[] randomNumber = new byte[1];
            _generator.GetBytes(randomNumber);
            double asciiValueOfRandomCharacter = Convert.ToDouble(randomNumber[0]);
            // We are using Math.Max, and substracting 0.00000000001,
            // to ensure "multiplier" will always be between 0.0 and .99999999999
            // Otherwise, it's possible for it to be "1", which causes problems in our rounding.
            double multiplier = Math.Max(0, (asciiValueOfRandomCharacter / 255d) - 0.00000000001d);
            // We need to add one to the range, to allow for the rounding done with Math.Floor
            int range = maximumValue - minimumValue + 1;
            double randomValueInRange = Math.Floor(multiplier * range);
            return (int)(minimumValue + randomValueInRange);
        }
    }
}

In this version, we use an instance of an encryption class (RNGCryptoServiceProvider). This class is better at not following a pattern when it creates random numbers. But we need to do some more math to get a value between the passed in parameters.

 

Summary

Now you can create random numbers in the game.

We only use random numbers in a few places, in this simple version of the game. However, if you want to add more features, you may find this useful.

 

Source code for this lesson

Source code on GitHub

Source code on Dropbox

NOTE: This has the code for both versions of the class, the simple one and the complex one. Only create one RandomNumberGenerator class and copy the code from the one version that you want to use.

 

Next lesson: Lesson 16.1 – Writing the function to move the player

Previous lesson: Lesson 14.3 – Foreach loops

All lessons: Learn C# by Building a Simple RPG Index

35 Comments

  1. Jimmy
    Jimmy July 15, 2014

    There is no “Next lesson” link here.

    • Scott Lilly
      Scott Lilly July 15, 2014

      Thanks. I’ve got it in there now.

  2. Bill S.
    Bill S. October 9, 2015

    Sanity Check!

    In the first “easy way” example, is it not true that the parameter of “maximumValue”  for the NumberBetween function will never actually return that maximum value?( this is what I have found)

    IE… if you wanted a number between 1-10, your input parameters should be 1-11, or the return value function call should be…
    return _generator.Next(minimumValue, maximumValue+1);

    If this is True, does this also apply to the “more random way”?
           

    • Scott Lilly
      Scott Lilly October 9, 2015

      Aaaarrgh! You’re right about the “easy” one. That was missing a “+ 1”. The “complex” one has the “+ 1” on line 25, to take care of that problem. I always use the complex one, and that’s the one I’ve tested. I’ve corrected the code.

      Thanks for catching that one.

  3. Matthew
    Matthew December 23, 2015

    Not sure where to post this question, but its related to the RNG so i’ll just post it here.

    I am trying to create a spawn chance for monsters and include more than one monster at a location. An example would be I have a location: Castle Field, that has 2 monsters: Bear and Wolf. The wolf has a 60% chance to spawn (rng < 60 = spawn) and the bear has a 40% chance to spawn (rng < 40 = spawn). But I only want one monster to spawn at a time and I would like the random spawn to continue after a monster is killed (so I wouldn’t keep getting bear’s). Any help? I already have a property set up in the monster class called spawnRate.

    From my own thoughts on the subject I would place this random spawn thingy in two places (im thinking its just gonna be an if statement, however if you think there is a better way please share). Those two places are in the creation of the location in the world class or in the moveto function in the superadventure class.

     

    Thanks for all the help!

  4. Jörg
    Jörg June 26, 2016

    What is about the performance?

    I use the seed to bring a little bit more “random” to the value…

    private static Random _generator = new Random((int)DateTime.Now.ToBinary());

    • Scott Lilly
      Scott Lilly June 26, 2016

      In this situation, the difference in performance is not noticeable.

      Sometimes, you have several ways to do something and need to consider which one is best for the program. For this program, we don’t need to worry about performance, because it is a turn-based, single-player game. We also don’t need to have a powerful random number generator, like you would if you wrote an online casino program where people gambled with real money. So, you can use several different options, including using a datetime seed.

  5. Bandi
    Bandi August 1, 2016

    Hi Scott,

    I have a problem with understanding why do we use ‘static’ here. Could you explain it to me please?

    • Scott Lilly
      Scott Lilly August 1, 2016

      Hello Bandi,

      We did not really need to do this as a static method. I just didn’t want to instantiate a new object every time the game needed a random number. With a static method, you do not need to instantiate an object, to use the method. This is similar to getting the current date by calling “DateTime.Now”, without needing to instantiate a DateTime object. Because the NumberBetween method is static, all class-level variables it uses (_generator) also need to be static.

      If this was not static, the code to get a random number would look like this:
      RandomNumberGenerator randomGenerator = new RandomNumberGenerator();
      int randomValue = randomGenerator.NumberBetween(1, 2);

      Because it is static, we can get a random number with one line:
      int randomValue = RandomNumberGenerator.NumberBetween(1, 2);

      If this was a bigger program, or had many users, the static method would also help improve the performance (instantiating objects is relatively slow).

      Please tell me if that did not answer your question.

      • Bandi
        Bandi August 2, 2016

        Hello Scott,

        Thanks a lot for your answer, I understand it all now, it helped me a lot to understand the working of ‘static’.

        • Scott Lilly
          Scott Lilly August 2, 2016

          You’re welcome. Please tell me if you have any other questions.

  6. Alex Coronas
    Alex Coronas January 18, 2017

    I’ve already posted a comment talking about this on the index, yet i’ll ask here as i’m really curious.

    Why did you make a class just to hold one method? You could’ve placed the method inside the “World” class. Is there a reason for that?

    • Scott Lilly
      Scott Lilly January 18, 2017

      Normally, a class with one method is not good. However, I put this method in its own class for a few reasons.

      1. There is something called the “Single Responsibility Principle” – where each class should be responsible for one thing. Even with a larger class, such as the Player class, its “single responsibility” is to maintain the player’s “state” (information/status).
      2. A game will need random numbers in several different places (classes). I don’t want to duplicate this method in each class. Now, every other part of the program can use this single class/method, to generate random numbers.
      3. This is also a method I might need in other programs. Because it is in its own class, I can easily copy it to another program, and use it there. If I had the function in a larger class, I would need to remember which “using” statements need to be copied, if I ever copied the method to another project.

      Does that make it clearer?

      • Alex Coronas
        Alex Coronas January 19, 2017

        I see, it is for the Single Responsibility Principle. Thank you for the explanation!

        Your tutorials are really great by the way.

        • Scott Lilly
          Scott Lilly January 19, 2017

          You’re welcome! I’m glad you’re enjoying the tutorial.

  7. Rya
    Rya October 3, 2017

    Hi there, I’m just wondering why you need the +1?

    Many thanks

    • Scott Lilly
      Scott Lilly October 3, 2017

      If we don’t have the + 1, and we called the function to create a random number between 1 and 10, it would return values between 1.000000 and 9.9999999 (because it does not include the upper limit of 10 as a possible value). When we round that number to an integer, it loses the decimal values. So, we would only ever get values from 1 to 9.

      If we add 1 to the upper limit, and ask for a random number between 1 and 10, the possible random values are between 1.000000 and 10.999999. So, the rounded numbers will be between 1 and 10.

      Does that make it clear?

  8. NotARobot
    NotARobot January 31, 2018

    Maybe the question off the topic but what’s the point to do “private static”. Like as we said we use static used to share the function or methods with other classes without creating an object, working directly with static, but on the other hand private closes its range.. Too odd

    • NotARobot
      NotARobot January 31, 2018

      An example
      “private static readonly RNGCryptoServiceProvider _generator = new RNGCryptoServiceProvider();”

      • Scott Lilly
        Scott Lilly February 1, 2018

        We use “private” because we don’t want any other classes to be able to use the variable or function. It can only be used by other code inside the class.

        We use “static” because the variable is a class-level variable (it is outside of functions, and can be used by anything in the class), but it is being called by a static function. Static functions can only use variables created/instantiated inside the function, or class-level variables that are also static. We could move this variable inside the “NumberBetween” function, and it would not need to be set to static – because it is inside the static function. But, then we would create a new instance of the variable every time we called NumberBetween.

  9. NotARobot
    NotARobot February 1, 2018

    Hi Scott!
    Am i getting it right about the code :
    public static int NumberBetween(int minimumValue, int maximumValue)
    “maximumValue” and ” minimumValue” are sort of class or just variables that we’ll in the future ?

    • Scott Lilly
      Scott Lilly February 1, 2018

      They are just variables that we use inside the function.

  10. MrEdWORD
    MrEdWORD February 25, 2018

    Hi Scott.

    Can you explain the reasoning for using an underscore and lowercase / camel naming convention for the private static readonly property (_generator)? I haven’t seen this used in any of the previous lessons and am not sure why and when in the future I should follow the naming convention.

    I assume it’s because this is a private property, whereas in the previous lessons our properties were all public and use the uppercase / pascal convention without an underscore. However, when I search online for clarity about this approach, there’s conflicting documentation:
    – MSDN capitalization conventions says properties need to use Pascal and doesn’t mention the use of underscore (https://msdn.microsoft.com/en-us/library/ms229043%28v=vs.100%29.aspx)
    – 8 year old stackoverflow discussion says only use underscore+camel when a private property uses a public get/set (https://stackoverflow.com/questions/3136594/naming-convention-underscore-in-c-and-c-sharp-variables)
    – Some other more recent stackoverflow discussions have said to use the “this” keyword instead of underscore; others are arguing that underscore should never be used and it’s a relic of the past.

    A nuanced question, I know, but would appreciate getting your take on this since I’m struggling to find a definitive answer. Thank you!

    • Scott Lilly
      Scott Lilly February 26, 2018

      I would say that there is no definitive answer. Everyone has their own naming standard – and we all think ours is “the one correct way”. As a contract programmer, I’ve often had to adapt to each client’s standards.

      First, all naming conventions are intended to reduce “cognitive load”. When you see a variable/property/UI control/etc., you don’t want to spend brainpower trying to figure out what it is.

      My personal rules are:
      Don’t use “this”. To me, it doesn’t add any value. I already know the variable/property belongs to this object, because it isn’t preceded by “OtherObjectName.”
      Variables are camel-case.
      Class-level variables are also prefixed with an underscore.
      Class-level variables should be private. If I need to expose them to another class, I will use a property instead.
      Fields and properties are Pascal-case (although I rarely use fields, other than constants).
      UI controls have a Hungarian notation prefix (“lbl” for labels, “cbo” for comboboxes, etc.). Hungarian notation is considered bad, but I want a way to distinguish UI controls from properties and variables.

      So, “_generator” is not a property. It’s a class-level variable. It doesn’t have a getter and/or setter, which is what would make it a property. That’s why it follows my “class-level variable” convention, instead of my “property” convention.

      Here is a small sample of how I name things: https://gist.github.com/ScottLilly/df856e1a04efa48ae4b83f3f7b27a73d

      You’ll need to find a naming convention that works for you. Most programmers have similar standards, but there isn’t one “correct” standard – unless your paycheck depends on it. 🙂

      • MrEdWORD
        MrEdWORD February 27, 2018

        Thanks for sharing your personal rules and providing the example code via gist. This clarified everything for me. I really appreciate you for making this lesson and responding to everyone’s questions so timely and thoroughly. Cheers.

  11. Philip
    Philip August 18, 2018

    Could you please explan the “+1” on the line 15. Thanks, really nice guide.

    return _generator.Next(minimumValue, maximumValue + 1)

    • Scott Lilly
      Scott Lilly August 19, 2018

      You’re welcome.

      If you call “random.Next(1, 5)”, the Next function only returns values from 1 to 4 (inclusive). If we want to include 5 as a possible result, we need to call “random.Next(1, 6)”. This is probably because the Next function actually creates floating point numbers, and rounds them down. So, values from 9.00000000001 to 9.9999999999 will be rounded down to 9. But, the only value that could possibly be created that rounds down to 10 is 10.00000000 – which will be rare.

      Here’s a video to demonstrate running random.Next 10,000 times with parameter values of (1,5) and (1,6): https://youtu.be/Nq2WxIZpH3Q

  12. Danny
    Danny September 26, 2019

    RNGCryptoServiceProvider seems to be missing a reference. How do I install the reference to System.Security.Cryptography on VS2017?

    • Scott Lilly
      Scott Lilly September 26, 2019

      Hi Danny,

      Usually, if someone doesn’t have RNGCryptoServiceProvider available, they accidentally chose “Class Library (.NET Standard)” when they created the Engine project, instead of “Class Library (.NET Framework)”. If that’s the situation for you, it might be easiest to use the simpler random number generator (instead of trying to convert a .NET Standard project to a .NET Framework). The better random number isn’t really important for this game.

      If you want me to check your solution, can you upload your solution (including the directories under it, and all the files in those directories) to GitHub or Dropbox, so I can look at it?

  13. Lacika201
    Lacika201 April 5, 2021

    Hi! I am writing to you because i got an issue with the code.I am running Visual Studio 2017 and everything was okay but after the world class the programs says ” CS7036 There is no argument given that corresponds to the required formal parameter ‘rewardItem’ of ‘Quest.Quest(int, string, string, int, int, Item)’ ” What would you advice to me how should I fix it ? Anywasy keep up this great tutorial thing I am impressed.

    • Scott Lilly
      Scott Lilly April 5, 2021

      Hello,

      Can you upload your solution (including the directories under it, and all the files in those directories) to GitHub, Dropbox, or some other file-sharing location so I can look at it?

  14. Lacika201
    Lacika201 April 11, 2021

    Hi,

    Here is the program uploaded to Dropbox.
    LINK REMOVED FOR PRIVACY

    • Scott Lilly
      Scott Lilly April 11, 2021

      Hello,

      Check the Quest class against the one in step 4 of Lesson 10.1. The problem is that the Quest class should not have a parameter for rewardItem. In the World class, we don’t pass RewardItem in as a parameter. Instead, we add set the RewardItem property separately, on lines 92 and 101.

      Let me know if that wasn’t clear, or if you have any other questions.

Leave a Reply

Your email address will not be published. Required fields are marked *