1

I am wanting to create a dice roller so that the user can choose a number of sides on a dice and it will randomise the response, My current code chucks out the same number all of the time.

Sub rollDie(ByVal sides As Integer)
    Dim rand As Single = Rnd()
    For cnt As Integer = 1 To sides
        If rand < cnt / sides Then
            diceRoll = cnt
            Exit For
        End If
    Next
    Console.WriteLine("You rolled a {0} sided die which landed on {1}", sides, diceRoll)
End Sub
Matt Wilko
  • 25,893
  • 10
  • 85
  • 132
ipro_ultra
  • 83
  • 7
  • 1
    possible duplicate of [Vb.net Random Number generator generating same number many times](http://stackoverflow.com/questions/7456477/vb-net-random-number-generator-generating-same-number-many-times) – Bjørn-Roger Kringsjå Oct 23 '14 at 07:13
  • 1
    Rnd is a legacy function. See MattWilko answer for preferred method. – dbasnett Oct 23 '14 at 12:05

3 Answers3

2

I think you would be better putting all of your Die logic into a class and using the System.Random class to generate your random numbers for you like this:

Public Class Die
    Private _sides As Integer
    Private Shared _generator As New System.Random '<<<one PRNG no matter how many dice

    Public ReadOnly Property Sides As Integer
        Get
            Return _sides
        End Get
    End Property

    Public Sub New(sides As Integer)
        _sides = sides
    End Sub

    ''' <summary>
    ''' Returns a random number between 1 and the number of sides of the die
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function Roll() As Integer
        Return _generator.Next(1, _sides + 1)
    End Function
End Class

Then you can use it like this:

Dim elevensidedDie As New Die(11)
Debug.WriteLine("You rolled a(n) {0} sided die which landed on {1}", elevensidedDie.Sides, elevensidedDie.Roll)
dbasnett
  • 10,010
  • 2
  • 22
  • 32
Matt Wilko
  • 25,893
  • 10
  • 85
  • 132
1

You should use VB.Net's Randomize function to seed the random number generator, i.e.

Sub rollDie(ByVal sides As Integer)
Randomize()
Dim rand As Single = Rnd()
For cnt As Integer = 1 To sides
    If rand < cnt / sides Then
        diceRoll = cnt
        Exit For
    End If
Next
Console.WriteLine("You rolled a {0} sided die which landed on {1}", sides, diceRoll)
End Sub

The Randomize function uses the system timer for the seed. You could place the call to it at the start of your program or in your rollDie function.

Phillip Trelford
  • 6,423
  • 22
  • 40
0

Using a loop is a very inefficient way to generate a random integer. Look at this page for a better method.

Here is a copy of the most relevant bit with comments:

' Initialize the random-number generator.
Randomize()
' Generate random value between 1 and 6. 
Dim value As Integer = CInt(Int((6 * Rnd()) + 1))

Much more concise and way faster too.

Magnitus
  • 278
  • 1
  • 11
  • 1
    And just as biased ... ;) – Joey Oct 23 '14 at 08:32
  • You mean biased in the context of the randomness of the generated results? That is solely contingent on the quality of the results returned by Rnd() and beyond the control of the API user short of writing (or finding a better implementation of) their own u(0,1) generator from scratch no? – Magnitus Oct 23 '14 at 17:02
  • Ah, I read your improvement in another answer. You're using VB's API to access the OS' random byte generator to generate the number? It generates better results for security critical applications (I'm thinking maybe overkill for dice unless it's for a Casino). Never dug deep enough to figure out why the OS provided byte generator is usually better, but that's probably a topic for another question. – Magnitus Oct 23 '14 at 17:28
  • Not sure what answer you found, but I was thinking more of what I wrote in [this answer](http://stackoverflow.com/a/11758872/73070). The point is that multiplying a float so that it fits in your range introduces bias, just as doing a modulus does. The example there is fairly contrived, admittedly; depending on the range the real-world results are not as drastic. This has nothing to do with the source of (pseudo-)randomness, by the way. – Joey Oct 23 '14 at 18:16
  • I can see how the modulo approach introduces bias if the divisor is not a factor of the dividend. However, were are not using that here. We are using a simulated u(0,1) and using u(0,1) as a base to generate samples to other types of random distribution is textbook applied probability. I don't see how that is biased. – Magnitus Oct 23 '14 at 20:34
  • Let P([x,y]) be the probability that Rnd() generates a result in the interval [x,y], y not included. Then, P([0,1/6])=P([1/6,2/6])=P([2/6,3/6])=P([3/6,4/6])=P([4/6,5/6])=P([5/6,6/6]). Let R be 6*Rnd()+1. Then, P(R=1|[0,1/6]) = P(R=2|[1/6,2/6]) = P (R=3|[2/6,3/6]) = P (R=4|[3/6,4/6]) = P(R=5|[4/6,5/6]) = P(R=6|[5/6,6/6]) = 1. And generally, P(R=i) = P(R=i|[(i-1)/6,i/6])*P([(i-1)/6,i/6]). From there, it's easy to get P(R=1) = P(R=2) = P(R=3) = P(R=4) = P(R=5) = P(R=6). – Magnitus Oct 23 '14 at 21:12
  • Read the other answer and the comments. And the pretty pictures. It really happens. Floating-point numbers are not real numbers. – Joey Oct 23 '14 at 21:50
  • Point taken, I guess if you accept that floating point numbers are really discretely distributed, then you can get a situation where intervals (ie, [1/6, 2/6] vs [2/6, 3/6]) get slightly more hits (althrough it's a minuscule difference), but then again, if you cannot rely on any implementation of u(0,1) because "floats are not reals!", how can you simulate most distributions that rely on tranforms of u(0,1)? Maybe it's not perfectly distributed, but it's close enough to pass chi-square tests against expected frequencies: https://github.com/Magnitus-/SciDice Good enough for me. – Magnitus Oct 23 '14 at 22:13
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/63569/discussion-between-joey-and-magnitus). – Joey Oct 24 '14 at 05:46