|
1 | 1 | #!/usr/bin/env python3 |
2 | 2 | import sys |
3 | 3 | from collections import Counter, defaultdict |
| 4 | +from operator import add |
4 | 5 | from typing import Tuple, List, Counter as TypeCounter |
5 | 6 |
|
6 | 7 |
|
7 | 8 |
|
8 | | -def check_neighbors(map, row_num, col_num, symbol) -> int: |
| 9 | +def check_neighbors(seatmap, row_num: int, col_num: int, symbol: str, direct: bool = True) -> int: |
9 | 10 | """return the number of symbols around the seat specified |
10 | 11 | by row and col""" |
11 | 12 | results = 0 |
12 | | - |
13 | | - copy = map.keys() |
14 | | - # print("neighbors:", row_num, col_num) |
15 | | - for row in range(row_num-1, row_num+2): |
16 | | - for col in range(col_num-1,col_num+2): |
17 | | - if (row, col) in copy: |
18 | | - #print(row, col) |
19 | | - # dont check the seat itself :) |
20 | | - if map[(row, col)] == symbol: |
21 | | - if not (row, col) == (row_num, col_num): |
22 | | - results += 1 |
23 | | - # print("res:", results) |
| 13 | + |
| 14 | + |
| 15 | + copy = seatmap.keys() |
| 16 | + if not direct: |
| 17 | + for row in range(row_num-1, row_num+2): |
| 18 | + for col in range(col_num-1,col_num+2): |
| 19 | + if (row, col) in copy: |
| 20 | + # dont check the seat itself :) |
| 21 | + if seatmap[(row, col)] == symbol: |
| 22 | + if not (row, col) == (row_num, col_num): |
| 23 | + results += 1 |
| 24 | + # print("res:", results) |
| 25 | + else: |
| 26 | + #build direction vectors: |
| 27 | + for dir in [(0, -1), (0, 1), (-1, 0), (1, 0), (-1, -1), (-1, 1), (1, -1), (1, 1)]: |
| 28 | + check_key = tuple(map(add, (row_num, col_num), dir)) |
| 29 | + while check_key in copy: |
| 30 | + if seatmap[check_key] == symbol: |
| 31 | + results += 1 |
| 32 | + break |
| 33 | + # stop looking if theres an empty seat |
| 34 | + elif seatmap[check_key] == "L": |
| 35 | + break |
| 36 | + |
| 37 | + check_key = tuple(map(add, check_key, dir)) |
| 38 | + |
| 39 | + # check if im being stupid :) |
| 40 | + assert(results <= 8) |
24 | 41 | return results |
25 | 42 |
|
26 | 43 |
|
27 | 44 |
|
28 | 45 |
|
29 | | -def calculate_step(map: List[List[str]]) -> Tuple[bool, List[List[str]]]: |
| 46 | +def calculate_step(seatmap: List[List[str]], lim: int = 4, direct: bool = True) -> Tuple[bool, List[List[str]]]: |
30 | 47 | changes = [] |
31 | | - next_map = defaultdict(str) |
32 | | - for entry in map.items(): |
| 48 | + next_seatmap = defaultdict(str) |
| 49 | + for entry in seatmap.items(): |
33 | 50 | row_num, col_num = entry[0] |
34 | 51 | seat = entry[1] |
35 | 52 | next_seat = seat |
36 | | - num_neighbors = check_neighbors(map, row_num, col_num, "#") |
| 53 | + num_neighbors = check_neighbors(seatmap, row_num, col_num, "#", direct) |
37 | 54 | if seat == "L": |
38 | 55 | if num_neighbors == 0: |
39 | 56 | next_seat = "#" |
40 | 57 | else: |
41 | 58 | pass |
42 | | - #print("Empty seat will stay empty:", row_num, col_num, num_neighbors) |
43 | 59 |
|
44 | | - if seat == "#" and num_neighbors >= 4: |
| 60 | + if seat == "#" and num_neighbors >= lim: |
45 | 61 | next_seat = "L" |
46 | 62 | if next_seat != seat: |
47 | 63 | changes.append(True) |
48 | | - #print("Next: ", row_num, col_num, next_seat, num_neighbors) |
49 | | - next_map[(row_num, col_num)] = next_seat |
| 64 | + next_seatmap[(row_num, col_num)] = next_seat |
50 | 65 |
|
51 | | - return (any(changes), next_map) |
| 66 | + return (any(changes), next_seatmap) |
| 67 | + |
| 68 | +def calculate_rounds(seatmap, lim: int = 4, direct = False) -> int: |
| 69 | + round = 0 |
| 70 | + run = True |
| 71 | + while run: |
| 72 | + round += 1 |
| 73 | + change, next_seatmap = calculate_step(seatmap, lim, direct) |
| 74 | + if change: |
| 75 | + seatmap = next_seatmap |
| 76 | + else: |
| 77 | + run = False |
| 78 | + return list(seatmap.values()).count("#") |
| 79 | + |
| 80 | +def print_nice_map(seatmap): |
| 81 | + result = "" |
| 82 | + for row in range(0,100): |
| 83 | + for col in range(0,100): |
| 84 | + if (row, col) in seatmap: |
| 85 | + result += str(seatmap[(row, col)]) |
| 86 | + if (row, 0) in seatmap: |
| 87 | + result += "\n" |
| 88 | + return result |
52 | 89 |
|
53 | 90 | if __name__ == "__main__": |
54 | | - map = defaultdict(str) |
| 91 | + seatmap = defaultdict(str) |
55 | 92 | with open(sys.argv[1], "r") as infile: |
56 | | - # add initial 0 as input jolt |
57 | 93 |
|
58 | | - # TODO change all to defaultdict, use (row, col) as key! |
59 | 94 | line = 0 |
60 | 95 | for nextline in infile: |
61 | 96 | for num, char in enumerate(nextline.strip()): |
62 | | - map[(line, num)] = char |
| 97 | + seatmap[(line, num)] = char |
63 | 98 | line += 1 |
64 | 99 |
|
65 | | - # print(map) |
66 | 100 |
|
67 | | - round = 0 |
68 | | - run = True |
69 | | - while run: |
70 | | - #print(check_neighbors(map, 9, 10, "#")) |
71 | | - #print("Round: ", round, "\n Map: ", map) |
72 | | - round += 1 |
73 | | - change, next_map = calculate_step(map) |
74 | | - if change: |
75 | | - map = next_map |
76 | | - else: |
77 | | - run = False |
78 | | - #if round > 10: |
79 | | - # run = False |
80 | | - print("Part1: ", list(map.values()).count("#")) |
81 | | - #print("Part2: ", get_variants(jolts)) |
| 101 | + part1 = calculate_rounds(seatmap.copy()) |
| 102 | + print("Part1: ", part1) |
| 103 | + part2 = calculate_rounds(seatmap.copy(), 5, True) |
| 104 | + print("Part2: ", part2) |
0 commit comments