aoc2022/day17.py

141 lines
4.3 KiB
Python

from tools.aoc import AOCDay
from typing import Any
ROCK_SHAPES = [
["####"],
[' # ', '###', ' # '],
[' #', ' #', '###'],
['#', '#', '#', '#'],
['##', '##']
]
class Chamber(list):
def hash(self):
max_len = min(30, len(self) - 1)
return "".join(self[i] for i in range(max_len))
def collide_hor(chamber: list, rock_shape:list, rock_x: int, rock_y: int) -> bool:
if rock_x < 0 or rock_x > 7 - len(rock_shape[0]):
return True
for i, line in enumerate(rock_shape):
for x, c in enumerate(rock_shape[i]):
if c == ' ':
continue
y = rock_y - ((len(rock_shape) - 1) - i)
if y < 0:
continue
if chamber[y][rock_x + x] == '#':
return True
return False
def settles(chamber: list, rock_shape: list, rock_x: int, rock_y: int) -> bool:
for i, c in enumerate(rock_shape[-1]):
if c == ' ':
continue
if rock_y >= -1 and chamber[rock_y + 1][i + rock_x] == '#':
return True
if rock_shape[-1] == ' # ':
for i, c in enumerate(rock_shape[-2]):
if c == ' ':
continue
if rock_y > -1 and chamber[rock_y][i + rock_x] == '#':
return True
return False
def drop_rock(chamber: list, rock_shape: list, jet: list, jet_index: int) -> int:
jet_len = len(jet)
rock_index_x = 2
for i in range(4):
rock_index_x += jet[(jet_index + i) % jet_len]
if not (0 <= rock_index_x <= 7 - len(rock_shape[0])):
rock_index_x = 0 if rock_index_x < 0 else 7 - len(rock_shape[0])
jet_index = (jet_index + 4) % jet_len
rock_index_y = -1
while not settles(chamber, rock_shape, rock_index_x, rock_index_y):
rock_index_y += 1
if not collide_hor(chamber, rock_shape, rock_index_x + jet[jet_index], rock_index_y):
rock_index_x += jet[jet_index]
jet_index = (jet_index + 1) % jet_len
for i in range(len(rock_shape) - 1, -1, -1):
y = rock_index_y + i - len(rock_shape) + 1
if y < 0:
chamber.insert(0, ' ')
y = 0
left = chamber[y][:rock_index_x]
middle = "".join(
"#" if rock_shape[i][x] == '#' or chamber[y][rock_index_x + x] == '#' else ' '
for x in range(len(rock_shape[i]))
)
right = chamber[y][rock_index_x + len(rock_shape[i]):]
chamber[y] = left + middle + right
return jet_index
class Day(AOCDay):
inputs = [
[
(3068, "input17_test"),
(3059, "input17"),
],
[
(1514285714288, "input17_test"),
(1500874635587, "input17"),
]
]
def get_jet_pattern(self) -> list:
return [-1 if x == '<' else 1 for x in self.getInput()]
def part1(self) -> Any:
chamber = ['#######']
jet = self.get_jet_pattern()
jet_index = 0
for rock_fall in range(2022):
jet_index = drop_rock(chamber, ROCK_SHAPES[rock_fall % 5], jet, jet_index)
return len(chamber) - 1
def part2(self) -> Any:
chamber = Chamber(['#######'])
jet = self.get_jet_pattern()
jet_index = drop_rock(chamber, ROCK_SHAPES[0], jet, 0)
rock_index = 1
cycle = 1
cycle_cache = {}
while (jet_index, rock_index, chamber.hash()) not in cycle_cache:
cycle_cache[(jet_index, rock_index, chamber.hash())] = (cycle, len(chamber) - 1)
jet_index = drop_rock(chamber, ROCK_SHAPES[rock_index], jet, jet_index)
rock_index = (rock_index + 1) % 5
cycle += 1
cycle_start, cycle_start_len = cycle_cache[(jet_index, rock_index, chamber.hash())]
cycle_height = len(chamber) - 1 - cycle_start_len
cycle_len = cycle - cycle_start
chamber = Chamber(['#######'])
jet_index = 0
rock_index = 0
bottom_part = 1_000_000_000_000 % cycle_len
for _ in range(cycle_len + bottom_part):
jet_index = drop_rock(chamber, ROCK_SHAPES[rock_index % 5], jet, jet_index)
rock_index += 1
cur_len = len(chamber) - 1 - cycle_height
return (1_000_000_000_000 // cycle_len * cycle_height) + cur_len
if __name__ == '__main__':
day = Day(2022, 17)
day.run(verbose=True)