Skip to content

Commit 2ebcb1d

Browse files
committed
Added days 2019-14 and 2019-15
1 parent 7901443 commit 2ebcb1d

File tree

2 files changed

+251
-0
lines changed

2 files changed

+251
-0
lines changed

2019/14-Space Stoichiometry.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# -------------------------------- Input data ---------------------------------------- #
2+
import os, pathfinding, re
3+
4+
from complex_utils import *
5+
from math import ceil
6+
7+
test_data = {}
8+
9+
test = 1
10+
test_data[test] = {
11+
"input": """10 ORE => 10 A
12+
1 ORE => 1 B
13+
7 A, 1 B => 1 C
14+
7 A, 1 C => 1 D
15+
7 A, 1 D => 1 E
16+
7 A, 1 E => 1 FUEL
17+
6 HTRFP, 1 FVXV, 4 JKLNF, 1 TXFCS, 2 PXBP => 4 JRBFT""",
18+
"expected": ["31", "Unknown"],
19+
}
20+
21+
test += 1
22+
test_data[test] = {
23+
"input": """157 ORE => 5 NZVS
24+
165 ORE => 6 DCFZ
25+
44 XJWVT, 5 KHKGT, 1 QDVJ, 29 NZVS, 9 GPVTF, 48 HKGWZ => 1 FUEL
26+
12 HKGWZ, 1 GPVTF, 8 PSHF => 9 QDVJ
27+
179 ORE => 7 PSHF
28+
177 ORE => 5 HKGWZ
29+
7 DCFZ, 7 PSHF => 2 XJWVT
30+
165 ORE => 2 GPVTF
31+
3 DCFZ, 7 NZVS, 5 HKGWZ, 10 PSHF => 8 KHKGT""",
32+
"expected": ["13312", "82892753"],
33+
}
34+
35+
test = "real"
36+
input_file = os.path.join(
37+
os.path.dirname(__file__),
38+
"Inputs",
39+
os.path.basename(__file__).replace(".py", ".txt"),
40+
)
41+
test_data[test] = {
42+
"input": open(input_file, "r+").read().strip(),
43+
"expected": ["1037742", "1572358"],
44+
}
45+
46+
# -------------------------------- Control program execution ------------------------- #
47+
48+
case_to_test = "real"
49+
part_to_test = 2
50+
51+
# -------------------------------- Initialize some variables ------------------------- #
52+
53+
puzzle_input = test_data[case_to_test]["input"]
54+
puzzle_expected_result = test_data[case_to_test]["expected"][part_to_test - 1]
55+
puzzle_actual_result = "Unknown"
56+
57+
58+
# -------------------------------- Actual code execution ----------------------------- #
59+
60+
61+
def execute_reaction(stock, reaction, required):
62+
global ore_required
63+
target = reaction[1]
64+
nb_reactions = ceil((required[target] - stock.get(target, 0)) / reaction[0])
65+
66+
# Impact on target material
67+
stock[target] = stock.get(target, 0) + nb_reactions * reaction[0] - required[target]
68+
del required[target]
69+
70+
# Impact on other materials
71+
for i in range(len(reaction[2]) // 2):
72+
nb_required, mat = reaction[2][i * 2 : i * 2 + 2]
73+
nb_required = int(nb_required) * nb_reactions
74+
if mat == "ORE" and part_to_test == 1:
75+
ore_required += nb_required
76+
elif stock.get(mat, 0) >= nb_required:
77+
stock[mat] -= nb_required
78+
else:
79+
missing = nb_required - stock.get(mat, 0)
80+
stock[mat] = 0
81+
required[mat] = required.get(mat, 0) + missing
82+
83+
84+
reactions = {}
85+
for string in puzzle_input.split("\n"):
86+
if string == "":
87+
continue
88+
89+
source, target = string.split(" => ")
90+
nb, target = target.split(" ")
91+
nb = int(nb)
92+
93+
sources = source.replace(",", "").split(" ")
94+
95+
reactions[target] = (nb, target, sources)
96+
97+
98+
if part_to_test == 1:
99+
required = {"FUEL": 1}
100+
ore_required = 0
101+
stock = {}
102+
while len(required) > 0:
103+
material = list(required.keys())[0]
104+
execute_reaction(stock, reactions[material], required)
105+
106+
puzzle_actual_result = ore_required
107+
108+
109+
else:
110+
below, above = 1000000000000 // 1037742, 1000000000000
111+
112+
while below != above - 1:
113+
required = {"FUEL": (below + above) // 2}
114+
stock = {"ORE": 1000000000000}
115+
while len(required) > 0 and "ORE" not in required:
116+
material = list(required.keys())[0]
117+
execute_reaction(stock, reactions[material], required)
118+
119+
if stock["ORE"] == 0 or "ORE" in required:
120+
above = (below + above) // 2
121+
else:
122+
below = (below + above) // 2
123+
124+
puzzle_actual_result = below
125+
126+
127+
# -------------------------------- Outputs / results --------------------------------- #
128+
129+
print("Expected result : " + str(puzzle_expected_result))
130+
print("Actual result : " + str(puzzle_actual_result))

2019/15-Oxygen System.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# -------------------------------- Input data ---------------------------------------- #
2+
import os, pathfinding, IntCode, copy
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": ["366", "384"],
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+
def breadth_first_search(self, start, end=None):
39+
current_distance = 0
40+
frontier = [(start, 0)]
41+
self.distance_from_start = {start[0]: 0}
42+
self.came_from = {start[0]: None}
43+
44+
while frontier:
45+
vertex, current_distance = frontier.pop(0)
46+
current_distance += 1
47+
48+
try:
49+
neighbors = self.neighbors(vertex)
50+
except pathfinding.TargetFound as e:
51+
raise pathfinding.TargetFound(current_distance, e.args[0])
52+
53+
if not neighbors:
54+
continue
55+
56+
for neighbor in neighbors:
57+
if neighbor[0] in self.distance_from_start:
58+
continue
59+
# Adding for future examination
60+
frontier.append((neighbor, current_distance))
61+
62+
# Adding for final search
63+
self.distance_from_start[neighbor[0]] = current_distance
64+
self.came_from[neighbor[0]] = vertex[0]
65+
66+
67+
def neighbors(self, vertex):
68+
position, program = vertex
69+
possible = []
70+
neighbors = []
71+
for dir in directions_straight:
72+
if position + dir not in self.vertices:
73+
possible.append(dir)
74+
new_program = copy.deepcopy(program)
75+
new_program.add_input(movements[dir])
76+
new_program.restart()
77+
new_program.run()
78+
result = new_program.outputs.pop()
79+
if result == 2:
80+
self.vertices[position + dir] = "O"
81+
if not start_from_oxygen:
82+
raise pathfinding.TargetFound(new_program)
83+
elif result == 1:
84+
self.vertices[position + dir] = "."
85+
neighbors.append([position + dir, new_program])
86+
else:
87+
self.vertices[position + dir] = "#"
88+
return neighbors
89+
90+
91+
pathfinding.Graph.breadth_first_search = breadth_first_search
92+
pathfinding.Graph.neighbors = neighbors
93+
94+
95+
movements = {north: 1, south: 2, west: 3, east: 4}
96+
position = 0
97+
droid = IntCode.IntCode(puzzle_input)
98+
start_from_oxygen = False
99+
100+
grid = pathfinding.Graph()
101+
grid.vertices = {}
102+
103+
status = 0
104+
try:
105+
grid.breadth_first_search((0, droid))
106+
except pathfinding.TargetFound as e:
107+
if part_to_test == 1:
108+
puzzle_actual_result = e.args[0]
109+
else:
110+
start_from_oxygen = True
111+
oxygen_program = e.args[1]
112+
grid.reset_search()
113+
grid.vertices = {}
114+
grid.breadth_first_search((0, oxygen_program))
115+
puzzle_actual_result = max(grid.distance_from_start.values())
116+
117+
118+
# -------------------------------- Outputs / results --------------------------------- #
119+
120+
print("Expected result : " + str(puzzle_expected_result))
121+
print("Actual result : " + str(puzzle_actual_result))

0 commit comments

Comments
 (0)