Skip to content

Commit a64ad6e

Browse files
committed
Added day 2021-20
1 parent 619c852 commit a64ad6e

File tree

1 file changed

+176
-0
lines changed

1 file changed

+176
-0
lines changed

2021/20-Trench Map.py

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# -------------------------------- Input data ---------------------------------------- #
2+
import os, grid, graph, dot, assembly, re, itertools, copy, functools
3+
from collections import Counter, deque, defaultdict
4+
5+
from compass import *
6+
7+
8+
# This functions come from https://github.com/mcpower/adventofcode - Thanks!
9+
def lmap(func, *iterables):
10+
return list(map(func, *iterables))
11+
12+
13+
def ints(s: str):
14+
return lmap(int, re.findall(r"-?\d+", s)) # thanks mserrano!
15+
16+
17+
def positive_ints(s: str):
18+
return lmap(int, re.findall(r"\d+", s)) # thanks mserrano!
19+
20+
21+
def floats(s: str):
22+
return lmap(float, re.findall(r"-?\d+(?:\.\d+)?", s))
23+
24+
25+
def positive_floats(s: str):
26+
return lmap(float, re.findall(r"\d+(?:\.\d+)?", s))
27+
28+
29+
def words(s: str):
30+
return re.findall(r"[a-zA-Z]+", s)
31+
32+
33+
test_data = {}
34+
35+
test = 1
36+
test_data[test] = {
37+
"input": """..#.#..#####.#.#.#.###.##.....###.##.#..###.####..#####..#....#..#..##..###..######.###...####..#..#####..##..#.#####...##.#.#..#.##..#.#......#.###.######.###.####...#.##.##..#..#..#####.....#.#....###..#.##......#.....#..#..#..##..#...##.######.####.####.#.#...#.......#..#.#.#...####.##.#......#..#...##.#.##..#...##.#.##..###.#......#.#.......#.#.#.####.###.##...#.....####.#..#..#.##.#....##..#.####....##...##..#...#......#.#.......#.......##..####..#...#.#.#...##..#.#..###..#####........#..####......#..#
38+
39+
#..#.
40+
#....
41+
##..#
42+
..#..
43+
..###""",
44+
"expected": ["35", "Unknown"],
45+
}
46+
47+
test = "real"
48+
input_file = os.path.join(
49+
os.path.dirname(__file__),
50+
"Inputs",
51+
os.path.basename(__file__).replace(".py", ".txt"),
52+
)
53+
test_data[test] = {
54+
"input": open(input_file, "r+").read(),
55+
"expected": ["5044", "18074"],
56+
}
57+
58+
59+
# -------------------------------- Control program execution ------------------------- #
60+
61+
case_to_test = "real"
62+
part_to_test = 2
63+
64+
# -------------------------------- Initialize some variables ------------------------- #
65+
66+
puzzle_input = test_data[case_to_test]["input"]
67+
puzzle_expected_result = test_data[case_to_test]["expected"][part_to_test - 1]
68+
puzzle_actual_result = "Unknown"
69+
70+
71+
# -------------------------------- Actual code execution ----------------------------- #
72+
73+
dot.Dot.all_directions = directions_diagonals
74+
all_directions = directions_diagonals
75+
dot.Dot.allowed_direction_map = {
76+
".": {dir: all_directions for dir in all_directions},
77+
"#": {dir: all_directions for dir in all_directions},
78+
}
79+
dot.Dot.terrain_map = {
80+
".": [True, False],
81+
"#": [True, False],
82+
"X": [True, False],
83+
}
84+
85+
86+
def get_neighbors(self):
87+
if self.neighbors_obsolete:
88+
self.neighbors = {}
89+
for direction in self.allowed_directions:
90+
if (self + direction) and (self + direction).is_walkable:
91+
self.neighbors[self + direction] = 1
92+
else:
93+
new_dot = self.__class__(self.grid, self.position + direction, ".")
94+
self.grid.dots[self.position + direction] = new_dot
95+
self.neighbors[self + direction] = 1
96+
97+
self.neighbors_obsolete = False
98+
return self.neighbors
99+
100+
101+
dot.Dot.get_neighbors = get_neighbors
102+
103+
grid.Grid.all_directions = directions_diagonals
104+
105+
dot.Dot.sort_value = dot.Dot.sorting_map["reading"]
106+
107+
if part_to_test == 1:
108+
generations = 2
109+
else:
110+
generations = 50
111+
112+
113+
algorithm = puzzle_input.split("\n")[0]
114+
115+
image = grid.Grid()
116+
image.all_directions = directions_diagonals
117+
image.text_to_dots("\n".join(puzzle_input.split("\n")[2:]))
118+
119+
# print (image.dots_to_text())
120+
121+
for i in range(generations + 5):
122+
dots = image.dots.copy()
123+
[image.dots[x].get_neighbors() for x in dots]
124+
125+
126+
for i in range(generations):
127+
# print ('Generation', i)
128+
new_image = grid.Grid()
129+
new_image.dots = {
130+
x: dot.Dot(new_image, image.dots[x].position, image.dots[x].terrain)
131+
for x in image.dots
132+
}
133+
new_image.all_directions = directions_diagonals
134+
135+
for x in image.dots.copy():
136+
neighbors = [neighbor for neighbor in image.dots[x].get_neighbors()] + [
137+
image.dots[x]
138+
]
139+
text = "".join([neighbor.terrain for neighbor in sorted(neighbors)])
140+
binary = int(text.replace(".", "0").replace("#", "1"), 2)
141+
new_image.dots[x].set_terrain(algorithm[binary])
142+
# print (new_image.dots_to_text())
143+
144+
# Empty borders so they're not counted later
145+
# They use surrounding data (out of image) that default to . and this messes up the rest
146+
# This is done only for odd generations because that's enough (all non-borders get blanked out due to the "." at the end of the algorithm)
147+
if i % 2 == 1:
148+
borders, _ = new_image.get_borders()
149+
borders = functools.reduce(lambda a, b: a + b, borders)
150+
[dot.set_terrain(".") for dot in borders]
151+
152+
image.dots = {
153+
x: dot.Dot(image, new_image.dots[x].position, new_image.dots[x].terrain)
154+
for x in new_image.dots
155+
}
156+
157+
# print ('Lit dots', sum([1 for dot in image.dots if image.dots[dot].terrain == '#']))
158+
159+
# Remove the borders that were added (they shouldn't count because they take into account elements outside the image)
160+
borders, _ = image.get_borders()
161+
borders = functools.reduce(lambda a, b: a + b, borders)
162+
image.dots = {
163+
dot: image.dots[dot] for dot in image.dots if image.dots[dot] not in borders
164+
}
165+
166+
puzzle_actual_result = sum([1 for dot in image.dots if image.dots[dot].terrain == "#"])
167+
168+
169+
# -------------------------------- Outputs / results --------------------------------- #
170+
171+
print("Case :", case_to_test, "- Part", part_to_test)
172+
print("Expected result : " + str(puzzle_expected_result))
173+
print("Actual result : " + str(puzzle_actual_result))
174+
# Date created: 2021-12-20 08:30:35.363096
175+
# Part 1: 2021-12-20 10:19:36
176+
# Part 2: 2021-12-20 10:35:25

0 commit comments

Comments
 (0)