|
-
Oct 27th, 2008, 11:24 PM
#1
[.NET 2.0+] Rolling Dice
C# version here.
Quite a few people have asked how to "roll dice" for games. Here's the "proper" way. First you define a Die class to represent a die.
vb.net Code:
Public Class Die Private Shared faceSelector As New Random Private _faceCount As Integer Private _value As Integer Public Property FaceCount() As Integer Get Return Me._faceCount End Get Set(ByVal value As Integer) Me._faceCount = value End Set End Property Public ReadOnly Property Value() As Integer Get Return Me._value End Get End Property Public Sub New(ByVal faceCount As Integer) If faceCount <= 0 Then Throw New ArgumentOutOfRangeException("faceCount", "Dice must have one or more faces.") End If Me._faceCount = faceCount End Sub Public Function Roll() As Integer Me._value = faceSelector.Next(1, Me.FaceCount + 1) Return Me.Value End Function End Class
Note that the Die class has a Shared variable of type Random. This means that there is one and only one Random object for the class. This same object is used to generate random sides for each and every instance of the class.
The Roll method generates the random number based on the number of faces the current die has. It returns this value and it is also available via the Value property thereafter.
Many games use multiple dice though, so it's appropriate to define a class that represents multiple Die objects.
vb.net Code:
Public Class DieCollection Inherits System.Collections.ObjectModel.Collection(Of Die) Public Function RollAll() As Integer() Dim values(Me.Count - 1) As Integer For index As Integer = 0 To values.GetUpperBound(0) values(index) = Me.Items(index).Roll() Next Return values End Function End Class
There's very little code to write for this class because it inherits all the standard collection functionality from the generic Collection(Of Die) class. Inherited functionality includes an Item property of type Die and an Add method that takes a Die parameter.
Here's an example of using these classes. As is supposed to be the case in OOP, using the types is dead simple because all the work is done inside the classes themselves. You simply create a DieCollection, create and add as many Die objects as you need and call RollAll:
vb.net Code:
'Create the collection to store the dice. Dim dice As New DieCollection 'Add two six-sided dice. dice.Add(New Die(6)) dice.Add(New Die(6)) 'Roll all the dice. Dim rolls As Integer() = dice.RollAll() 'Display the values rolled. For Each roll As Integer In rolls MessageBox.Show(roll.ToString(), "You rolled a ...") Next
Obviously for a game you would assign your DieCollection to a member variable and then call it's RollAll method each time a player had to roll the dice. You would not create a new DieCollection and Die objects each time you wanted to roll.
Last edited by jmcilhinney; Aug 31st, 2009 at 09:49 PM.
-
Nov 3rd, 2008, 07:40 PM
#2
Re: [.NET 2.0+] Rolling Dice
Actually, having thought about it seems more appropriate to me for the RollAll method to return the total of all the rolls.
vb.net Code:
Public Class DieCollection Inherits System.Collections.ObjectModel.Collection(Of Die) Public Function RollAll() As Integer Dim total As Integer = 0 For Each die As Die In Me.Items total += die.Roll() Next Return total End Function End Class
If you want the individual values you could loop through the dice and get their Value properties:
vb.net Code:
'Create the collection to store the dice. Dim dice As New DieCollection 'Add two six-sided dice. dice.Add(New Die(6)) dice.Add(New Die(6)) 'Roll all the dice. Dim total As Integer = dice.RollAll() 'Display the total rolled. MessageBox.Show(total.ToString(), "You rolled...") 'Display the values rolled. For Each die As Die In dice MessageBox.Show(die.Value.ToString(), "You rolled a...") Next
You could even add a property to the DieCollection that returned all the Values:
vb.net Code:
Public ReadOnly Property Values() As Integer() Get Dim upperBound As Integer = Me.Count - 1 Dim faces(upperBound) As Integer For index As Integer = 0 To upperBound faces(index) = Me.Items(index).Value Next Return faces End Get End Property
Last edited by jmcilhinney; Nov 3rd, 2008 at 07:43 PM.
-
Nov 17th, 2011, 10:16 PM
#3
Re: [.NET 2.0+] Rolling Dice
Having read this thread, I like the idea of using Dependency Injection as suggested by Evil_Giraffe. As a result, I have improved the code for my dice and added that functionality, along with some other additions.
vb.net Code:
''' <summary>
''' Represents a single die.
''' </summary>
Public Class Die
#Region " Fields "
''' <summary>
''' The number of faces a die will have by default, if not specified by the user.
''' </summary>
Public Shared ReadOnly DefaultFaceCount As Integer = 6
''' <summary>
''' The object used to select a die face.
''' </summary>
Private faceSelector As IRandomNumberGenerator
''' <summary>
''' The current face value of the die.
''' </summary>
Private _value As Integer
#End Region 'Fields
#Region " Properties "
''' <summary>
''' Gets or sets the number of faces the die has.
''' </summary>
''' <value>
''' An <b>Int32</b> containing the number of faces.
''' </value>
''' <remarks>
''' The default number of faces is defined by <see cref="DefaultFaceCount" />
''' </remarks>
Public Property FaceCount() As Integer
''' <summary>
''' Gets the current face value of the die.
''' </summary>
''' <value>
''' An <b>Int32</b> containing the current face value.
''' </value>
''' <remarks>
''' This would be the side facing upwards on a physical die.
''' </remarks>
Public ReadOnly Property Value() As Integer
Get
Return _value
End Get
End Property
#End Region 'Properties
#Region " Constructors "
''' <summary>
''' Creates a new instance of the <see cref="Die" /> class with the default number of faces and using the default random number generator.
''' </summary>
''' <remarks>
''' The default number of faces is defined by <see cref="DefaultFaceCount" />
''' </remarks>
Public Sub New()
Me.New(DefaultFaceCount)
End Sub
''' <summary>
''' Creates a new instance of the <see cref="Die" /> class using the default random number generator.
''' </summary>
''' <param name="faceCount">
''' The number of faces the die has.
''' </param>
Public Sub New(ByVal faceCount As Integer)
Me.New(faceCount, New RandomNumberGenerator)
End Sub
''' <summary>
''' Creates a new instance of the <see cref="Die" /> class with the default number of faces.
''' </summary>
''' <param name="randomNumberGenerator">
''' The object used to select a die face.
''' </param>
''' <remarks>
''' The default number of faces is defined by <see cref="DefaultFaceCount" />
''' </remarks>
Public Sub New(randomNumberGenerator As IRandomNumberGenerator)
Me.New(DefaultFaceCount, randomNumberGenerator)
End Sub
''' <summary>
''' Creates a new instance of the <see cref="Die" /> class.
''' </summary>
''' <param name="faceCount">
''' The number of faces the die has.
''' </param>
''' <param name="randomNumberGenerator">
''' The object used to select a die face.
''' </param>
Public Sub New(ByVal faceCount As Integer, randomNumberGenerator As IRandomNumberGenerator)
If faceCount <= 0 Then
Throw New ArgumentOutOfRangeException("faceCount", "Dice must have one or more faces.")
End If
Me.FaceCount = faceCount
If randomNumberGenerator Is Nothing Then
randomNumberGenerator = New RandomNumberGenerator()
End If
faceSelector = randomNumberGenerator
End Sub
#End Region 'Constructors
#Region " Methods "
Public Function Roll() As Integer
_value = faceSelector.GenerateRandomNumber(FaceCount)
Return Value
End Function
#End Region 'Methods
End Class
vb.net Code:
''' <summary>
''' A collection to store multiple <see cref="Die" /> objects.
''' </summary>
Public Class DieCollection
Inherits System.Collections.ObjectModel.Collection(Of Die)
#Region " Constructors "
''' <summary>
''' Creates a new, empty instance of the <see cref="DieCollection" /> class.
''' </summary>
Public Sub New()
End Sub
''' <summary>
''' Create a new instance of the <see cref="DieCollection" /> with the specified number of items.
''' </summary>
''' <param name="dieCount">
''' The initial number of items in the collection.
''' </param>
''' <remarks>
''' The initial items all have the default number of faces, as defined by the <see cref="Die.DefaultFaceCount" /> field, and use the default random number generator.
''' </remarks>
Public Sub New(dieCount As Integer)
For i = 1 To dieCount
Items.Add(New Die)
Next
End Sub
''' <summary>
''' Create a new instance of the <see cref="DieCollection" /> with the specified number of items.
''' </summary>
''' <param name="dieCount">
''' The initial number of items in the collection.
''' </param>
''' <param name="faceCount">
''' The number of faces each die has.
''' </param>
''' <remarks>
''' The initial items all use the default random number generator.
''' </remarks>
Public Sub New(dieCount As Integer, faceCount As Integer)
For i = 1 To dieCount
Items.Add(New Die(faceCount))
Next
End Sub
''' <summary>
''' Create a new instance of the <see cref="DieCollection" /> with the specified number of items.
''' </summary>
''' <param name="dieCount">
''' The initial number of items in the collection.
''' </param>
''' <param name="randomNumberGenerator">
''' The object used to select a die face.
''' </param>
''' <remarks>
''' The initial items all have the default number of faces, as defined by the <see cref="Die.DefaultFaceCount" /> field.
''' </remarks>
Public Sub New(dieCount As Integer, randomNumberGenerator As IRandomNumberGenerator)
For i = 1 To dieCount
Items.Add(New Die(randomNumberGenerator))
Next
End Sub
''' <summary>
''' Create a new instance of the <see cref="DieCollection" /> with the specified number of items.
''' </summary>
''' <param name="dieCount">
''' The initial number of items in the collection.
''' </param>
''' <param name="faceCount">
''' The number of faces each die has.
''' </param>
''' <param name="randomNumberGenerator">
''' The object used to select a die face.
''' </param>
Public Sub New(dieCount As Integer, faceCount As Integer, randomNumberGenerator As IRandomNumberGenerator)
For i = 1 To dieCount
Items.Add(New Die(faceCount, randomNumberGenerator))
Next
End Sub
#End Region 'Constructors
#Region " Methods "
''' <summary>
''' Rolls all the dice in the collection and returns the sum of the results.
''' </summary>
''' <returns>
''' An <b>Int32</b> containing the summed values of all dice.
''' </returns>
Public Function RollAll() As Integer
Return Items.Sum(Function(die) die.Roll())
End Function
#End Region 'Methods
End Class
Here is the interface and default implementation for dependency injection:
vb.net Code:
''' <summary>
''' Represents an object that can generate a random integer.
''' </summary>
Public Interface IRandomNumberGenerator
''' <summary>
''' Generates a random integer.
''' </summary>
''' <param name="max">
''' The maximum value (inclusive) that should be generated.
''' </param>
''' <returns>
''' An <b>Int32</b> containing a random number.
''' </returns>
''' <remarks>
''' The value returned should never be less than 1.
''' </remarks>
Function GenerateRandomNumber(max As Integer) As Integer
End Interface
''' <summary>
''' Represents an object that can generate a random integer.
''' </summary>
''' <remarks>
''' This class is the default random number generator for the <see cref="Die" /> class.
''' </remarks>
Public Class RandomNumberGenerator
Implements IRandomNumberGenerator
''' <summary>
''' The underlting random number generator.
''' </summary>
''' <remarks>
''' A single <b>Random</b> object is used by all instances to avoid duplicate results when multiple values are generated quickly.
''' </remarks>
Private Shared rng As New Random
''' <summary>
''' Generates a random number.
''' </summary>
''' <param name="max">
''' The maximum value (inclusive) that can be generated.
''' </param>
''' <returns>
''' An <b>Int32</b> containing a random number.
''' </returns>
''' <remarks>
''' The value returned will never be less than 1.
''' </remarks>
Public Function GenerateRandomNumber(max As Integer) As Integer Implements IRandomNumberGenerator.GenerateRandomNumber
If max < 1 Then
Throw New ArgumentOutOfRangeException("max", max, "Value cannot be less than 1.")
End If
Return rng.Next(1, max + 1)
End Function
End Class
If you want to create dice that behave randomly then you use one of the constructors that doesn't have a parameter of type IRandomNumberGenerator. In that case, an instance of the RandomNumberGenerator class will be used, which uses a shared Random object internally.
If you wanted your dice to produce known results for testing purposes then you could create your own implementation of the IRandomNumberGenerator interface and then pass an instance of that to one of the Die constructors that has a parameter of that type. Your dice will then produce the results you expect and you can test that your "game" behaves as expected under those circumstances.
-
Jan 9th, 2013, 04:26 PM
#4
New Member
Re: [.NET 2.0+] Rolling Dice
Why does everybody have to make stuff so complicated:
Code:
Public Function D(Optional DieNumber As Integer = 1, Optional DieSize As Integer = 6, Optional Modifier As Integer = 0) As Integer
Randomize()
Dim DieRoll As Integer = 0
For I As Integer = 1 To DieNumber
DieRoll += Int(Rnd() * (DieSize)) + Modifier
Next
Return DieRoll + DieNumber
End Function
-
Jan 9th, 2013, 05:22 PM
#5
Re: [.NET 2.0+] Rolling Dice
because 1) that's not very flexible, 2) it's using the older VB6-style methods, when the newer .net ones are actually superior, 3) quick successive calls to the method runs the risk of generating the same numbers (because of the call to Randomize()) ... and to be honest... I don't think it's all that complicated... and if I were developing a game that required the used of dice... I'd probably would go for something like this.
jmc - I'd go one step further and allow for a dictionary... then I can specify which die or dice I want to roll... sometimes I don't want the combined total either... Example I might roll two D10's where one is the designated 10's value and the other is the ones value... 1D10 * 10 + 2D10 = range from 0 - 99... also not all die start at 1 ... just saying. either way, having the dice in a dictionary would allow for the rolling of some either independently or in concert with others... but that's probably my inner geek gamer peeking through.
-tg
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|