|
@@ -0,0 +1,116 @@
|
|
|
+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))
|
|
|
+
|