lines = [line.split() for line in open("17.input")] jets = lines[0][0] blocks = [ [(0, 0), (1, 0), (2, 0), (3, 0)], [(1, 0), (0, 1), (1, 1), (2, 1), (1, 2)], [(2, 0), (2, 1), (0, 2), (1, 2), (2, 2)], [(0, 0), (0, 1), (0, 2), (0, 3)], [(0, 0), (0, 1), (1, 0), (1, 1)], ] def show(occupied): stack_height = min([pos[1] for pos in occupied] + [0]) for y in range(stack_height, 0): print("|", end='') for x in range(7): if (x, y) in occupied: print("#", end='') else: print(".", end='') print("|") print("-" * 9) def add(a, b): return (a[0] + b[0], a[1] + b[1]) def run(target_blocks): occupied = set([(x, 0) for x in range(9)]) jet_idx = 0 combos = set() target = None found = [] block_count = 0 time_skip = False while block_count < target_blocks: block_idx = block_count % len(blocks) stack_height = min([pos[1] for pos in occupied] + [0]) if not found: if (jet_idx, block_idx) in combos: # Set our target for time skip target = (jet_idx, block_idx) found.append((block_count, stack_height)) print("Found repetition after", block_count, "blocks") else: combos.add((jet_idx, block_idx)) elif not time_skip: if (jet_idx, block_idx) == target: print("Found again after", block_count) found.append((block_count, stack_height)) if len(found) == 2: chunk_blocks = found[-1][0] - found[-2][0] chunk_height = -(found[-1][1] - found[-2][1]) extra_chunks = (target_blocks - block_count) // chunk_blocks extra_blocks = extra_chunks * chunk_blocks block_count += extra_blocks extra_height = extra_chunks * chunk_height print(f"Performing time-skip (+{extra_blocks} blocks, +{extra_height} height).") occupied = {add(pos, (0, -extra_height)) for pos in occupied} time_skip = True continue block = blocks[block_idx] b_height = max(pos[1] for pos in block) start_pos = (2, stack_height - b_height - 4) block = [add(pos, start_pos) for pos in block] #show(occupied.union(block)) while True: # Jetting max_x = max(pos[0] for pos in block) min_x = min(pos[0] for pos in block) jet = jets[jet_idx] jet_idx += 1 jet_idx = jet_idx % len(jets) if min_x > 0 and jet == '<': next_block = {add(pos, (-1, 0)) for pos in block} elif max_x < 6 and jet == '>': next_block = {add(pos, (1, 0)) for pos in block} if not next_block.intersection(occupied): block = next_block # Falling next_block = {add(pos, (0, 1)) for pos in block} if next_block.intersection(occupied): occupied = occupied.union(block) break else: block = next_block block_count += 1 stack_height = min([pos[1] for pos in occupied] + [0]) return -stack_height print("Part 1:", run(2022)) print("Part 2:", run(1000000000000))