My Advent of Code solutions in Python.
kevinyap.ca/2019/12/going-fast-in-advent-of-code/
advent-of-code
python
1import fileinput
2from collections import defaultdict
3
4from utils import Point, N, NE, E, SE, S, SW, W, NW, min_max_xy
5
6
7BOARD = {}
8
9for y, line in enumerate(fileinput.input()):
10 for x, c in enumerate(line.strip()):
11 BOARD[Point(x, y)] = c
12
13# N and S are switched from the problem statement because of
14# the convention of using +y -> N in my utils, which does have
15# an effect on the interaction between elves in this problem.
16CHECKS = [
17 [[S, SE, SW], S],
18 [[N, NE, NW], N],
19 [[W, NW, SW], W],
20 [[E, NE, SE], E],
21]
22
23for i in range(100000):
24 proposal = defaultdict(list)
25 for p, c in BOARD.items():
26 if c == '#':
27 # If elf has no neighbours in all 8 directions, don't consider moving.
28 if not any(BOARD.get(n) == '#' for n in p.neighbours_8()):
29 continue
30
31 # Add elf's proposed location to `proposal`.
32 for d in range(4):
33 checks, move = CHECKS[(i + d) % 4]
34 if any(BOARD.get(p + c, ' ') == '#' for c in checks):
35 continue
36
37 proposal[p + move].append(p)
38 break
39
40 for new_loc in proposal:
41 elves = proposal[new_loc]
42 # If only one elf wants to move there, they can move (and vacate their old spot).
43 if len(elves) == 1:
44 BOARD[elves[0]] = '.'
45 BOARD[new_loc] = '#'
46
47 # Answer to part 1, compute number of empty squares within the bounds.
48 if i == 9:
49 elves = [p for p in BOARD if BOARD[p] == '#']
50 min_x, max_x, min_y, max_y = min_max_xy(elves)
51 blanks = 0
52 for y in range(min_y, max_y + 1):
53 for x in range(min_x, max_x + 1):
54 p = Point(x, y)
55 if BOARD.get(p) != '#':
56 blanks += 1
57
58 print("Part 1:", blanks)
59
60 # Empty proposal list -> no elves moved, the answer to part 2.
61 if len(proposal) == 0:
62 print("Part 2:", i + 1)
63 break