Day 18: WIP

This commit is contained in:
Adrien Abraham 2019-12-18 18:01:42 +01:00
parent be6b265446
commit c1540f2096
2 changed files with 123 additions and 26 deletions

View file

@ -1,13 +1,59 @@
import string
from grid import Grid from grid import Grid
from vector import v2a
EX1 = [
"#########",
"#b.A.@.a#",
"#########",
]
# reqs:
# a: []
# b: [a]
EX2 = [
"########################",
"#f.D.E.e.C.b.A.@.a.B.c.#",
"######################.#",
"#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) lab = Lab(lines=EX1)
lab.dumb_print() lab.dumb_print()
lab.find_positions()
pos = lab.start
missing_keys = list(lab.key_pos.keys())
obtained_keys = set("a")
while missing_keys:
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
class Lab(Grid): class Lab(Grid):
@ -15,6 +61,62 @@ class Lab(Grid):
TILE_WALL = "#" TILE_WALL = "#"
TILE_START = "@" TILE_START = "@"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs, value_factory=lambda: " ")
self.start = None
self.key_pos = {}
self.door_pos = {}
def find_positions(self):
for x, y, v in self.values_gen():
if v == Lab.TILE_START:
self.start = (x, y)
if v in string.ascii_lowercase:
self.key_pos[v] = (x, y)
if v in string.ascii_uppercase:
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 path(self, s, e):
discovered = Grid(value_factory=bool)
discovered.setv(s[0], s[1], True)
parents = {}
q = [s]
while q:
pos = q.pop(0)
if pos == e:
return parents
nears = self.near_objects(pos)
for near_pos, near_tile in nears.items():
if near_tile == Lab.TILE_WALL:
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
def backtrack(self, parents, pos, end):
count = 0
requirements = set()
keys_found = set()
while pos != end:
pos = parents[pos]
count += 1
tile = self.getv(pos[0], pos[1])
if tile in string.ascii_uppercase:
requirements.add(tile.lower())
elif tile in string.ascii_lowercase:
keys_found.add(tile)
return count, requirements, keys_found
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View file

@ -5,25 +5,30 @@ from collections import defaultdict
class Grid: class Grid:
def __init__(self, lines=None): def __init__(self, value_factory=int, lines=None):
self.g = defaultdict(lambda: defaultdict(int)) self.g = defaultdict(lambda: defaultdict(value_factory))
if lines: if lines:
self.load(lines) self.load(lines)
def load(self, lines, f=lambda v: v):
for y, line in enumerate(lines):
for x, c in enumerate(line.rstrip()):
self.g[y][x] = f(c)
def getv(self, x, y): def getv(self, x, y):
return self.g[y][x] return self.g[y][x]
def setv(self, x, y, value): def setv(self, x, y, value):
self.g[y][x] = value self.g[y][x] = value
def near_items(self, p): def load(self, lines, f=lambda v: v):
for y, line in enumerate(lines):
for x, c in enumerate(line.rstrip()):
self.g[y][x] = f(c)
def values_gen(self):
for y, row in self.g.items():
for x, v in row.items():
yield (x, y, v)
def near_objects(self, p):
"""Return a dict of neighbor positions to values (U, L, D, R).""" """Return a dict of neighbor positions to values (U, L, D, R)."""
return {pos: self.g[pos[1]][pos[0]] for pos in Grid.near(p)} return {q: self.g[q[1]][q[0]] for q in Grid.near(p)}
def dumb_print(self, f=lambda v: v): def dumb_print(self, f=lambda v: v):
for row in self.g.values(): for row in self.g.values():
@ -31,22 +36,12 @@ class Grid:
print(f(x), end="") print(f(x), end="")
print() print()
def print_near(self, p): def print_near(self, p, view_size=2):
print("".join([ for dy in range(-view_size, view_size + 1):
chr(self.g[p[1] - 1][p[0] - 1]), print("".join([
chr(self.g[p[1] - 1][p[0]]), self.g[p[1] + dy][p[0] + dx]
chr(self.g[p[1] - 1][p[0] + 1]), for dx in range(-view_size, view_size + 1)
])) ]))
print("".join([
chr(self.g[p[1] ][p[0] - 1]),
chr(self.g[p[1] ][p[0]]),
chr(self.g[p[1] ][p[0] + 1]),
]))
print("".join([
chr(self.g[p[1] + 1][p[0] - 1]),
chr(self.g[p[1] + 1][p[0]]),
chr(self.g[p[1] + 1][p[0] + 1]),
]))
@staticmethod @staticmethod
def near(p): def near(p):