Results 1 to 5 of 5

Thread: [.NET 2.0+] Rolling Dice

  1. #1
    .NUT jmcilhinney's Avatar
    Join Date
    May 05
    Location
    Sydney, Australia
    Posts
    80,768

    [.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:
    1. Public Class Die
    2.  
    3.     Private Shared faceSelector As New Random
    4.  
    5.     Private _faceCount As Integer
    6.     Private _value As Integer
    7.  
    8.     Public Property FaceCount() As Integer
    9.         Get
    10.             Return Me._faceCount
    11.         End Get
    12.         Set(ByVal value As Integer)
    13.             Me._faceCount = value
    14.         End Set
    15.     End Property
    16.  
    17.     Public ReadOnly Property Value() As Integer
    18.         Get
    19.             Return Me._value
    20.         End Get
    21.     End Property
    22.  
    23.     Public Sub New(ByVal faceCount As Integer)
    24.         If faceCount <= 0 Then
    25.             Throw New ArgumentOutOfRangeException("faceCount", "Dice must have one or more faces.")
    26.         End If
    27.  
    28.         Me._faceCount = faceCount
    29.     End Sub
    30.  
    31.     Public Function Roll() As Integer
    32.         Me._value = faceSelector.Next(1, Me.FaceCount + 1)
    33.         Return Me.Value
    34.     End Function
    35.  
    36. 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:
    1. Public Class DieCollection
    2.     Inherits System.Collections.ObjectModel.Collection(Of Die)
    3.  
    4.     Public Function RollAll() As Integer()
    5.         Dim values(Me.Count - 1) As Integer
    6.  
    7.         For index As Integer = 0 To values.GetUpperBound(0)
    8.             values(index) = Me.Items(index).Roll()
    9.         Next
    10.  
    11.         Return values
    12.     End Function
    13.  
    14. 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:
    1. 'Create the collection to store the dice.
    2. Dim dice As New DieCollection
    3.  
    4. 'Add two six-sided dice.
    5. dice.Add(New Die(6))
    6. dice.Add(New Die(6))
    7.  
    8. 'Roll all the dice.
    9. Dim rolls As Integer() = dice.RollAll()
    10.  
    11. 'Display the values rolled.
    12. For Each roll As Integer In rolls
    13.     MessageBox.Show(roll.ToString(), "You rolled a ...")
    14. 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.

  2. #2
    .NUT jmcilhinney's Avatar
    Join Date
    May 05
    Location
    Sydney, Australia
    Posts
    80,768

    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:
    1. Public Class DieCollection
    2.     Inherits System.Collections.ObjectModel.Collection(Of Die)
    3.  
    4.     Public Function RollAll() As Integer
    5.         Dim total As Integer = 0
    6.  
    7.         For Each die As Die In Me.Items
    8.             total += die.Roll()
    9.         Next
    10.  
    11.         Return total
    12.     End Function
    13.  
    14. End Class
    If you want the individual values you could loop through the dice and get their Value properties:
    vb.net Code:
    1. 'Create the collection to store the dice.
    2. Dim dice As New DieCollection
    3.  
    4. 'Add two six-sided dice.
    5. dice.Add(New Die(6))
    6. dice.Add(New Die(6))
    7.  
    8. 'Roll all the dice.
    9. Dim total As Integer = dice.RollAll()
    10.  
    11. 'Display the total rolled.
    12. MessageBox.Show(total.ToString(), "You rolled...")
    13.  
    14. 'Display the values rolled.
    15. For Each die As Die In dice
    16.     MessageBox.Show(die.Value.ToString(), "You rolled a...")
    17. Next
    You could even add a property to the DieCollection that returned all the Values:
    vb.net Code:
    1. Public ReadOnly Property Values() As Integer()
    2.     Get
    3.         Dim upperBound As Integer = Me.Count - 1
    4.         Dim faces(upperBound) As Integer
    5.  
    6.         For index As Integer = 0 To upperBound
    7.             faces(index) = Me.Items(index).Value
    8.         Next
    9.  
    10.         Return faces
    11.     End Get
    12. End Property
    Last edited by jmcilhinney; Nov 3rd, 2008 at 06:43 PM.

  3. #3
    .NUT jmcilhinney's Avatar
    Join Date
    May 05
    Location
    Sydney, Australia
    Posts
    80,768

    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:
    1. ''' <summary>
    2. ''' Represents a single die.
    3. ''' </summary>
    4. Public Class Die
    5.  
    6. #Region " Fields "
    7.  
    8.     ''' <summary>
    9.     ''' The number of faces a die will have by default, if not specified by the user.
    10.     ''' </summary>
    11.     Public Shared ReadOnly DefaultFaceCount As Integer = 6
    12.  
    13.     ''' <summary>
    14.     ''' The object used to select a die face.
    15.     ''' </summary>
    16.     Private faceSelector As IRandomNumberGenerator
    17.  
    18.     ''' <summary>
    19.     ''' The current face value of the die.
    20.     ''' </summary>
    21.     Private _value As Integer
    22.  
    23. #End Region 'Fields
    24.  
    25. #Region " Properties "
    26.  
    27.     ''' <summary>
    28.     ''' Gets or sets the number of faces the die has.
    29.     ''' </summary>
    30.     ''' <value>
    31.     ''' An <b>Int32</b> containing the number of faces.
    32.     ''' </value>
    33.     ''' <remarks>
    34.     ''' The default number of faces is defined by <see cref="DefaultFaceCount" />
    35.     ''' </remarks>
    36.     Public Property FaceCount() As Integer
    37.  
    38.     ''' <summary>
    39.     ''' Gets the current face value of the die.
    40.     ''' </summary>
    41.     ''' <value>
    42.     ''' An <b>Int32</b> containing the current face value.
    43.     ''' </value>
    44.     ''' <remarks>
    45.     ''' This would be the side facing upwards on a physical die.
    46.     ''' </remarks>
    47.     Public ReadOnly Property Value() As Integer
    48.         Get
    49.             Return _value
    50.         End Get
    51.     End Property
    52.  
    53. #End Region 'Properties
    54.  
    55. #Region " Constructors "
    56.  
    57.     ''' <summary>
    58.     ''' Creates a new instance of the <see cref="Die" /> class with the default number of faces and using the default random number generator.
    59.     ''' </summary>
    60.     ''' <remarks>
    61.     ''' The default number of faces is defined by <see cref="DefaultFaceCount" />
    62.     ''' </remarks>
    63.     Public Sub New()
    64.         Me.New(DefaultFaceCount)
    65.     End Sub
    66.  
    67.     ''' <summary>
    68.     ''' Creates a new instance of the <see cref="Die" /> class using the default random number generator.
    69.     ''' </summary>
    70.     ''' <param name="faceCount">
    71.     ''' The number of faces the die has.
    72.     ''' </param>
    73.     Public Sub New(ByVal faceCount As Integer)
    74.         Me.New(faceCount, New RandomNumberGenerator)
    75.     End Sub
    76.  
    77.     ''' <summary>
    78.     ''' Creates a new instance of the <see cref="Die" /> class with the default number of faces.
    79.     ''' </summary>
    80.     ''' <param name="randomNumberGenerator">
    81.     ''' The object used to select a die face.
    82.     ''' </param>
    83.     ''' <remarks>
    84.     ''' The default number of faces is defined by <see cref="DefaultFaceCount" />
    85.     ''' </remarks>
    86.     Public Sub New(randomNumberGenerator As IRandomNumberGenerator)
    87.         Me.New(DefaultFaceCount, randomNumberGenerator)
    88.     End Sub
    89.  
    90.     ''' <summary>
    91.     ''' Creates a new instance of the <see cref="Die" /> class.
    92.     ''' </summary>
    93.     ''' <param name="faceCount">
    94.     ''' The number of faces the die has.
    95.     ''' </param>
    96.     ''' <param name="randomNumberGenerator">
    97.     ''' The object used to select a die face.
    98.     ''' </param>
    99.     Public Sub New(ByVal faceCount As Integer, randomNumberGenerator As IRandomNumberGenerator)
    100.         If faceCount <= 0 Then
    101.             Throw New ArgumentOutOfRangeException("faceCount", "Dice must have one or more faces.")
    102.         End If
    103.  
    104.         Me.FaceCount = faceCount
    105.  
    106.         If randomNumberGenerator Is Nothing Then
    107.             randomNumberGenerator = New RandomNumberGenerator()
    108.         End If
    109.  
    110.         faceSelector = randomNumberGenerator
    111.     End Sub
    112.  
    113. #End Region 'Constructors
    114.  
    115. #Region " Methods "
    116.  
    117.     Public Function Roll() As Integer
    118.         _value = faceSelector.GenerateRandomNumber(FaceCount)
    119.  
    120.         Return Value
    121.     End Function
    122.  
    123. #End Region 'Methods
    124.  
    125. End Class
    vb.net Code:
    1. ''' <summary>
    2. ''' A collection to store multiple <see cref="Die" /> objects.
    3. ''' </summary>
    4. Public Class DieCollection
    5.     Inherits System.Collections.ObjectModel.Collection(Of Die)
    6.  
    7. #Region " Constructors "
    8.  
    9.     ''' <summary>
    10.     ''' Creates a new, empty instance of the <see cref="DieCollection" /> class.
    11.     ''' </summary>
    12.     Public Sub New()
    13.     End Sub
    14.  
    15.     ''' <summary>
    16.     ''' Create a new instance of the <see cref="DieCollection" /> with the specified number of items.
    17.     ''' </summary>
    18.     ''' <param name="dieCount">
    19.     ''' The initial number of items in the collection.
    20.     ''' </param>
    21.     ''' <remarks>
    22.     ''' 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.
    23.     ''' </remarks>
    24.     Public Sub New(dieCount As Integer)
    25.         For i = 1 To dieCount
    26.             Items.Add(New Die)
    27.         Next
    28.     End Sub
    29.  
    30.     ''' <summary>
    31.     ''' Create a new instance of the <see cref="DieCollection" /> with the specified number of items.
    32.     ''' </summary>
    33.     ''' <param name="dieCount">
    34.     ''' The initial number of items in the collection.
    35.     ''' </param>
    36.     ''' <param name="faceCount">
    37.     ''' The number of faces each die has.
    38.     ''' </param>
    39.     ''' <remarks>
    40.     ''' The initial items all use the default random number generator.
    41.     ''' </remarks>
    42.     Public Sub New(dieCount As Integer, faceCount As Integer)
    43.         For i = 1 To dieCount
    44.             Items.Add(New Die(faceCount))
    45.         Next
    46.     End Sub
    47.  
    48.     ''' <summary>
    49.     ''' Create a new instance of the <see cref="DieCollection" /> with the specified number of items.
    50.     ''' </summary>
    51.     ''' <param name="dieCount">
    52.     ''' The initial number of items in the collection.
    53.     ''' </param>
    54.     ''' <param name="randomNumberGenerator">
    55.     ''' The object used to select a die face.
    56.     ''' </param>
    57.     ''' <remarks>
    58.     ''' The initial items all have the default number of faces, as defined by the <see cref="Die.DefaultFaceCount" /> field.
    59.     ''' </remarks>
    60.     Public Sub New(dieCount As Integer, randomNumberGenerator As IRandomNumberGenerator)
    61.         For i = 1 To dieCount
    62.             Items.Add(New Die(randomNumberGenerator))
    63.         Next
    64.     End Sub
    65.  
    66.     ''' <summary>
    67.     ''' Create a new instance of the <see cref="DieCollection" /> with the specified number of items.
    68.     ''' </summary>
    69.     ''' <param name="dieCount">
    70.     ''' The initial number of items in the collection.
    71.     ''' </param>
    72.     ''' <param name="faceCount">
    73.     ''' The number of faces each die has.
    74.     ''' </param>
    75.     ''' <param name="randomNumberGenerator">
    76.     ''' The object used to select a die face.
    77.     ''' </param>
    78.     Public Sub New(dieCount As Integer, faceCount As Integer, randomNumberGenerator As IRandomNumberGenerator)
    79.         For i = 1 To dieCount
    80.             Items.Add(New Die(faceCount, randomNumberGenerator))
    81.         Next
    82.     End Sub
    83.  
    84. #End Region 'Constructors
    85.  
    86. #Region " Methods "
    87.  
    88.     ''' <summary>
    89.     ''' Rolls all the dice in the collection and returns the sum of the results.
    90.     ''' </summary>
    91.     ''' <returns>
    92.     ''' An <b>Int32</b> containing the summed values of all dice.
    93.     ''' </returns>
    94.     Public Function RollAll() As Integer
    95.         Return Items.Sum(Function(die) die.Roll())
    96.     End Function
    97.  
    98. #End Region 'Methods
    99.  
    100. End Class
    Here is the interface and default implementation for dependency injection:
    vb.net Code:
    1. ''' <summary>
    2. ''' Represents an object that can generate a random integer.
    3. ''' </summary>
    4. Public Interface IRandomNumberGenerator
    5.  
    6.     ''' <summary>
    7.     ''' Generates a random integer.
    8.     ''' </summary>
    9.     ''' <param name="max">
    10.     ''' The maximum value (inclusive) that should be generated.
    11.     ''' </param>
    12.     ''' <returns>
    13.     ''' An <b>Int32</b> containing a random number.
    14.     ''' </returns>
    15.     ''' <remarks>
    16.     ''' The value returned should never be less than 1.
    17.     ''' </remarks>
    18.     Function GenerateRandomNumber(max As Integer) As Integer
    19.  
    20. End Interface
    21.  
    22.  
    23. ''' <summary>
    24. ''' Represents an object that can generate a random integer.
    25. ''' </summary>
    26. ''' <remarks>
    27. ''' This class is the default random number generator for the <see cref="Die" /> class.
    28. ''' </remarks>
    29. Public Class RandomNumberGenerator
    30.     Implements IRandomNumberGenerator
    31.  
    32.     ''' <summary>
    33.     ''' The underlting random number generator.
    34.     ''' </summary>
    35.     ''' <remarks>
    36.     ''' A single <b>Random</b> object is used by all instances to avoid duplicate results when multiple values are generated quickly.
    37.     ''' </remarks>
    38.     Private Shared rng As New Random
    39.  
    40.     ''' <summary>
    41.     ''' Generates a random number.
    42.     ''' </summary>
    43.     ''' <param name="max">
    44.     ''' The maximum value (inclusive) that can be generated.
    45.     ''' </param>
    46.     ''' <returns>
    47.     ''' An <b>Int32</b> containing a random number.
    48.     ''' </returns>
    49.     ''' <remarks>
    50.     ''' The value returned will never be less than 1.
    51.     ''' </remarks>
    52.     Public Function GenerateRandomNumber(max As Integer) As Integer Implements IRandomNumberGenerator.GenerateRandomNumber
    53.         If max < 1 Then
    54.             Throw New ArgumentOutOfRangeException("max", max, "Value cannot be less than 1.")
    55.         End If
    56.         Return rng.Next(1, max + 1)
    57.     End Function
    58.  
    59. 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.

  4. #4
    New Member
    Join Date
    Jul 06
    Posts
    2

    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

  5. #5
    PowerPoster techgnome's Avatar
    Join Date
    May 02
    Posts
    21,636

    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
    * I don't respond to private (PM) requests for help. It's not conducive to the general learning of others.-I also subscribe to all threads I participate, so there's no need to pm when there's an update.*
    *Proof positive that searching the forums does work: View Thread *
    * How to get EFFECTIVE help: The Hitchhiker's Guide to Getting Help at VBF - Removing eels from your hovercraft *
    * How to Use Parameters * Create Disconnected ADO Recordset Clones * Set your VB6 ActiveX Compatibility * Get rid of those pesky VB Line Numbers * I swear I saved my data, where'd it run off to??? *
    * Use Offensive Programming, not Defensive Programming. * On Error Resume Next is error ignoring, not error handling(tm).
    "There is a major problem with your code, and VB wants to tell you what it is.. but you have decided to put your fingers in your ears and shout 'I'm not listening!'" - si_the_geek on using OERN

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •