finnvoor / PlaydateKit

Create games for Playdate using Swift.
https://finnvoor.github.io/PlaydateKit/documentation/playdatekit
Creative Commons Zero v1.0 Universal
199 stars 21 forks source link

Fix Random Number Generation Behavior #58

Closed tyetrask closed 5 months ago

tyetrask commented 5 months ago

(👋 This is (one of) my first contribution(s) to the repository so please let me know if I've missed any rules/guidelines/instructions or anything else I should do for this pull request, thank you!)

Description

While observing some random generation in a project, I noticed that seemingly not all of the values were being selected despite many calls to Int.random(), Float.random(), or array's .randomElement(). I know very little about C or lower level memory so my debugging skills are fairly limited but I was able to see that arc4random_buf implementation had been added and then later updated.

As a test, I reverted the implementation to the original in my working fork of PlaydateKit and the issues resolved. I've tried to dig a bit as to why this might be happening and see if there's a fix for the newer implementation but this is out of my comfort zone for now.

This pull request reverts to the original implementation (before this change).

Of course, maybe this isn't desired and there's a reason not to go back but I thought I'd at least open the pull request for discussion! I was definitely planning to see if I could spot anything the newer implementation but as mentioned, I need to do a bit of studying before I think I'll be able to make much headway there.

Reproduction

A 'game' to reproduce this is on a branch here but the simplest portion is something like this:

final class Game: PlaydateGame {
    // MARK: Internal
    func update() -> Bool {
        let randomInt = Int.random(in: 1...3)
        if randomInt == 1 {
            System.log(StaticString("randomInt: 1"))
        } else if randomInt == 2 {
            System.log(StaticString("randomInt: 2"))
        } else if randomInt == 3 {
            System.log(StaticString("randomInt: 3"))
        }

        System.drawFPS()
        return true
    }
}

Current Behavior

With latest main for PlaydateKit, this produces the following:

random_bug

(Note the distinct lack of 3s 😄, and this is the case seemingly no matter how long I let it run.)

Expected Behavior

With the reversion to the original implementation, the same Game produces the following:

random_expected
finnvoor commented 5 months ago

Wow, nice catch. It seems like this is caused by rand() on Playdate only generating 31 bits of randomness instead of 32. I would prefer to use the system rand since it's probably better than the one I threw together, so I opened a PR here: https://github.com/finnvoor/PlaydateKit/pull/61 to only use the bottom half of rand(), which fixes the issue. Thanks for looking into this!

tyetrask commented 5 months ago

Thank you, @finnvoor!