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
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
There is no “Next lesson” link here.
Thanks. I’ve got it in there now.
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”?
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.
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!
I started work on this feature in a larger version of this game – which I will get back to working on next month.
Look at the Location class here: https://github.com/ScottLilly/ScottsOpenSourceRPG/blob/master/Engine/Entities/Location.cs
You can add potential monsters to a location, with their chance of appearance, with the AddPotentialMonster() function. This saves them to a RandomDistributionList variable – a custom class I wrote to easily manage getting a random item from a weighted/percentage list.
If you use this method, you would also need to include the RandomDistributionList class (https://github.com/ScottLilly/ScottsOpenSourceRPG/blob/master/Engine/Collections/RandomDistributionList.cs) in your Engine project (and change its namespace from “Engine.Collections” to “Engine”)
Let me know if that makes sense, and if you have any trouble adapting your project to use this technique (or if you try some other way).
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());
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.
Hi Scott,
I have a problem with understanding why do we use ‘static’ here. Could you explain it to me please?
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.
Hello Scott,
Thanks a lot for your answer, I understand it all now, it helped me a lot to understand the working of ‘static’.
You’re welcome. Please tell me if you have any other questions.
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?
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?
I see, it is for the Single Responsibility Principle. Thank you for the explanation!
Your tutorials are really great by the way.
You’re welcome! I’m glad you’re enjoying the tutorial.
Hi there, I’m just wondering why you need the +1?
Many thanks
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?
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
An example
“private static readonly RNGCryptoServiceProvider _generator = new RNGCryptoServiceProvider();”
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.
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 ?
They are just variables that we use inside the function.
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!
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. 🙂
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.
You’re welcome!
Could you please explan the “+1” on the line 15. Thanks, really nice guide.
return _generator.Next(minimumValue, maximumValue + 1)
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
RNGCryptoServiceProvider seems to be missing a reference. How do I install the reference to System.Security.Cryptography on VS2017?
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?
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.
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?
Hi,
Here is the program uploaded to Dropbox.
LINK REMOVED FOR PRIVACY
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.