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?
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:
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):
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?
Quote from: 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:
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):
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.
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
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 random.seed(seed)
the passed time/ticks using time.ticks_us()
or 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).