87 lines
2.6 KiB
Python
87 lines
2.6 KiB
Python
|
from helpers import *
|
||
|
lines = read_day(23).split('\n')
|
||
|
sample_lines = '''
|
||
|
....#..
|
||
|
..###.#
|
||
|
#...#.#
|
||
|
.#...##
|
||
|
#.###..
|
||
|
##.#.##
|
||
|
.#..#..
|
||
|
'''.strip().split('\n')
|
||
|
|
||
|
def get_intention(round, elves, x, y):
|
||
|
adj_nw = (x-1,y-1) in elves
|
||
|
adj_w = (x-1,y) in elves
|
||
|
adj_sw = (x-1,y+1) in elves
|
||
|
adj_n = (x,y-1) in elves
|
||
|
adj_s = (x,y+1) in elves
|
||
|
adj_ne = (x+1,y-1) in elves
|
||
|
adj_e = (x+1,y) in elves
|
||
|
adj_se = (x+1,y+1) in elves
|
||
|
if adj_nw or adj_n or adj_ne or adj_w or adj_e or adj_sw or adj_s or adj_se:
|
||
|
for i in range(4):
|
||
|
direction = (round + i) % 4
|
||
|
if (direction == 0) and not (adj_n or adj_nw or adj_ne):
|
||
|
return (x,y-1) # move north
|
||
|
if (direction == 1) and not (adj_s or adj_sw or adj_se):
|
||
|
return (x,y+1) # move south
|
||
|
if (direction == 2) and not (adj_w or adj_nw or adj_sw):
|
||
|
return (x-1,y) # move west
|
||
|
if (direction == 3) and not (adj_e or adj_ne or adj_se):
|
||
|
return (x+1,y) # move east
|
||
|
return (x,y) # no movement
|
||
|
|
||
|
def simulate(lines: list[str], rounds: int = 10) -> set[tuple[int,int]]:
|
||
|
# position = np.array([[c=='#' for c in line] for line in lines], dtype=np.bool8)
|
||
|
elves = set()
|
||
|
for row, line in enumerate(lines):
|
||
|
for col, c in enumerate(line):
|
||
|
if c == '#':
|
||
|
elves.add((col, row))
|
||
|
|
||
|
for round in range(rounds):
|
||
|
if round == 10:
|
||
|
print(f'*** Part 1: ground tiles after round #10: {get_rect(elves)} ***')
|
||
|
# else:
|
||
|
# print(f'\tground tiles before round #{round+1}: {get_rect(elves)}')
|
||
|
# print(f'performing round #{round+1}')
|
||
|
next_elves = {}
|
||
|
for x,y in sorted(elves, key=lambda p: (p[1], p[0])):
|
||
|
intention = get_intention(round, elves, x, y)
|
||
|
# print(f'{x,y} wants to move to {intention}')
|
||
|
next_elves[intention] = next_elves.get(intention, []) + [(x,y)]
|
||
|
old_elves = elves
|
||
|
elves = set()
|
||
|
for position, elf_intentions in next_elves.items():
|
||
|
if len(elf_intentions) == 1:
|
||
|
elves.add(position)
|
||
|
else:
|
||
|
# print(f'conflict: {len(elf_intentions)} elves tried to move to {position}')
|
||
|
elves |= set(elf_intentions) # neither move
|
||
|
if old_elves == elves:
|
||
|
print(f'*** Part 2: No elves moved in round #{round+1} ***')
|
||
|
break
|
||
|
print(f'Final ground tiles: {get_rect(elves)}')
|
||
|
return elves
|
||
|
|
||
|
def get_rect(elves: set[tuple[int,int]]):
|
||
|
x0, x1, y0, y1 = None, None, None, None
|
||
|
for x,y in elves:
|
||
|
if x0 is None or x < x0:
|
||
|
x0 = x
|
||
|
if x1 is None or x > x1:
|
||
|
x1 = x
|
||
|
if y0 is None or y < y0:
|
||
|
y0 = y
|
||
|
if y1 is None or y > y1:
|
||
|
y1 = y
|
||
|
# print(f'Elves in rectangle from x:{x0}~{x1}, y:{y0}~{y1}')
|
||
|
# for y in range(y0, y1+1):
|
||
|
# print(''.join(('#' if (x,y) in elves else '.' for x in range(x0,x1+1))))
|
||
|
rect_size = (x1-x0+1)*(y1-y0+1) # inclusive
|
||
|
return rect_size - len(elves)
|
||
|
|
||
|
simulate(sample_lines, 100)
|
||
|
simulate(lines, 100000)
|