TinyCircuits Forum

TinyCircuits Products => Thumby => Topic started by: Void777 on October 17, 2021, 02:21:41 PM

Title: Is the Random Function broken for anyone else?
Post by: Void777 on October 17, 2021, 02:21:41 PM
So, im trying to pick a random.choice in micropython. For whatever reason, it always picks the same number based on the number i pick. For example if i pick the first option, it will always pick the third. if i pick the second, it will always pick the first. If i pick the third it will always pick the first. Ive checked my code and executed it at least 30 times with the same results. Has anyone else encouneterd this?
Title: Re: Is the Random Function broken for anyone else?
Post by: rli on October 17, 2021, 02:43:03 PM
Do you have example code? I think I was able to reproduce the issue you're describing.

I believe the "problem" is that the random number generator's seed doesn't change between runs of the emulator.

Here's my test:
Code: [Select]
import random
import time

l = [0,1,2,3]

for i in range(20):
    r = random.choice(l)
    print(f"{i}: {r}")

# Outputs:
# 0: 1
# 1: 0
# 2: 1
# 3: 3
# 4: 0
# 5: 3
# 6: 3
# 7: 3
# 8: 1
# 9: 1
# 10: 3
# 11: 3
# 12: 3
# 13: 1
# 14: 1
# 15: 2
# 16: 1
# 17: 2
# 18: 3
# 19: 1

The output is the same each time.

Relevant documentation: https://docs.micropython.org/en/latest/library/random.html?highlight=random#random.seed

Some usual tricks for initializing a seed for less deterministic results is passing in the time, but because the emulator has no true Real Time Clock(RTC) or this MICROPY_PY_URANDOM_SEED_INIT_FUNC setting enabled, all the random number functions will act deterministically.

In practice, you might be fine with passing the time.ticks_ms() or time.ticks_us() as the seed (https://docs.micropython.org/en/latest/library/time.html?highlight=time#time.ticks_ms):
Code: [Select]
import random
import time

random.seed(time.ticks_us()) # Relies on minor inconsistencies to differ a little each time

l = [0,1,2,3]

for i in range(20):
    r = random.choice(l)
    print(f"{i}: {r}")

It's not great and depending on how early on in the execution you're getting random numbers, it might get the same seed more often than not.


EDIT: I'm just stating what appears to be happening in the emulator. I don't know if the actual hardware will implement the micropython option or at least have working RTC so we can have some source of noise. Maybe the engineers/devs can answer?
Title: Re: Is the Random Function broken for anyone else?
Post by: Void777 on October 17, 2021, 02:49:15 PM
Do you have example code? I think I was able to reproduce the issue you're describing.

I believe the "problem" is that the random number generator's seed doesn't change between runs of the emulator.

Here's my test:
Code: [Select]
import random
import time

l = [0,1,2,3]

for i in range(20):
    r = random.choice(l)
    print(f"{i}: {r}")

# Outputs:
# 0: 1
# 1: 0
# 2: 1
# 3: 3
# 4: 0
# 5: 3
# 6: 3
# 7: 3
# 8: 1
# 9: 1
# 10: 3
# 11: 3
# 12: 3
# 13: 1
# 14: 1
# 15: 2
# 16: 1
# 17: 2
# 18: 3
# 19: 1

The output is the same each time.

Relevant documentation: https://docs.micropython.org/en/latest/library/random.html?highlight=random#random.seed

Some usual tricks for initializing a seed for less deterministic results is passing in the time, but because the emulator has no true Real Time Clock(RTC) or this MICROPY_PY_URANDOM_SEED_INIT_FUNC setting enabled, all the random number functions will act deterministically.

In practice, you might be fine with passing the time.ticks_ms() or time.ticks_us() as the seed (https://docs.micropython.org/en/latest/library/time.html?highlight=time#time.ticks_ms):
Code: [Select]
import random
import time

random.seed(time.ticks_us()) # Relies on minor inconsistencies to differ a little each time

l = [0,1,2,3]

for i in range(20):
    r = random.choice(l)
    print(f"{i}: {r}")

It's not great and depending on how early on in the execution you're getting random numbers, it might get the same seed more often than not.
I think your probably right, ill try and edit it based on your suggestions, Heres the code for the character select screen, which should select a random opponent based on the character id.
Code: [Select]
chars = [['goggles', goggles_icon, goggles_icon_selected], ['zap', zap_icon, zap_icon_selected], ['apex', apex_icon, apex_icon_selected]]
        for i in range(len(chars)):
            thumby.display.blit(box, (16*i) + 5, 0, 16, 16, 1)
            if i != selected:
                thumby.display.blit(chars[i][1], (16*i) + 5, 0, 16, 16, 1)
            else:
                thumby.display.blit(chars[i][2], (16*i) + 5, 0, 16, 16, 1)
        if thumby.buttonR.pressed() and selected < len(chars):
            selected += 1
        elif thumby.buttonL.pressed() and selected > 0:
            selected -= 1
        elif thumby.buttonA.pressed():
            selected_char = chars[selected][0]
            enemy_char_data = random.choice(chars)
            enemy_char = enemy_char_data[0]
            while enemy_char == selected_char:
                enemy_char_data = random.choice(chars)
                enemy_char = enemy_char_data[0]
            thumby.display.fill(1)
            select_screen = False
            singleplayer_battle(selected_char, enemy_char)
        time.sleep(0.1)
        thumby.display.update()
        i = 0
Title: Re: Is the Random Function broken for anyone else?
Post by: Jason on October 18, 2021, 10:55:53 AM
The random number returning the same number should happen both on hardware and in the emulator.

In the case of your game, a more reliable way to seed the random number generator is to feed
Code: [Select]
random.seed(seed) the passed time/ticks using
Code: [Select]
time.ticks_us() or
Code: [Select]
time.ticks_ms() after the user selects a character. It is unlikely that a user will select a character at the same milli or micro second each time (unless maybe they hold down the button from game start each time).