Skip to content

Instantly share code, notes, and snippets.

@lincoln-lm
Created September 28, 2024 22:15
Show Gist options
  • Save lincoln-lm/c859e030968bdfde34183f7fad7b7544 to your computer and use it in GitHub Desktop.
Save lincoln-lm/c859e030968bdfde34183f7fad7b7544 to your computer and use it in GitHub Desktop.
SWSH rain & galar bird rng scripts
# search config
MIN_NPC_COUNT = 25
MAX_NPC_COUNT = 40
# unknown what exactly determines this value but it should be consistent for the area and not very high
MIN_FLY_VALUE = 0
MAX_FLY_VALUE = 12
# rng config
BRUTEFORCE_RANGE = 5000
class Xoroshiro:
def __init__(self, seed_0, seed_1 = 0x82A2B175229D6A5B):
self.state = [seed_0, seed_1]
@staticmethod
def rotl(x, k):
return ((x << k) | (x >> (64 - k))) & 0xFFFFFFFFFFFFFFFF
@staticmethod
def get_mask(x):
x -= 1
for i in range(6):
x |= x >> (1 << i)
return x
def next(self):
s0, s1 = self.state
result = (s0 + s1) & 0xFFFFFFFFFFFFFFFF
s1 ^= s0
self.state = [self.rotl(s0, 24) ^ s1 ^ ((s1 << 16) & 0xFFFFFFFFFFFFFFFF), self.rotl(s1, 37)]
return result
def rand_count(self, maximum = 0xFFFFFFFF):
mask = self.get_mask(maximum)
res = self.next() & mask
count = 1
while res >= maximum:
res = self.next() & mask
count += 1
return res, count
def find_valid_calibration(rng_state: tuple[int, int], possible_results: set[int]) -> list[tuple[int, int]]:
calibrations = []
for fly_value in range(MIN_FLY_VALUE, MAX_FLY_VALUE):
for npc_count in range(MIN_NPC_COUNT, MAX_NPC_COUNT):
test_rng = Xoroshiro(*rng_state)
jump = 0
for _ in range(fly_value):
_, adv = test_rng.rand_count(100)
jump += adv
for _ in range(npc_count):
_, adv = test_rng.rand_count(91)
jump += adv
if jump in possible_results:
calibrations.append((fly_value, npc_count))
return calibrations
def calibrate():
state_0 = int(input("State 0: 0x"), 16)
state_1 = int(input("State 1: 0x"), 16)
current_advance = int(input("Current Advance: "))
rng = Xoroshiro(state_0, state_1)
for _ in range(current_advance):
rng.next()
next_valid_advance = current_advance
temp_rng = Xoroshiro(*rng.state)
while not (5 <= (temp_rng.rand_count(100)[0] + 1)):
next_valid_advance += 1
rng.next()
temp_rng = Xoroshiro(*rng.state)
post_menu_state = tuple(temp_rng.state)
print(f"Advance to {next_valid_advance} before opening the map")
possible_results = set()
while True:
inp = input("Possible hit advance (enter to confirm list): ")
if inp:
possible_results.add(int(inp) - next_valid_advance)
else:
break
print(f"{possible_results=}")
calibrations = find_valid_calibration(post_menu_state, possible_results)
print("Possible Calibrations:")
for fly_value, npc_count in calibrations:
print(f"{fly_value=} {npc_count=}")
def bruteforce_valid_advances(state: tuple[int, int], target_advance: int, fly_value: int, npc_count: int) -> list[int]:
valid_advances = []
rng = Xoroshiro(*state)
for start in range(target_advance):
if start != 0:
rng.next()
test_rng = Xoroshiro(*rng.state)
memory_rand, jump = test_rng.rand_count(100)
if not (5 <= (memory_rand + 1)):
continue
for _ in range(fly_value):
_, adv = test_rng.rand_count(100)
jump += adv
for _ in range(npc_count):
_, adv = test_rng.rand_count(91)
jump += adv
if start + jump == target_advance:
valid_advances.append(start)
return valid_advances
def advance_conversion():
state_0 = int(input("State 0: 0x"), 16)
state_1 = int(input("State 1: 0x"), 16)
fly_value = int(input("Fly Value: "))
npc_count = int(input("NPC Count: "))
while True:
inp = input("Target Advance (enter to exit program): ")
if inp:
target_advance = int(inp)
test_rng = Xoroshiro(state_0, state_1)
bruteforce_start = max(0, target_advance - BRUTEFORCE_RANGE)
for _ in range(bruteforce_start):
test_rng.next()
for result in bruteforce_valid_advances(test_rng.state, target_advance - bruteforce_start, fly_value, npc_count):
print(f"Enter menu at advance: {result+bruteforce_start}")
print("-"*20)
else:
break
if __name__ == "__main__":
if int(input("(0) RNG | (1) Calibrate: ")):
calibrate()
else:
advance_conversion()
# search config
# find in clear weather based on menu close advances
NPC_COUNT = 0
# *roughly* tied to 60x the number of seconds between leaving the menu and the encounter generating
MIN_WEATHER_VALUE = 0
MAX_WEATHER_VALUE = 60
# rng config
BRUTEFORCE_RANGE = 5000
class Xoroshiro:
def __init__(self, seed_0, seed_1 = 0x82A2B175229D6A5B):
self.state = [seed_0, seed_1]
@staticmethod
def rotl(x, k):
return ((x << k) | (x >> (64 - k))) & 0xFFFFFFFFFFFFFFFF
@staticmethod
def get_mask(x):
x -= 1
for i in range(6):
x |= x >> (1 << i)
return x
def next(self):
s0, s1 = self.state
result = (s0 + s1) & 0xFFFFFFFFFFFFFFFF
s1 ^= s0
self.state = [self.rotl(s0, 24) ^ s1 ^ ((s1 << 16) & 0xFFFFFFFFFFFFFFFF), self.rotl(s1, 37)]
return result
def rand_count(self, maximum = 0xFFFFFFFF):
mask = self.get_mask(maximum)
res = self.next() & mask
count = 1
while res >= maximum:
res = self.next() & mask
count += 1
return res, count
def find_valid_calibration(rng_state: tuple[int, int], leaving_pokemon_menu: bool, possible_results: set[int]) -> list[int]:
calibrations = []
for weather_value in range(MIN_WEATHER_VALUE, MAX_WEATHER_VALUE, 2):
test_rng = Xoroshiro(*rng_state)
jump = 0
for _ in range(4 + 6 if leaving_pokemon_menu else 4):
_, adv = test_rng.rand_count(20001)
jump += adv
for _ in range(NPC_COUNT):
_, adv = test_rng.rand_count(91)
jump += adv
for _ in range(weather_value):
_, adv = test_rng.rand_count(20001)
jump += adv
if jump in possible_results:
calibrations.append(weather_value)
return calibrations
def calibrate():
state_0 = int(input("State 0: 0x"), 16)
state_1 = int(input("State 1: 0x"), 16)
current_advance = int(input("Current Advance: "))
leaving_pokemon_menu = bool(int(input("Leaving pokemon menu? (0/1): ")))
rng = Xoroshiro(state_0, state_1)
for _ in range(current_advance):
rng.next()
next_valid_advance = current_advance
possible_results = set()
while True:
inp = input("Possible hit advance (enter to confirm list): ")
if inp:
possible_results.add(int(inp) - next_valid_advance)
else:
break
print(f"{possible_results=}")
calibrations = find_valid_calibration(tuple(rng.state), leaving_pokemon_menu, possible_results)
print("Possible Weather Values:")
print(calibrations)
def bruteforce_valid_advances(state: tuple[int, int], target_advance: int, leaving_pokemon_menu: bool, weather_value: int) -> list[int]:
valid_advances = []
rng = Xoroshiro(*state)
for start in range(target_advance):
if start != 0:
rng.next()
test_rng = Xoroshiro(*rng.state)
jump = 0
for _ in range(4 + 6 if leaving_pokemon_menu else 4):
_, adv = test_rng.rand_count(20001)
jump += adv
for _ in range(NPC_COUNT):
_, adv = test_rng.rand_count(91)
jump += adv
for _ in range(weather_value):
_, adv = test_rng.rand_count(20001)
jump += adv
if start + jump == target_advance:
valid_advances.append(start)
return valid_advances
def advance_conversion():
state_0 = int(input("State 0: 0x"), 16)
state_1 = int(input("State 1: 0x"), 16)
weather_value = int(input("Weather Value: "))
leaving_pokemon_menu = bool(int(input("Leaving pokemon menu? (0/1): ")))
while True:
inp = input("Target Advance (enter to exit program): ")
if inp:
target_advance = int(inp)
test_rng = Xoroshiro(state_0, state_1)
bruteforce_start = max(0, target_advance - BRUTEFORCE_RANGE)
for _ in range(bruteforce_start):
test_rng.next()
for result in bruteforce_valid_advances(test_rng.state, target_advance - bruteforce_start, leaving_pokemon_menu, weather_value):
print(f"Exit menu at advance: {result+bruteforce_start}")
print("-"*20)
else:
break
if __name__ == "__main__":
if int(input("(0) RNG | (1) Calibrate: ")):
calibrate()
else:
advance_conversion()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment