Skip to content

Commit 3a6c96c

Browse files
committed
Added days 2019-19 and 2019-20
1 parent bb79269 commit 3a6c96c

File tree

2 files changed

+393
-0
lines changed

2 files changed

+393
-0
lines changed

2019/19-Tractor Beam.py

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# -------------------------------- Input data ---------------------------------------- #
2+
import os, pathfinding, IntCode, math
3+
4+
from complex_utils import *
5+
6+
test_data = {}
7+
8+
test = 1
9+
test_data[test] = {
10+
"input": """""",
11+
"expected": ["Unknown", "Unknown"],
12+
}
13+
14+
test = "real"
15+
input_file = os.path.join(
16+
os.path.dirname(__file__),
17+
"Inputs",
18+
os.path.basename(__file__).replace(".py", ".txt"),
19+
)
20+
test_data[test] = {
21+
"input": open(input_file, "r+").read().strip(),
22+
"expected": ["169", "7001134"],
23+
}
24+
25+
# -------------------------------- Control program execution ------------------------- #
26+
27+
case_to_test = "real"
28+
part_to_test = 2
29+
30+
# -------------------------------- Initialize some variables ------------------------- #
31+
32+
puzzle_input = test_data[case_to_test]["input"]
33+
puzzle_expected_result = test_data[case_to_test]["expected"][part_to_test - 1]
34+
puzzle_actual_result = "Unknown"
35+
36+
37+
# -------------------------------- Actual code execution ----------------------------- #
38+
39+
if part_to_test == 1:
40+
beam = IntCode.IntCode(puzzle_input)
41+
42+
affected = 0
43+
for x in range(50):
44+
for y in range(50):
45+
beam.reset(puzzle_input)
46+
beam.add_input(x)
47+
beam.add_input(y)
48+
beam.run()
49+
affected += beam.outputs.pop()
50+
51+
puzzle_actual_result = affected
52+
53+
54+
else:
55+
beam = IntCode.IntCode(puzzle_input)
56+
known_points = {}
57+
58+
def check_tractor(position):
59+
if position not in known_points:
60+
beam.reset(puzzle_input)
61+
beam.add_input(position.real)
62+
beam.add_input(-position.imag)
63+
beam.run()
64+
known_points[position] = beam.outputs.pop()
65+
return known_points[position] == 1
66+
67+
# If we call alpha the angle from vertical to the lowest part of the beam
68+
# And beta the angle from vertical to the highest part of the beam
69+
# And x, y the target position
70+
# Then we have:
71+
# x + 100 = y*tan(beta)
72+
# x = (y+100)*tan(alpha)
73+
# Therefore:
74+
# y = 100*(tan (alpha) - 1) / (tan(beta) - tan(alpha))
75+
# x = y * tan(beta) - 100
76+
77+
# First, get an approximation of alpha and beta
78+
def search_x(direction):
79+
y = 1000
80+
x = 0 if direction == 1 else 10 ** 4
81+
resolution = 100
82+
while True:
83+
if check_tractor(x + resolution - j * y) == 1:
84+
if resolution == 1:
85+
break
86+
resolution //= 2
87+
else:
88+
x += resolution * direction
89+
return x
90+
91+
alpha = math.atan(search_x(1) / 1000)
92+
beta = math.atan(search_x(-1) / 1000)
93+
94+
# Then, math!
95+
# Note: We look for size 150 as a safety
96+
y = 150 * (math.tan(alpha) + 1) / (math.tan(beta) - math.tan(alpha))
97+
x = y * math.tan(beta) - 150
98+
position = int(x) - int(y) * j
99+
100+
def corners(position):
101+
# We need to check only those 2 positions
102+
return [position + 99, position - 99 * j]
103+
104+
valid_position = 0
105+
checked_positions = []
106+
best_position = position
107+
resolution = 100
108+
109+
while True:
110+
box = corners(position)
111+
checked_positions.append(position)
112+
113+
new_position = position
114+
if check_tractor(box[0]) and check_tractor(box[1]):
115+
if manhattan_distance(0, best_position) > manhattan_distance(0, position):
116+
best_position = position
117+
# If I move the box just by 1, it fails
118+
if (
119+
not check_tractor(box[0] + 1)
120+
and not check_tractor(box[0] + 1 * j)
121+
and not check_tractor(box[1] + 1 * j)
122+
and not check_tractor(box[1] + 1 * j)
123+
):
124+
break
125+
new_position += resolution * j
126+
elif check_tractor(box[0]):
127+
new_position += resolution
128+
elif check_tractor(box[1]):
129+
new_position -= resolution
130+
else:
131+
new_position -= resolution * j
132+
133+
# This means we have already checked the new position
134+
# So, either we reduce the resolution, or we check closer
135+
if new_position in checked_positions:
136+
if resolution != 1:
137+
resolution //= 2
138+
else:
139+
# This means we are close
140+
# So now, check the 10*10 grid closer to the emitter
141+
found = False
142+
for dx in range(10, 0, -1):
143+
for dy in range(10, 0, -1):
144+
test = best_position - dx + dy * j
145+
box = corners(test)
146+
if check_tractor(box[0]) and check_tractor(box[1]):
147+
new_position = test
148+
found = True
149+
break
150+
151+
if not found:
152+
break
153+
position = new_position
154+
puzzle_actual_result = int(best_position.real * 10 ** 4 - best_position.imag)
155+
156+
157+
# -------------------------------- Outputs / results --------------------------------- #
158+
159+
print("Expected result : " + str(puzzle_expected_result))
160+
print("Actual result : " + str(puzzle_actual_result))

2019/20-Donut Maze.py

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
# -------------------------------- Input data ---------------------------------------- #
2+
import os, pathfinding
3+
4+
from complex_utils import *
5+
6+
test_data = {}
7+
8+
test = 1
9+
test_data[test] = {
10+
"input": """ A
11+
A
12+
#################.#############
13+
#.#...#...................#.#.#
14+
#.#.#.###.###.###.#########.#.#
15+
#.#.#.......#...#.....#.#.#...#
16+
#.#########.###.#####.#.#.###.#
17+
#.............#.#.....#.......#
18+
###.###########.###.#####.#.#.#
19+
#.....# A C #.#.#.#
20+
####### S P #####.#
21+
#.#...# #......VT
22+
#.#.#.# #.#####
23+
#...#.# YN....#.#
24+
#.###.# #####.#
25+
DI....#.# #.....#
26+
#####.# #.###.#
27+
ZZ......# QG....#..AS
28+
###.### #######
29+
JO..#.#.# #.....#
30+
#.#.#.# ###.#.#
31+
#...#..DI BU....#..LF
32+
#####.# #.#####
33+
YN......# VT..#....QG
34+
#.###.# #.###.#
35+
#.#...# #.....#
36+
###.### J L J #.#.###
37+
#.....# O F P #.#...#
38+
#.###.#####.#.#####.#####.###.#
39+
#...#.#.#...#.....#.....#.#...#
40+
#.#####.###.###.#.#.#########.#
41+
#...#.#.....#...#.#.#.#.....#.#
42+
#.###.#####.###.###.#.#.#######
43+
#.#.........#...#.............#
44+
#########.###.###.#############
45+
B J C
46+
U P P """,
47+
"expected": ["58", "Unknown"],
48+
}
49+
50+
test += 1
51+
test_data[test] = {
52+
"input": """ Z L X W C
53+
Z P Q B K
54+
###########.#.#.#.#######.###############
55+
#...#.......#.#.......#.#.......#.#.#...#
56+
###.#.#.#.#.#.#.#.###.#.#.#######.#.#.###
57+
#.#...#.#.#...#.#.#...#...#...#.#.......#
58+
#.###.#######.###.###.#.###.###.#.#######
59+
#...#.......#.#...#...#.............#...#
60+
#.#########.#######.#.#######.#######.###
61+
#...#.# F R I Z #.#.#.#
62+
#.###.# D E C H #.#.#.#
63+
#.#...# #...#.#
64+
#.###.# #.###.#
65+
#.#....OA WB..#.#..ZH
66+
#.###.# #.#.#.#
67+
CJ......# #.....#
68+
####### #######
69+
#.#....CK #......IC
70+
#.###.# #.###.#
71+
#.....# #...#.#
72+
###.### #.#.#.#
73+
XF....#.# RF..#.#.#
74+
#####.# #######
75+
#......CJ NM..#...#
76+
###.#.# #.###.#
77+
RE....#.# #......RF
78+
###.### X X L #.#.#.#
79+
#.....# F Q P #.#.#.#
80+
###.###########.###.#######.#########.###
81+
#.....#...#.....#.......#...#.....#.#...#
82+
#####.#.###.#######.#######.###.###.#.#.#
83+
#.......#.......#.#.#.#.#...#...#...#.#.#
84+
#####.###.#####.#.#.#.#.###.###.#.###.###
85+
#.......#.....#.#...#...............#...#
86+
#############.#.#.###.###################
87+
A O F N
88+
A A D M """,
89+
"expected": ["Unknown", "396"],
90+
}
91+
92+
test = "real"
93+
input_file = os.path.join(
94+
os.path.dirname(__file__),
95+
"Inputs",
96+
os.path.basename(__file__).replace(".py", ".txt"),
97+
)
98+
test_data[test] = {
99+
"input": open(input_file, "r+").read(),
100+
"expected": ["642", "7492"],
101+
}
102+
103+
# -------------------------------- Control program execution ------------------------- #
104+
105+
case_to_test = 2
106+
part_to_test = 2
107+
108+
# -------------------------------- Initialize some variables ------------------------- #
109+
110+
puzzle_input = test_data[case_to_test]["input"]
111+
puzzle_expected_result = test_data[case_to_test]["expected"][part_to_test - 1]
112+
puzzle_actual_result = "Unknown"
113+
114+
115+
# -------------------------------- Actual code execution ----------------------------- #
116+
def grid_to_vertices(self, grid, diagonals_allowed=False, wall="#"):
117+
self.vertices = {}
118+
y = 0
119+
120+
for line in grid.splitlines():
121+
for x in range(len(line)):
122+
if line[x] != wall:
123+
self.vertices[x - y * j] = line[x]
124+
y += 1
125+
126+
for source in self.vertices:
127+
for direction in directions_straight:
128+
target = source + direction
129+
if target in self.vertices:
130+
if source in self.edges:
131+
self.edges[source][target] = 1
132+
else:
133+
self.edges[source] = {target: 1}
134+
135+
return True
136+
137+
138+
pathfinding.WeightedGraph.grid_to_vertices = grid_to_vertices
139+
140+
141+
grid = pathfinding.WeightedGraph()
142+
grid.grid_to_vertices(puzzle_input.replace(" ", "#"))
143+
width, height = max_real(grid.vertices), -min_imag(grid.vertices)
144+
letters = grid.grid_search(puzzle_input, "abcdefghijklmnopqrstuvwxyz".upper())
145+
portals = {}
146+
for letter in letters:
147+
for position in letters[letter]:
148+
# Vertical portal
149+
if (
150+
grid.vertices.get(position + south, "#")
151+
in "abcdefghijklmnopqrstuvwxyz".upper()
152+
):
153+
portal = letter + grid.vertices[position + south]
154+
if grid.vertices.get(position + south * 2, "#") == ".":
155+
portal_position = position + south * 2
156+
else:
157+
portal_position = position - south
158+
159+
# Horizontal portal
160+
elif (
161+
grid.vertices.get(position + east, "#")
162+
in "abcdefghijklmnopqrstuvwxyz".upper()
163+
):
164+
portal = letter + grid.vertices[position + east]
165+
if grid.vertices.get(position + east * 2, "#") == ".":
166+
portal_position = position + east * 2
167+
else:
168+
portal_position = position - east
169+
else:
170+
continue
171+
172+
portal_position = SuperComplex(portal_position)
173+
174+
# Find whether we're at the center or not (I don't care for AA or ZZ)
175+
if portal in ("AA", "ZZ"):
176+
portals[portal] = portal_position
177+
elif portal_position.real == 2 or portal_position.real == width - 2:
178+
portals[(portal, "out")] = portal_position
179+
elif portal_position.imag == -2 or portal_position.imag == -(height - 2):
180+
portals[(portal, "out")] = portal_position
181+
else:
182+
portals[(portal, "in")] = portal_position
183+
184+
185+
if part_to_test == 1:
186+
for portal in portals:
187+
if len(portal) == 2 and portal[1] == "in":
188+
portal_in = portals[portal]
189+
portal_out = portals[(portal[0], "out")]
190+
grid.edges[portal_in][portal_out] = 1
191+
grid.edges[portal_in][portal_out] = 1
192+
193+
grid.dijkstra(portals["AA"], portals["ZZ"])
194+
puzzle_actual_result = grid.distance_from_start[portals["ZZ"]]
195+
196+
197+
else:
198+
edges = {}
199+
for portal in portals:
200+
grid.reset_search()
201+
grid.dijkstra(portals[portal])
202+
for other_portal in portals:
203+
if portal == other_portal:
204+
continue
205+
if not portals[other_portal] in grid.distance_from_start:
206+
continue
207+
distance = grid.distance_from_start[portals[other_portal]]
208+
for level in range(20):
209+
if portal in ("AA", "ZZ") and level != 0:
210+
break
211+
if other_portal in ("AA", "ZZ") and level != 0:
212+
break
213+
if (portal, level) in edges:
214+
edges[(portal, level)].update({(other_portal, level): distance})
215+
else:
216+
edges[(portal, level)] = {(other_portal, level): distance}
217+
218+
if len(portal) == 2 and portal[1] == "in":
219+
portal_out = (portal[0], "out")
220+
edges[(portal, level)].update({(portal_out, level + 1): 1})
221+
elif len(portal) == 2 and portal[1] == "out" and level != 0:
222+
portal_in = (portal[0], "in")
223+
edges[(portal, level)].update({(portal_in, level - 1): 1})
224+
225+
grid = pathfinding.WeightedGraph({}, edges)
226+
227+
grid.dijkstra(("AA", 0), ("ZZ", 0))
228+
puzzle_actual_result = grid.distance_from_start[("ZZ", 0)]
229+
230+
# -------------------------------- Outputs / results --------------------------------- #
231+
232+
print("Expected result : " + str(puzzle_expected_result))
233+
print("Actual result : " + str(puzzle_actual_result))

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy