This commit is contained in:
Dece 2019-12-19 00:21:45 +01:00
parent c1540f2096
commit c40027f12e
2 changed files with 49 additions and 36 deletions

View file

@ -1,4 +1,5 @@
import string import string
import sys
from grid import Grid from grid import Grid
from vector import v2a from vector import v2a
@ -9,9 +10,6 @@ EX1 = [
"#b.A.@.a#", "#b.A.@.a#",
"#########", "#########",
] ]
# reqs:
# a: []
# b: [a]
EX2 = [ EX2 = [
"########################", "########################",
"#f.D.E.e.C.b.A.@.a.B.c.#", "#f.D.E.e.C.b.A.@.a.B.c.#",
@ -19,41 +17,48 @@ EX2 = [
"#d.....................#", "#d.....................#",
"########################", "########################",
] ]
# reqs:
# a: []
# b: [a]
# c: [b]
# d: [b]
# e: [a, c]
# f: [a, c, e, d]
#
# - get a
# - get b
# - get c or d?
# - get c
# - get d or e?
# - d
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()
lab = Lab(lines=EX1) lab = Lab(lines=lines)
lab.dumb_print() lab.dumb_print()
lab.find_positions() lab.find_positions()
pos = lab.start states = [(lab.start, frozenset(), 0)]
missing_keys = list(lab.key_pos.keys()) state_cache = Grid(value_factory=dict)
obtained_keys = set("a") num_keys = len(lab.key_pos)
while missing_keys: min_steps = 2**32
num_processed = 0
while states:
num_processed += 1
pos, obtained, total_steps = states.pop(0)
if len(obtained) == num_keys:
print(f"All keys obtained in {total_steps}.")
min_steps = min(min_steps, total_steps)
continue
for key, bt in lab.get_all_backtracks(pos).items(): for key_name, bt in lab.get_missing_backtracks(pos, keys=obtained).items():
steps, reqs, founds = bt steps, required, found = bt
if len(reqs - obtained_keys) == 0: if len(required - obtained) > 0:
print(key, "is accessible") continue
break
next_pos = lab.key_pos[key_name]
next_obtained = obtained | found | {key_name}
next_steps = total_steps + steps
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:
print("o", end="")
states.append((next_pos, next_obtained, next_steps))
old_bests[next_obtained] = next_steps
print(".", end="")
if num_processed % 100 == 0:
print(f"[{num_processed}/{len(states)}]", end="")
sys.stdout.flush()
print(f"Minimal number of steps to get all keys: {min_steps}.")
class Lab(Grid): class Lab(Grid):
@ -77,13 +82,18 @@ class Lab(Grid):
self.door_pos[v] = (x, y) self.door_pos[v] = (x, y)
self.setv(self.start[0], self.start[1], Lab.TILE_PATH) self.setv(self.start[0], self.start[1], Lab.TILE_PATH)
def get_all_backtracks(self, start): def get_missing_backtracks(self, start, keys=set()):
return { backtracks = {}
kname: self.backtrack(self.path(start, kpos), kpos, start) for kname, kpos in self.key_pos.items():
for kname, kpos in self.key_pos.items() if kname in keys:
} continue
path = self.path(start, kpos, keys=keys)
if path is None:
continue
backtracks[kname] = self.backtrack(path, kpos, start)
return backtracks
def path(self, s, e): def path(self, s, e, keys=set()):
discovered = Grid(value_factory=bool) discovered = Grid(value_factory=bool)
discovered.setv(s[0], s[1], True) discovered.setv(s[0], s[1], True)
parents = {} parents = {}
@ -96,12 +106,14 @@ class Lab(Grid):
for near_pos, near_tile in nears.items(): for near_pos, near_tile in nears.items():
if near_tile == Lab.TILE_WALL: if near_tile == Lab.TILE_WALL:
continue continue
if near_tile in string.ascii_uppercase and near_tile.lower() not in keys:
continue
if discovered.getv(near_pos[0], near_pos[1]): if discovered.getv(near_pos[0], near_pos[1]):
continue continue
discovered.setv(near_pos[0], near_pos[1], True) discovered.setv(near_pos[0], near_pos[1], True)
parents[near_pos] = pos parents[near_pos] = pos
q.append(near_pos) q.append(near_pos)
return parents return None
def backtrack(self, parents, pos, end): def backtrack(self, parents, pos, end):
count = 0 count = 0
@ -115,7 +127,7 @@ class Lab(Grid):
requirements.add(tile.lower()) requirements.add(tile.lower())
elif tile in string.ascii_lowercase: elif tile in string.ascii_lowercase:
keys_found.add(tile) keys_found.add(tile)
return count, requirements, keys_found return count, requirements, frozenset(keys_found)
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -37,6 +37,7 @@ class Grid:
print() print()
def print_near(self, p, view_size=2): def print_near(self, p, view_size=2):
"""Print near values in an area of view_size. Works iff keys support addition."""
for dy in range(-view_size, view_size + 1): for dy in range(-view_size, view_size + 1):
print("".join([ print("".join([
self.g[p[1] + dy][p[0] + dx] self.g[p[1] + dy][p[0] + dx]