2022 Day 17 Python
This commit is contained in:
parent
66b17d96ba
commit
fead965600
|
@ -0,0 +1,124 @@
|
|||
from helpers import *
|
||||
|
||||
gas_cycles = read_day(17)
|
||||
sample_gas_cycles = '>>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>'
|
||||
|
||||
rocks = '''
|
||||
####
|
||||
|
||||
.#.
|
||||
###
|
||||
.#.
|
||||
|
||||
..#
|
||||
..#
|
||||
###
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
##
|
||||
##
|
||||
'''.strip().split('\n\n')
|
||||
# rock_shapes = [np.array([[1 if c=='#' else 0 for c in line] for line in r.split('\n')], dtype=np.int8).T for r in rocks]
|
||||
rock_shapes = [np.fliplr(np.array([[1 if c=='#' else 0 for c in line] for line in r.split('\n')], dtype=np.int8).T) for r in rocks]
|
||||
|
||||
def simulate_rockfall(gas_cycle: str, num_rocks=50000, debug=False) -> ArrayLike: # returns height increment per rock
|
||||
def p(message):
|
||||
if debug:
|
||||
print(message)
|
||||
|
||||
width = 7
|
||||
height_5_rocks = 1+3+3+4+2
|
||||
max_possible_height = (num_rocks//5 + 2) * height_5_rocks
|
||||
len_gas_cycle = len(gas_cycle)
|
||||
|
||||
field = np.zeros((7, max_possible_height), dtype=np.int8)
|
||||
height_increments = np.zeros(num_rocks, dtype=np.int8)
|
||||
highest = 0
|
||||
t = -1
|
||||
for r in range(num_rocks):
|
||||
rock_shape = rock_shapes[r%5]
|
||||
r_width, r_height = rock_shape.shape
|
||||
|
||||
def check_collision(r_x, r_y):
|
||||
r_x2 = r_x + r_width
|
||||
r_y2 = r_y + r_height
|
||||
if r_x < 0:
|
||||
p(f'would hit left wall - {r_x, r_x2} at y={r_y}')
|
||||
return True
|
||||
if r_x2 > width:
|
||||
p(f'would hit right wall - {r_x, r_x2} at y={r_y}')
|
||||
return True
|
||||
if r_y < 0:
|
||||
p(f'would hit floor - {r_x, r_x2} at y={r_y}')
|
||||
return True
|
||||
return np.any(field[r_x:r_x2, r_y:r_y2] & rock_shape == 1)
|
||||
|
||||
# Spawn rock with bottom-left corner at x=2, y=highest+3 (y-up)
|
||||
r_x = 2
|
||||
r_y = highest + 3
|
||||
while True:
|
||||
t += 1
|
||||
if gas_cycle[t%len_gas_cycle] == '>':
|
||||
if not check_collision(r_x+1, r_y): # move right
|
||||
r_x += 1
|
||||
p('moved right')
|
||||
else:
|
||||
if not check_collision(r_x-1, r_y): # move left
|
||||
r_x -= 1
|
||||
p('moved left')
|
||||
# move down
|
||||
if check_collision(r_x, r_y-1):
|
||||
field[r_x:r_x+r_width, r_y:r_y+r_height] |= rock_shape
|
||||
old_highest = highest
|
||||
highest = max(highest, r_y+r_height)
|
||||
height_increments[r] = highest - old_highest
|
||||
break
|
||||
else:
|
||||
r_y -= 1
|
||||
p('moved down')
|
||||
p(f'rock #{r} landed, highest point {highest}, gas cycle {t}, rock shape ({r_width}x{r_height}) at {r_x},{r_y}')
|
||||
return height_increments
|
||||
|
||||
sample_incs = simulate_rockfall(sample_gas_cycles, 10000)
|
||||
incs = simulate_rockfall(gas_cycles, 30000)
|
||||
print(f'Part 1 (sample): {sample_incs[:2022].sum()}')
|
||||
print(f'Part 1: {incs[:2022].sum()}')
|
||||
|
||||
|
||||
def find_cycle_and_infer(increments: ArrayLike, search_cycle_length: int, target_index: int):
|
||||
search_term = increments[search_cycle_length:search_cycle_length*2]
|
||||
hits = []
|
||||
for st in range(search_cycle_length*2, len(increments)-search_cycle_length):
|
||||
if np.all(increments[st:st+search_cycle_length] == search_term):
|
||||
hits.append(st)
|
||||
print(f'Found loop starting at {st}')
|
||||
if len(hits) > 2:
|
||||
break
|
||||
else:
|
||||
print('no loop found!')
|
||||
return
|
||||
|
||||
loop_len = hits[1]-hits[0]
|
||||
loop_len2 = hits[2]-hits[1]
|
||||
if loop_len2 == loop_len:
|
||||
print(f'Confirmed loop of length {loop_len}')
|
||||
else:
|
||||
print(f'Mismatched loop lengths of {loop_len} and {loop_len2}!')
|
||||
return
|
||||
|
||||
base_value = increments[:hits[0]].sum()
|
||||
loop_values = increments[hits[0]:hits[1]].cumsum()
|
||||
loop_total = loop_values[-1]
|
||||
|
||||
loops, remainder = divmod(target_index - hits[0], loop_len)
|
||||
value = base_value + loop_total*loops
|
||||
if remainder > 0:
|
||||
value += loop_values[remainder-1]
|
||||
return value
|
||||
|
||||
print(f'Part 2 (sample): {find_cycle_and_infer(sample_incs, 1000, 1000000000000)}')
|
||||
print(f'Part 2: {find_cycle_and_infer(incs, 5000, 1000000000000)}')
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue