Day 18 part 2

This commit is contained in:
Dece 2019-12-19 01:42:50 +01:00
parent c40027f12e
commit 0a694d06f9

View file

@ -2,43 +2,62 @@ import string
import sys import sys
from grid import Grid from grid import Grid
from vector import v2a
EX1 = [ EX3 = [
"#########", "#######",
"#b.A.@.a#", "#a.#Cd#",
"#########", "##@#@##",
"#######",
"##@#@##",
"#cB#Ab#",
"#######",
] ]
EX2 = [ EX4 = [
"########################", "###############",
"#f.D.E.e.C.b.A.@.a.B.c.#", "#d.ABC.#.....a#",
"######################.#", "######@#@######",
"#d.....................#", "###############",
"########################", "######@#@######",
"#b.....#.....c#",
"###############",
] ]
def main(): def main():
with open("day18.txt", "rt") as input_file: with open("day18.txt", "rt") as input_file:
lines = input_file.readlines() lines = input_file.readlines()
print(f"Part 1 answer: {find_all_keys_with_min_steps(lines)} steps.")
with open("day18-p2.txt", "rt") as input_file: # yeah
lines = input_file.readlines()
print(f"Part 2 answer: {find_all_keys_with_min_steps(lines)} steps.")
def find_all_keys_with_min_steps(lines):
"""Find all keys with 1 or 4 starting points."""
lab = Lab(lines=lines) lab = Lab(lines=lines)
lab.dumb_print() lab.dumb_print()
lab.find_positions() lab.find_positions()
states = [(lab.start, frozenset(), 0)] num_robots = len(lab.starts)
states = [(
lab.starts[0] if num_robots == 1 else encode_posis_packed(lab.starts),
frozenset(),
0
)]
state_cache = Grid(value_factory=dict) state_cache = Grid(value_factory=dict)
num_keys = len(lab.key_pos) num_keys = len(lab.key_pos)
min_steps = 2**32 min_steps = 2**32
num_processed = 0 num_processed = 0
while states: while states:
num_processed += 1 num_processed += 1
pos, obtained, total_steps = states.pop(0) one_or_more_pos, obtained, total_steps = states.pop(0)
if len(obtained) == num_keys: if len(obtained) == num_keys:
print(f"All keys obtained in {total_steps}.") print(f"[Found total of {total_steps}]", end="")
min_steps = min(min_steps, total_steps) min_steps = min(min_steps, total_steps)
continue continue
robot_positions = [one_or_more_pos] if num_robots == 1 else decode_posis(one_or_more_pos)
for pos_index, pos in enumerate(robot_positions):
for key_name, bt in lab.get_missing_backtracks(pos, keys=obtained).items(): for key_name, bt in lab.get_missing_backtracks(pos, keys=obtained).items():
steps, required, found = bt steps, required, found = bt
if len(required - obtained) > 0: if len(required - obtained) > 0:
@ -51,6 +70,10 @@ def main():
old_bests = state_cache.getv(next_pos[0], next_pos[1]) old_bests = state_cache.getv(next_pos[0], next_pos[1])
if next_obtained not in old_bests or old_bests[next_obtained] > next_steps: if next_obtained not in old_bests or old_bests[next_obtained] > next_steps:
print("o", end="") print("o", end="")
if num_robots > 1:
mpos = robot_positions.copy()
mpos[pos_index] = next_pos
next_pos = encode_posis_packed(tuple(mpos))
states.append((next_pos, next_obtained, next_steps)) states.append((next_pos, next_obtained, next_steps))
old_bests[next_obtained] = next_steps old_bests[next_obtained] = next_steps
print(".", end="") print(".", end="")
@ -58,7 +81,28 @@ def main():
if num_processed % 100 == 0: if num_processed % 100 == 0:
print(f"[{num_processed}/{len(states)}]", end="") print(f"[{num_processed}/{len(states)}]", end="")
sys.stdout.flush() sys.stdout.flush()
print(f"Minimal number of steps to get all keys: {min_steps}.")
print()
return min_steps
def encode_posis_packed(pt):
p1, p2, p3, p4 = pt
return encode_posis(p1, p2, p3, p4)
def encode_posis(p1, p2, p3, p4):
return (
(p1[0] << 24) + (p2[0] << 16) + (p3[0] << 8) + (p4[0]),
(p1[1] << 24) + (p2[1] << 16) + (p3[1] << 8) + (p4[1]),
)
def decode_posis(p):
return [
((p[0] & 0xFF000000) >> 24, (p[1] & 0xFF000000) >> 24),
((p[0] & 0x00FF0000) >> 16, (p[1] & 0x00FF0000) >> 16),
((p[0] & 0x0000FF00) >> 8, (p[1] & 0x0000FF00) >> 8),
((p[0] & 0x000000FF) , (p[1] & 0x000000FF)),
]
class Lab(Grid): class Lab(Grid):
@ -68,19 +112,20 @@ class Lab(Grid):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs, value_factory=lambda: " ") super().__init__(*args, **kwargs, value_factory=lambda: " ")
self.start = None self.starts = []
self.key_pos = {} self.key_pos = {}
self.door_pos = {} self.door_pos = {}
def find_positions(self): def find_positions(self):
for x, y, v in self.values_gen(): for x, y, v in self.values_gen():
if v == Lab.TILE_START: if v == Lab.TILE_START:
self.start = (x, y) self.starts.append((x, y))
if v in string.ascii_lowercase: if v in string.ascii_lowercase:
self.key_pos[v] = (x, y) self.key_pos[v] = (x, y)
if v in string.ascii_uppercase: if v in string.ascii_uppercase:
self.door_pos[v] = (x, y) self.door_pos[v] = (x, y)
self.setv(self.start[0], self.start[1], Lab.TILE_PATH) for p in self.starts:
self.setv(p[0], p[1], Lab.TILE_PATH)
def get_missing_backtracks(self, start, keys=set()): def get_missing_backtracks(self, start, keys=set()):
backtracks = {} backtracks = {}