Skip to content

Instantly share code, notes, and snippets.

@SciresM
Last active March 28, 2021 22:28
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SciresM/967853862da6bdf4e079b4548199df1d to your computer and use it in GitHub Desktop.
Save SciresM/967853862da6bdf4e079b4548199df1d to your computer and use it in GitHub Desktop.
Ushabti predictor for Spelunky 2 seeded runs.
import sys
# Spelunky 2 ushabti prediction for seeded runs, author SciresM.
def u32(v):
return v & 0xFFFFFFFF
def u64(v):
return v & 0xFFFFFFFFFFFFFFFF
def rotate_left64(a, b):
return u64((((a) << (b)) | ((a) >> (64 - (b)))))
def expand_32_bits(seed):
x = u32(seed)
x = u64(-x) if x != 0 else 1
x = u64(0x9E6C63D0676A9A99 * x)
x = (((x >> 28) ^ x) >> 23) ^ x
x = u64(0x9E6C63D0676A9A99 * x)
y = (((x >> 28) ^ x) >> 23) ^ x
x = u64(0x9E6C63D0676A9A99 * rotate_left64(x, 27))
return (x, y)
def advance(x, y):
return u64(0xD3833E804F4C574B * y), rotate_left64(u64(y - x), 27)
def rand(state, limit):
return (u32(state[0]) * limit) >> 32
def generate_initial_randomness(seed):
# Generate initial randomness sources.
x, y = expand_32_bits(seed)
x |= 1
y = u64(0xD3833E804F4C574B * y)
y = u64(-y) if y != 0 else 1
y = u64(0x9E6C63D0676A9A99 * y)
y = (((y >> 28) ^ y) >> 23) ^ y
y = u64(0x9E6C63D0676A9A99 * y)
y = u64(0x9E6C63D0676A9A99 * rotate_left64(y, 27))
return (x, u64(x + y))
def generate_random_table(seed):
table = []
x, y = expand_32_bits(seed)
# Spelunky 2 1.19.7a added an extra entry to the table, changing the seeds used for generation.
# For pre-1.19.7a, edit this length to 9.
while len(table) != 10:
table.append(expand_32_bits(x))
x, y = advance(x, y)
return table[::-1]
def get_style(u):
assert 0 <= u and u < 100
return [['cracked', 'simple'], ['smiling', 'tall']][u // 50][(u % 10) // 5]
def get_material(u):
assert 0 <= u and u < 100
return ['clay', 'gold', 'jade', 'wood', 'onyx'][u % 5]
def get_symbol(u):
assert 0 <= u and u < 100
return ['eye', 'ankh', 'snake', 'vortex', 'bat'][(u // 10) % 5]
def generate_ushabti(seed):
# Generate initial randomness.
init_rnd = generate_initial_randomness(seed)
# Derive the game seed.
game_seed = (u32(init_rnd[1]) ^ u32(init_rnd[1] >> 32))
# Populate the random tables for the game seed.
tables = generate_random_table(game_seed)
# Generate the winning ushabti.
return rand(tables[2], 100)
def main(argc, argv):
if argc != 2:
print 'Usage: %s seed' % argv[0]
return 1
seed = int(argv[1], 16)
u = generate_ushabti(seed)
print '%08X: A %ls servant of %ls marked by the %ls shall open the door for the Aspirant.' % (seed, get_style(u), get_material(u), get_symbol(u))
return 0
if __name__ == '__main__':
sys.exit(main(len(sys.argv), sys.argv))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment