Skip to content

Commit 54cdb62

Browse files
committed
Added days 2016-11, 2016-12 and a pathfinding library
1 parent a1bc715 commit 54cdb62

File tree

3 files changed

+826
-0
lines changed

3 files changed

+826
-0
lines changed
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
# -------------------------------- Input data -------------------------------- #
2+
import os, itertools
3+
import pathfinding
4+
5+
test_data = {}
6+
7+
test = 1
8+
test_data[test] = {"input": """""",
9+
"expected": ['Unknown', 'Unknown'],
10+
}
11+
12+
test += 1
13+
test_data[test] = {"input": """""",
14+
"expected": ['Unknown', 'Unknown'],
15+
}
16+
17+
test = 'real'
18+
test_data[test] = {"input": '11112123333',
19+
"expected": ['31', 'Unknown'],
20+
}
21+
22+
# -------------------------------- Control program execution -------------------------------- #
23+
24+
case_to_test = 'real'
25+
part_to_test = 2
26+
verbose_level = 1
27+
28+
# -------------------------------- Initialize some variables -------------------------------- #
29+
30+
puzzle_input = test_data[case_to_test]['input']
31+
puzzle_expected_result = test_data[case_to_test]['expected'][part_to_test-1]
32+
puzzle_actual_result = 'Unknown'
33+
34+
35+
36+
37+
# -------------------------------- Actual code execution -------------------------------- #
38+
39+
40+
41+
if part_to_test == 1:
42+
# -------------------------------- Graph-related functions -------------------------------- #
43+
# Re-implement the heuristic to match this graph
44+
def heuristic (self, current_node, target_node):
45+
return sum([abs(int(target_node[i]) - int(current_node[i])) for i in range (1, len(current_node))]) // 2
46+
pathfinding.WeightedGraph.heuristic = heuristic
47+
48+
49+
# How to determine neighbors
50+
def neighbors (self, state):
51+
global states
52+
E = int(state[0])
53+
movables = [x for x in range(1, len(state)) if state[x] == state[0]]
54+
55+
# Connecting if we move 1 element only
56+
possible_neighbors = []
57+
for movable in movables:
58+
if E > 1:
59+
neighbor = str(E-1) + state[1:movable] + str(int(state[movable])-1) + state[movable+1:]
60+
possible_neighbors.append(neighbor)
61+
if E < 4:
62+
neighbor = str(E+1) + state[1:movable] + str(int(state[movable])+1) + state[movable+1:]
63+
possible_neighbors.append(neighbor)
64+
65+
if len(movables) >= 2:
66+
for moved_objects in itertools.combinations(movables, 2):
67+
mov1, mov2 = moved_objects
68+
# No use to bring 2 items downstairs
69+
# if E > 1:
70+
# neighbor = str(E-1) + state[1:mov1] + str(int(state[mov1])-1) + state[mov1+1:mov2] + str(int(state[mov2])-1) + state[mov2+1:]
71+
# possible_neighbors.append(neighbor)
72+
if E < 4:
73+
neighbor = str(E+1) + state[1:mov1] + str(int(state[mov1])+1) + state[mov1+1:mov2] + str(int(state[mov2])+1) + state[mov2+1:]
74+
possible_neighbors.append(neighbor)
75+
76+
return [x for x in possible_neighbors if x in states]
77+
78+
pathfinding.WeightedGraph.neighbors = neighbors
79+
80+
def cost(self, current_node, next_node):
81+
return 1
82+
pathfinding.WeightedGraph.cost = cost
83+
84+
85+
# -------------------------------- Graph construction & execution -------------------------------- #
86+
87+
# state = (E, TG, TM, PtG, PtM, SG, SM, PrG, PrM, RG, RM)
88+
# Forbidden states: Any G + M if G for M is absent
89+
# Forbidden transitions: E changes, the rest is identical
90+
91+
states = set([''.join([str(E), str(TG), str(TM), str(PtG), str(PtM), str(SG), str(SM), str(PrG), str(PrM), str(RG), str(RM)])
92+
for E in range(1, 5)
93+
for TG in range(1, 5)
94+
for TM in range(1, 5)
95+
for PtG in range(1, 5)
96+
for PtM in range(1, 5)
97+
for SG in range(1, 5)
98+
for SM in range(1, 5)
99+
for PrG in range(1, 5)
100+
for PrM in range(1, 5)
101+
for RG in range(1, 5)
102+
for RM in range(1, 5)
103+
104+
if (TG == TM or TM not in (TG, PtG, SG, PrG, RG))
105+
and (PtG == PtM or PtM not in (TG, PtG, SG, PrG, RG))
106+
and (SG == SM or SM not in (TG, PtG, SG, PrG, RG))
107+
and (PrG == PrM or PrM not in (TG, PtG, SG, PrG, RG))
108+
and (RG == RM or RM not in (TG, PtG, SG, PrG, RG))
109+
])
110+
111+
end = '4' * 11
112+
113+
print ('number of states', len(states))
114+
115+
graph = pathfinding.WeightedGraph()
116+
came_from, total_cost = graph.a_star_search(puzzle_input, end)
117+
118+
puzzle_actual_result = total_cost[end]
119+
120+
else:
121+
# -------------------------------- Graph-related functions -------------------------------- #
122+
# Part 2 was completely rewritten for performance improvements
123+
124+
def valid_state (state):
125+
pairs = [(state[x], state[x+1]) for x in range (1, len(state), 2)]
126+
generators = state[1::2]
127+
128+
for pair in pairs:
129+
if pair[0] != pair[1]: # Microchip is not with generator
130+
if pair[1] in generators: # Microchip is with a generator
131+
return False
132+
133+
return True
134+
135+
def visited_state(state):
136+
global visited_coded_states
137+
138+
pairs = [(state[x], state[x+1]) for x in range (1, len(state), 2)]
139+
140+
coded_state = [(state[0], pair) for pair in sorted(pairs)]
141+
142+
if coded_state in visited_coded_states:
143+
return True
144+
else:
145+
visited_coded_states.append(coded_state)
146+
return False
147+
148+
149+
# -------------------------------- BFS implementation -------------------------------- #
150+
start = list(map(int, puzzle_input)) + [1] * 4
151+
end = [4] * 15
152+
153+
visited_coded_states = []
154+
frontier = [(start, 0)]
155+
156+
for status in frontier:
157+
state, curr_steps = status
158+
159+
# Determine potential states to go to
160+
elev_position = state[0]
161+
# The +1 ignores the elevator
162+
elements_at_level = [item+1 for item, level in enumerate(state[1:]) if level == elev_position]
163+
164+
movables = list(itertools.combinations(elements_at_level, 2)) + elements_at_level
165+
166+
if elev_position == 1:
167+
directions = [1]
168+
elif elev_position == 4:
169+
directions = [-1]
170+
else:
171+
directions = [1, -1]
172+
173+
for direction in directions:
174+
for movable in movables:
175+
new_state = state.copy()
176+
177+
new_floor = elev_position + direction
178+
new_state[0] = new_floor
179+
if isinstance(movable, tuple):
180+
# No point in moving 2 items downwards
181+
if direction == -1:
182+
continue
183+
new_state[movable[0]] = new_floor
184+
new_state[movable[1]] = new_floor
185+
else:
186+
new_state[movable] = new_floor
187+
188+
if valid_state(new_state):
189+
if visited_state(new_state):
190+
continue
191+
else:
192+
frontier.append((new_state, curr_steps+1))
193+
194+
if new_state == end:
195+
puzzle_actual_result = curr_steps + 1
196+
break
197+
198+
if puzzle_actual_result != 'Unknown':
199+
break
200+
201+
if puzzle_actual_result != 'Unknown':
202+
break
203+
204+
205+
206+
207+
208+
209+
210+
puzzle_actual_result = curr_steps + 1
211+
212+
213+
214+
215+
216+
217+
218+
# -------------------------------- Outputs / results -------------------------------- #
219+
220+
if verbose_level >= 3:
221+
print ('Input : ' + puzzle_input)
222+
print ('Expected result : ' + str(puzzle_expected_result))
223+
print ('Actual result : ' + str(puzzle_actual_result))
224+
225+
226+
227+

2016/12-Leonardo's Monorail.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# -------------------------------- Input data -------------------------------- #
2+
import os
3+
4+
test_data = {}
5+
6+
test = 1
7+
test_data[test] = {"input": """cpy 41 a
8+
inc a
9+
inc a
10+
dec a
11+
jnz a 2
12+
dec a""",
13+
"expected": ['42', 'Unknown'],
14+
}
15+
16+
test += 1
17+
test_data[test] = {"input": """""",
18+
"expected": ['Unknown', 'Unknown'],
19+
}
20+
21+
test = 'real'
22+
input_file = os.path.join(os.path.dirname(__file__), 'Inputs', os.path.basename(__file__).replace('.py', '.txt'))
23+
test_data[test] = {"input": open(input_file, "r+").read().strip(),
24+
"expected": ['318083', '9227737'],
25+
}
26+
27+
# -------------------------------- Control program execution -------------------------------- #
28+
29+
case_to_test = 'real'
30+
part_to_test = 2
31+
verbose_level = 1
32+
33+
# -------------------------------- Initialize some variables -------------------------------- #
34+
35+
puzzle_input = test_data[case_to_test]['input']
36+
puzzle_expected_result = test_data[case_to_test]['expected'][part_to_test-1]
37+
puzzle_actual_result = 'Unknown'
38+
39+
40+
# -------------------------------- Actual code execution -------------------------------- #
41+
registers = {'a':0, 'b':0, 'c':0, 'd':0}
42+
if part_to_test == 2:
43+
registers['c'] = 1
44+
45+
46+
instructions = puzzle_input.split('\n')
47+
i = 0
48+
while True:
49+
instruction = instructions[i]
50+
i += 1
51+
52+
if instruction[0:3] == 'cpy':
53+
_, val, target = instruction.split(' ')
54+
try:
55+
registers[target] = int(val)
56+
except ValueError:
57+
registers[target] = registers[val]
58+
59+
elif instruction[0:3] == 'inc':
60+
_, target = instruction.split(' ')
61+
registers[target] += 1
62+
elif instruction[0:3] == 'dec':
63+
_, target = instruction.split(' ')
64+
registers[target] -= 1
65+
66+
elif instruction[0:3] == 'jnz':
67+
_, target, jump = instruction.split(' ')
68+
if target == '0':
69+
pass
70+
else:
71+
try:
72+
if int(target):
73+
i = i + int(jump) - 1 # -1 to compensate for what we added before
74+
except ValueError:
75+
if registers[target] != 0:
76+
i = i + int(jump) - 1 # -1 to compensate for what we added before
77+
78+
if i >= len(instructions):
79+
break
80+
81+
puzzle_actual_result = registers['a']
82+
83+
84+
85+
86+
87+
# -------------------------------- Outputs / results -------------------------------- #
88+
89+
if verbose_level >= 3:
90+
print ('Input : ' + puzzle_input)
91+
print ('Expected result : ' + str(puzzle_expected_result))
92+
print ('Actual result : ' + str(puzzle_actual_result))
93+
94+
95+
96+

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