TinyCircuits Forum
TinyCircuits Products => Thumby => Topic started 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?
-
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?
-
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).