17.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. lines = [line.split() for line in open("17.input")]
  2. jets = lines[0][0]
  3. blocks = [
  4. [(0, 0), (1, 0), (2, 0), (3, 0)],
  5. [(1, 0), (0, 1), (1, 1), (2, 1), (1, 2)],
  6. [(2, 0), (2, 1), (0, 2), (1, 2), (2, 2)],
  7. [(0, 0), (0, 1), (0, 2), (0, 3)],
  8. [(0, 0), (0, 1), (1, 0), (1, 1)],
  9. ]
  10. def show(occupied):
  11. stack_height = min([pos[1] for pos in occupied] + [0])
  12. for y in range(stack_height, 0):
  13. print("|", end='')
  14. for x in range(7):
  15. if (x, y) in occupied:
  16. print("#", end='')
  17. else:
  18. print(".", end='')
  19. print("|")
  20. print("-" * 9)
  21. def add(a, b):
  22. return (a[0] + b[0], a[1] + b[1])
  23. def run(target_blocks):
  24. occupied = set([(x, 0) for x in range(9)])
  25. jet_idx = 0
  26. combos = set()
  27. target = None
  28. found = []
  29. block_count = 0
  30. time_skip = False
  31. while block_count < target_blocks:
  32. block_idx = block_count % len(blocks)
  33. stack_height = min([pos[1] for pos in occupied] + [0])
  34. if not found:
  35. if (jet_idx, block_idx) in combos:
  36. # Set our target for time skip
  37. target = (jet_idx, block_idx)
  38. found.append((block_count, stack_height))
  39. print("Found repetition after", block_count, "blocks")
  40. else:
  41. combos.add((jet_idx, block_idx))
  42. elif not time_skip:
  43. if (jet_idx, block_idx) == target:
  44. print("Found again after", block_count)
  45. found.append((block_count, stack_height))
  46. if len(found) == 2:
  47. chunk_blocks = found[-1][0] - found[-2][0]
  48. chunk_height = -(found[-1][1] - found[-2][1])
  49. extra_chunks = (target_blocks - block_count) // chunk_blocks
  50. extra_blocks = extra_chunks * chunk_blocks
  51. block_count += extra_blocks
  52. extra_height = extra_chunks * chunk_height
  53. print(f"Performing time-skip (+{extra_blocks} blocks, +{extra_height} height).")
  54. occupied = {add(pos, (0, -extra_height)) for pos in occupied}
  55. time_skip = True
  56. continue
  57. block = blocks[block_idx]
  58. b_height = max(pos[1] for pos in block)
  59. start_pos = (2, stack_height - b_height - 4)
  60. block = [add(pos, start_pos) for pos in block]
  61. #show(occupied.union(block))
  62. while True:
  63. # Jetting
  64. max_x = max(pos[0] for pos in block)
  65. min_x = min(pos[0] for pos in block)
  66. jet = jets[jet_idx]
  67. jet_idx += 1
  68. jet_idx = jet_idx % len(jets)
  69. if min_x > 0 and jet == '<':
  70. next_block = {add(pos, (-1, 0)) for pos in block}
  71. elif max_x < 6 and jet == '>':
  72. next_block = {add(pos, (1, 0)) for pos in block}
  73. if not next_block.intersection(occupied):
  74. block = next_block
  75. # Falling
  76. next_block = {add(pos, (0, 1)) for pos in block}
  77. if next_block.intersection(occupied):
  78. occupied = occupied.union(block)
  79. break
  80. else:
  81. block = next_block
  82. block_count += 1
  83. stack_height = min([pos[1] for pos in occupied] + [0])
  84. return -stack_height
  85. print("Part 1:", run(2022))
  86. print("Part 2:", run(1000000000000))