0

On again i´m needing some help here, today i was doin a c# homework that basically consist in creating a method that can sum the result of 2 dice rolls, so far, so good!, no hard thing to do... BUT, when i run my code some really odd thing happens, i will let my code and results to speak from them self:

using System;

class Program
{

    public static String GetTimestamp()
    {
        DateTime saveNow = DateTime.Now;
        return saveNow.ToString("yyyyMMddHHmmssffff");
    }

    public static int ThrowDice()
    {
        int randVal1, randVal2;
        string TS1, TS2;

        Random rand = new Random();
        randVal1 = rand.Next(1,6);
        TS1 = GetTimestamp();
        randVal2 = rand.Next(1,6);
        TS2 = GetTimestamp();

        Console.WriteLine("Dice 1: \t" + randVal1 + ' ' + TS1);
        Console.WriteLine("Dice 2: \t" + randVal2 + ' ' + TS2);
        Console.WriteLine("-----------------");

        return randVal1 + randVal2;
    }

    static void Main()
    {
        for(int i = 1; i<=10;i++)
        {
            Console.WriteLine("Dice Roll #" + i);
            Console.WriteLine("Dice Sum:\t" + ThrowDice());
            Console.WriteLine();
        }
    }
}

Now, this is what i got when i ran it:

Dice Roll #1

Dice 1: 1 / TimeStamp : 201308260146259075

Dice 2: 2 / TimeStamp : 201308260146259131

Dice Sum: 3


Dice Roll #2

Dice 1: 2 / TimeStamp : 201308260146259135

Dice 2: 5 / TimeStamp : 201308260146259135

Dice Sum: 7


Dice Roll #3

Dice 1: 2 / TimeStamp : 201308260146259135

Dice 2: 5 / TimeStamp : 201308260146259135

Dice Sum: 7


Dice Roll #4

Dice 1: 2 / TimeStamp : 201308260146259135

Dice 2: 5 / TimeStamp : 201308260146259135

Dice Sum: 7


Dice Roll #5

Dice 1: 2 / TimeStamp : 201308260146259135

Dice 2: 5 / TimeStamp : 201308260146259135

Dice Sum: 7


Dice Roll #6

Dice 1: 2 / TimeStamp : 201308260146259135

Dice 2: 5 / TimeStamp : 201308260146259135

Dice Sum: 7


Dice Roll #7

Dice 1: 2 / TimeStamp : 201308260146259135

Dice 2: 5 / TimeStamp : 201308260146259135

Dice Sum: 7


Dice Roll #8

Dice 1: 2 / TimeStamp : 201308260146259135

Dice 2: 5 / TimeStamp : 201308260146259135

Dice Sum: 7


Dice Roll #9

Dice 1: 2 / TimeStamp : 201308260146259135

Dice 2: 5 / TimeStamp : 201308260146259135

Dice Sum: 7


Dice Roll #10

Dice 1: 2 / TimeStamp : 201308260146259135

Dice 2: 5 / TimeStamp : 201308260146259135

Dice Sum: 7


-Roll 1 is perfect

-Roll 2/dice 1... perfect,

-Roll 2/dice 2... WTH???

Those results are really weird, i was expecting to get different timestamps and different values on each roll. The timeStamp stuff what an idea i came up with to check the time when those numbers are generated, and it looks like both "dices" are rolled at the same time, it looks like "Random" generates the seed using the clock.

I even tryed creating a whole class for dice with a DiceRoll() method, but even there where both a complete different objects i was just getting the same results over and over..

How can i fix this?, any ideas?

AcidRod75
  • 187
  • 9
  • Someone asks this question on SO pretty much every day. API designers take note: this design is a confusion-producing machine! – Eric Lippert Aug 26 '13 at 16:28
  • @EricLippert So how would you design the API? Would you use something different as a seed? Force the user to always provide their own seed? Have a static "Default" `Random` instance for "normal" use (without prohibiting creation of others)? – Servy Aug 26 '13 at 16:32
  • 1
    @Servy: That's a good question; detailing all the pros and cons of each would be difficult in a 500 character comment. :-) My inclination would be to seed off the current time on the first `new Random` and then keep a thread-safe counter that bumps the seed up by one on each subsequent `new Random`. That gives you both a reasonable source of entropy and a guarantee that any two instances will have a different seed, without a huge performance cost. – Eric Lippert Aug 26 '13 at 16:47
  • @EricLippert That actually would lead to non-random behavior in cases like this. `Random` doesn't retain its random appearance if you take the first value from each subsequent seed. – Mike Precup Aug 26 '13 at 16:52

2 Answers2

4

Random, when used with the default constructor, uses the time as a seed. With the same seed, you will get the same results every time. Since you're creating a new Random several times in a row, each one will start with the same seed and so produce the same numbers. To solve the issue, make only one Random and reuse it.

Also, you may well know this, but since it's a common mistake, and most dice rolls are from 1-6, I feel like I should point out that Random.Next uses and exclusive upper bound, so currently you're only getting numbers from 1-5.

Mike Precup
  • 4,148
  • 21
  • 41
  • 2
    Note that `Random` is not threadsafe, so if you have a multithreaded program be very careful about re-using the same `Random` on multiple threads. A thread-static field is a good idea in this scenario. – Eric Lippert Aug 26 '13 at 16:29
  • Eric, thank you for the warning about the threadsafe stuff! :) it´s a good thing to know! – AcidRod75 Aug 26 '13 at 16:58
  • Mike, i just did the "static Random rand = new Random();" solution and it works perfect!, about the upper you where right... corrected it and now i´m getting 6's – AcidRod75 Aug 26 '13 at 16:59
0

Declare Rand as a static variable and initilize it just once

using System;

class Program
{

    public static String GetTimestamp()
    {
        DateTime saveNow = DateTime.Now;
        return saveNow.ToString("yyyyMMddHHmmssffff");
    }

    public static int ThrowDice()
    {
        int randVal1, randVal2;
        string TS1, TS2;


        randVal1 = rand.Next(1,7);
        TS1 = GetTimestamp();
        randVal2 = rand.Next(1,7);
        TS2 = GetTimestamp();

        Console.WriteLine("Dice 1: \t" + randVal1 + ' ' + TS1);
        Console.WriteLine("Dice 2: \t" + randVal2 + ' ' + TS2);
        Console.WriteLine("-----------------");

        return randVal1 + randVal2;
    }
    static Random rand ;

    static void Main()
    {
        rand = new Random();

        for(int i = 1; i<=10;i++)
        {
            Console.WriteLine("Dice Roll #" + i);
            Console.WriteLine("Dice Sum:\t" + ThrowDice());
            Console.WriteLine();
        }
    }
}

And the time stamps can be identical for a few rolls but not for that many that is strage

Mauricio Gracia Gutierrez
  • 10,288
  • 6
  • 68
  • 99
  • 2
    But only when running single threaded. – H H Aug 26 '13 at 16:28
  • Well.. yea.. the time stamps ARE wierd, since i was using to debug the code it´s nothing important, but i will love to understand what´s goin on here so i can reuse the code in the future. – AcidRod75 Aug 26 '13 at 17:01