143 lines
4.4 KiB
Python
143 lines
4.4 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"),
|
|
(3067, "input17_dennis"),
|
|
(3059, "input17"),
|
|
],
|
|
[
|
|
(1514285714288, "input17_test"),
|
|
(1514369501484, "input17_dennis"),
|
|
(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)
|