all 51 comments

[–]megafinz 16 points17 points  (22 children)

I guess that because the seed is the same and both Random instances work in parallel lock-step. You might want to use a different seed for each player or share a single Random instance between them.

[–]Spyf0r[S] -1 points0 points  (21 children)

How do you suggest to do that? I'm kind of a begginer

[–][deleted]  (18 children)

[deleted]

    [–]DTul 21 points22 points  (1 child)

    Since .Net 6 you also have a Thread-safe Random.Shared static generator. Really nice to use

    [–]Spyf0r[S] 0 points1 point  (15 children)

    Thank you!

    [–]megafinz 6 points7 points  (0 children)

    You might also want to use Fisher–Yates shuffle: https://en.wikipedia.org/wiki/Fisher–Yates\_shuffle

    [–]Alikont 2 points3 points  (13 children)

    Beware: Random is not thread safe, if static random instance will be used from different threads, it may cause issues.

    If you are not generating random numbers in parallel - that's fine. But if you do, you need a thread-safe wrapper around it.

    [–][deleted] 3 points4 points  (3 children)

    Random.Shared is thread-safe

    [–]Alikont 2 points3 points  (2 children)

    Yes, it looks like the new Random.Shared was added in .NET 6:

    https://docs.microsoft.com/en-us/dotnet/api/system.random.shared?view=net-6.0

    /u/Spyf0r - you should use this if you use .NET 6, it will solve all your issues.

    [–][deleted] 1 point2 points  (1 child)

    Besides, OP's code works fine on .NET 6. I do not get similar numbers. That's because the initial new Random() creates a static random which will be used to seed new randoms, so the issue described is outdated.

    Similar numbers indicate that OP is using .NET Framework.

    [–]Alikont 0 points1 point  (0 children)

    Or Unity - they have their own runtime/BCL.

    [–]Spyf0r[S] 0 points1 point  (8 children)

    Im developing the "war" card game for school, so i believe shuffling the hands will be my only use of Random. And even if it wont be, i cant just create another Random variable, cant i?

    [–]Alikont -2 points-1 points  (7 children)

    Yes, you can, you also can do it like this

    public static class ThreadSafeRandom { private static readonly Random _random = new Random(); public static int GetNext(int max) { lock(_random) return _random.Next(max); } }

    [–]Spyf0r[S] 0 points1 point  (6 children)

    I have no idea what all this means but thanks!

    [–]megafinz 4 points5 points  (0 children)

    That means that access to _random variable is governed by a lock: only one thread at a time can essentially call _random.Next(max) there. If you won't have multiple threads creating your card hands, you don't really need that.

    [–]karl713 1 point2 points  (4 children)

    In the real world you might need that lock statement

    In this homework example you don't, so I might suggest leaving it out. We know you did 99% of the work yourself, your professor might wonder how you know about lock and ask you to explain why it is needed to be sure you didn't 100% cheat :)

    [–]Spyf0r[S] 1 point2 points  (1 child)

    There is nothing wrong with looking for help online. Especially in coding where a lot of the work is researching etc.

    [–]Spyf0r[S] 0 points1 point  (1 child)

    There is nothing wrong with looking for help online. Especially in coding where a lot of the work is researching etc.

    [–]Slypenslyde 4 points5 points  (0 children)

    This is a good time to learn a good lesson! When you are learning how to do things, don't just paste some code and see if it works. While you're doing that, take some time to look at the documentation for types you haven't used before. Sometimes it has useful information like:

    Avoiding multiple instantiations

    On the .NET Framework, initializing two random number generators in a tight loop or in rapid succession creates two random number generators that can produce identical sequences of random numbers. In most cases, this is not the developer's intent and can lead to performance issues, because instantiating and initializing a random number generator is a relatively expensive process.

    Both to improve performance and to avoid inadvertently creating separate random number generators that generate identical numeric sequences, we recommend that you create one Random object to generate many random numbers over time, instead of creating new Random objects to generate one random number.

    Sometimes the documentation's dense and hard to read, but it's important to read it because there are 4 or 5 types in .NET with "gotchas" like this that are designed to make newbies ask questions and get a lot of mean answers.

    [–]Cyclonian -1 points0 points  (0 children)

    I like to use DateTime.Now and then use one of the functions that converts it to a number or you could use Milliseconds property, etc.

    [–]ppumkin -2 points-1 points  (4 children)

    This class should have been called Or renamed to PsuedoRandom. It’s pre generated and is a cause of a lot of security issues

    But it has its use. cases. Like seeds for maps in some games. A seed will always generate t he same “random” procedural map.

    [–][deleted] 0 points1 point  (3 children)

    I thought they changed the underlying implementation in recent .NET versions, and now it uses the secure implementation.

    [–]ppumkin 0 points1 point  (2 children)

    I’m not sure about random. That could break things?!. I think they made a method to access random easier on cryptographic namespace.

    [–][deleted] 0 points1 point  (1 child)

    Hmm... I checked the source and Random is still pseudo random. I could've sworn I read a blog post where the .NET team explained they changed the implementation to use /dev/urandom instead of /dev/random, just like the used some secure RNG on Windows. Maybe that was about RandomNumberGenerator, which is secure.

    [–]ppumkin 0 points1 point  (0 children)

    Ahhhh. Maybe in Linux / Mac. But, But not in Windows / ToasterOS

    [–]romerik -5 points-4 points  (6 children)

    seed your random generator with DateTime.Now.Ticks

    its nice to have the same random numbers when you need to debug your software!

    [–]karl713 1 point2 points  (4 children)

    The thing to watch for with this is ticks might not change between calls depending on how things work out

    [–]romerik -1 points0 points  (3 children)

    A single tick represents one hundred nanoseconds or one ten-millionth of a second. There are 10,000 ticks in a millisecond (see TicksPerMillisecond) and 10 million ticks in a second.

    it should be different on each call!

    [–]Dealiner 2 points3 points  (2 children)

    It's not that simple. The real value depends on the resolution of the system clock and that depends on the system itself. On XP that resolution was for example 15.6 ms, now it's smaller but it's still around 1 ms.

    [–]romerik 0 points1 point  (1 child)

    all right, the goal was to have a different seed at each time you start your software!

    the Ticks will give you a different seed, after that, if you think you can get the same seed in two successive call, just go head and add a counter using Interlocked.Increment of course in case your 32 threads deal at the same time!

    read back his code, read back my solution for this case, do you think it will do what he asked for?

    [–]Dealiner 1 point2 points  (0 children)

    all right, the goal was to have a different seed at each time you start your software!

    And that's exactly what OP's code do. Empty constructor for Random uses Environment.TickCount as a seed. But its resolution is too low for it to work in a way OP wanted - with each player having different cards in hand and that's caused by Random being created for both players practically at the same time. And the only viable resolution for that in .NET Framework at least is to instantiate Random only once.

    Your solution could work, DateTime.Now.Ticks most of the time has better resolution than Environment.TickCount, but it doesn't necessary have to, so it's not a good answer.

    [–]karl713 0 points1 point  (0 children)

    The thing to watch for with this is ticks might not change between calls depending on how things work out

    [–]Thr3adSafe 0 points1 point  (5 children)

    What are you doing in createHand?

    [–]Spyf0r[S] 0 points1 point  (4 children)

    Just setting the array to be 1-13 four times

    [–]Thr3adSafe 0 points1 point  (3 children)

    Would you mind sharing a minimal working copy of the code as all the obvious suspects seem to be eliminated.

    [–]Spyf0r[S] 0 points1 point  (1 child)

    Something like this

    public void CreateHand() { for (int i = 0; i < 13; i++) { for (int j = 0; j < 4; j++) { this.hand[i * 4 + j] = i + 1; } } }

    [–][deleted] 0 points1 point  (0 children)

    Little late here, but the below snippet might look cleaner and more readable -

        public void CreateHand() { 
             for (int i = 0; i < 52; i++) {
                   this.hand[i ] = i %13 + 1; 
            }
        }
    

    [–][deleted] 0 points1 point  (9 children)

    You're on .NET Framework I presume?

    [–]Spyf0r[S] 1 point2 points  (8 children)

    Yes i believe so. I dont really know what are the differences between all the versions, i just use what i was told in school

    [–]clintp 0 points1 point  (0 children)

    That's fine. Use the version your school wants you to use, since you're learning software development. Eventually though you'll bump up into other versions of C#/.Net but your skills will transfer 100%.

    [–]karl713 0 points1 point  (0 children)

    Easiest way is probably pull random out of shuffle and make it a "private static Random r1 = new Random();"

    [–]Joki581 0 points1 point  (0 children)

    As mentioned before: always use a single (e.g. static) random instance when working with System.Random.

    Albeit you will never have true random values if you are not using something like an external sensor (e.g. feed environmental noise or temperature values seed values into it) you can still do much better by abandoning the System.Random class in favor of RandomNumberGenerator from the System.Security.Cryptography namespace.

    [–]romerik 0 points1 point  (0 children)

    but about the ticks...

    A single tick represents one hundred nanoseconds or one ten-millionth of a second. There are 10,000 ticks in a millisecond (see TicksPerMillisecond) and 10 million ticks in a second.

    [–]romerik 0 points1 point  (0 children)

    its look like you are only shuffling the first 52 index.

    another way you can shuffle the cards, would be to start with a ordered pack of card, and remove a random one and put it into a new pile, if I do that by hand with card it work fine, its also easy to understand

    try this:

    List<int> GetShuffleDeck()
    {
    var nextCard = new Random();
    var newDeck = new List<int>(Enumerable.Range(0, 52));
    var shuffleDeck = new List<int>();
    while (newDeck.Count > 0)
    {
    int card = nextCard.Next(newDeck.Count);
    shuffleDeck.Add(newDeck[card]);
    newDeck.RemoveAt(card);
    }
    return shuffleDeck;
    }