Day 18
This commit is contained in:
parent
c1540f2096
commit
c40027f12e
|
@ -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__":
|
||||||
|
|
|
@ -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]
|
||||||
|
|
Loading…
Reference in a new issue