from parse_cart import Pico8Cart import struct import zlib def main(): # No need to store any seeds for level 2 or 3: all 5000 of the first 5000 seeds are fine # We could use negencode_delta_4b, which is pretty uneventful # level_1 = load_seeds("input/level_1.txt")[:5000] # analyze(level_1) # level_2 = load_seeds("input/level_2.txt")[:5000] # analyze(level_2) # level_3 = load_seeds("input/level_3.txt")[:5000] # analyze(level_3) level_4 = load_seeds("input/level_4.txt")[:5000] # analyze(level_4) level_5 = load_seeds("input/level_5.txt")[:5000] # analyze(level_5) ff = load_seeds("input/fortunes_foundation.txt") # analyze(ff) # level_7 = load_seeds("input/level_7.txt")[:5000] # analyze(level_7) level_4_data = negencode_16b(level_4) level_5_data = negencode_delta_4b(level_5) ff_data = delta_4b(ff) all_data = b"" offsets = {} for block, data in [ ("l4", level_4_data), ("l5", level_5_data), ("ff", ff_data) ]: offsets[f"{block}_start"] = len(all_data) all_data += data offsets[f"{block}_end"] = len(all_data) augment_map("../main.p8", "../seed_constants.lua", all_data, offsets) def augment_map(target, constants_file, binary, offsets): assert isinstance(binary, bytes) and len(binary) < 8192 # length of mapdata print(f"Length of basic extra map data: {len(binary)}") mapdata = (binary + bytes([0] * 8192))[:8192] cart = Pico8Cart.load(target) def touch_map(memory): memory[0x0:0x1000] = mapdata[0x1000:0x2000] def touch_gfx(memory): memory[0x1000:0x2000] = mapdata[0x0000:0x1000] cart.touch("__map__", touch_map) cart.touch("__gfx__", touch_gfx) cart.save(target) with open(constants_file, "wt") as f: f.write("seed_constants={\n") for i, (k, v) in enumerate(offsets.items()): sep = "," if i < len(offsets) - 1 else "" f.write(f" {k}={v+0x1000}{sep}\n") f.write("}\n") def load_seeds(fname): seeds = set() with open(fname, "rt") as f: for line in f: seeds.add(int(line)) return list(sorted(seeds)) def analyze(seeds): def peek_at_seeds(seeds): print("Seeds modulo various") for i in range(0, 30): matches = [s for s in seeds if s%30 == i] print("- {}: {} (max {})".format(i, len(matches), max(matches))) print() peek_at_seeds(seeds) # seeds=seeds[:8192] # stick to the range with a realistic distribution print("{} seeds".format(len(seeds))) for encoding in [ naive, bitfield, delta_8b, delta_2b, delta_3b, delta_4b, delta_5b, zlib_delta_4b, zlib_delta_8b, negencode_delta_4b, negencode_16b, ]: print("{} encoding: {} bytes".format(encoding.__name__, len(encoding(seeds)))) def naive(seeds): return b"".join(struct.pack(" 255: out += struct.pack(" 15: out_nibbles.append(0) acc += 15 else: out_nibbles.append(diff) acc = seeds[i] i += 1 while len(out_nibbles) % 2 != 0: out_nibbles.append(0) out = b"" for i in range(0, len(out_nibbles), 2): out += bytes([(out_nibbles[i] << 4) + out_nibbles[i+1]]) return out def zlib_delta_4b(seeds): return zlib.compress(delta_4b(seeds)) def delta_2b(seeds): return delta_nb(seeds, 2) def delta_3b(seeds): return delta_nb(seeds, 3) def delta_5b(seeds): return delta_nb(seeds, 5) def delta_nb(seeds, n): out_fibbles = [] acc = 0 i = 0 while i < len(seeds): diff = seeds[i] - acc if diff > (1<