From c40027f12ed3e8a817688ebfbdb83e4f53ac6b60 Mon Sep 17 00:00:00 2001 From: Dece Date: Thu, 19 Dec 2019 00:21:45 +0100 Subject: [PATCH] Day 18 --- 2019/day18.py | 84 +++++++++++++++++++++++++++++---------------------- 2019/grid.py | 1 + 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/2019/day18.py b/2019/day18.py index 32a2833..f59572a 100644 --- a/2019/day18.py +++ b/2019/day18.py @@ -1,4 +1,5 @@ import string +import sys from grid import Grid from vector import v2a @@ -9,9 +10,6 @@ EX1 = [ "#b.A.@.a#", "#########", ] -# reqs: -# a: [] -# b: [a] EX2 = [ "########################", "#f.D.E.e.C.b.A.@.a.B.c.#", @@ -19,41 +17,48 @@ EX2 = [ "#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(): with open("day18.txt", "rt") as input_file: lines = input_file.readlines() - lab = Lab(lines=EX1) + lab = Lab(lines=lines) lab.dumb_print() - lab.find_positions() - pos = lab.start - missing_keys = list(lab.key_pos.keys()) - obtained_keys = set("a") - while missing_keys: + states = [(lab.start, frozenset(), 0)] + state_cache = Grid(value_factory=dict) + num_keys = len(lab.key_pos) + 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(): - steps, reqs, founds = bt - if len(reqs - obtained_keys) == 0: - print(key, "is accessible") - break + for key_name, bt in lab.get_missing_backtracks(pos, keys=obtained).items(): + steps, required, found = bt + if len(required - obtained) > 0: + continue + + 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): @@ -77,13 +82,18 @@ class Lab(Grid): self.door_pos[v] = (x, y) self.setv(self.start[0], self.start[1], Lab.TILE_PATH) - def get_all_backtracks(self, start): - return { - kname: self.backtrack(self.path(start, kpos), kpos, start) - for kname, kpos in self.key_pos.items() - } + def get_missing_backtracks(self, start, keys=set()): + backtracks = {} + 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.setv(s[0], s[1], True) parents = {} @@ -96,12 +106,14 @@ class Lab(Grid): for near_pos, near_tile in nears.items(): if near_tile == Lab.TILE_WALL: 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]): continue discovered.setv(near_pos[0], near_pos[1], True) parents[near_pos] = pos q.append(near_pos) - return parents + return None def backtrack(self, parents, pos, end): count = 0 @@ -115,7 +127,7 @@ class Lab(Grid): requirements.add(tile.lower()) elif tile in string.ascii_lowercase: keys_found.add(tile) - return count, requirements, keys_found + return count, requirements, frozenset(keys_found) if __name__ == "__main__": diff --git a/2019/grid.py b/2019/grid.py index f442284..2365fca 100644 --- a/2019/grid.py +++ b/2019/grid.py @@ -37,6 +37,7 @@ class Grid: print() 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): print("".join([ self.g[p[1] + dy][p[0] + dx]