Day 20 part 2 (WIP)

This commit is contained in:
Adrien Abraham 2019-12-20 15:57:20 +01:00
parent b1108e98a4
commit 961247863c

View file

@ -4,11 +4,82 @@ from string import ascii_uppercase
from grid import Grid from grid import Grid
EX1 = [
" A ",
" A ",
" #######.######### ",
" #######.........# ",
" #######.#######.# ",
" #######.#######.# ",
" #######.#######.# ",
" ##### B ###.# ",
"BC...## C ###.# ",
" ##.## ###.# ",
" ##...DE F ###.# ",
" ##### G ###.# ",
" #########.#####.# ",
"DE..#######...###.# ",
" #.#########.###.# ",
"FG..#########.....# ",
" ###########.##### ",
" Z ",
" Z ",
]
EX2 = [
" Z L X W C ",
" Z P Q B K ",
" ###########.#.#.#.#######.############### ",
" #...#.......#.#.......#.#.......#.#.#...# ",
" ###.#.#.#.#.#.#.#.###.#.#.#######.#.#.### ",
" #.#...#.#.#...#.#.#...#...#...#.#.......# ",
" #.###.#######.###.###.#.###.###.#.####### ",
" #...#.......#.#...#...#.............#...# ",
" #.#########.#######.#.#######.#######.### ",
" #...#.# F R I Z #.#.#.# ",
" #.###.# D E C H #.#.#.# ",
" #.#...# #...#.# ",
" #.###.# #.###.# ",
" #.#....OA WB..#.#..ZH",
" #.###.# #.#.#.# ",
"CJ......# #.....# ",
" ####### ####### ",
" #.#....CK #......IC",
" #.###.# #.###.# ",
" #.....# #...#.# ",
" ###.### #.#.#.# ",
"XF....#.# RF..#.#.# ",
" #####.# ####### ",
" #......CJ NM..#...# ",
" ###.#.# #.###.# ",
"RE....#.# #......RF",
" ###.### X X L #.#.#.# ",
" #.....# F Q P #.#.#.# ",
" ###.###########.###.#######.#########.### ",
" #.....#...#.....#.......#...#.....#.#...# ",
" #####.#.###.#######.#######.###.###.#.#.# ",
" #.......#.......#.#.#.#.#...#...#...#.#.# ",
" #####.###.#####.#.#.#.#.###.###.#.###.### ",
" #.......#.....#.#...#...............#...# ",
" #############.#.#.###.################### ",
" A O F N ",
" A A D M " ,
]
def main(): def main():
with open("day20.txt") as input_file: with open("day20.txt") as input_file:
lines = [l.rstrip() for l in input_file.readlines()] lines = [l.rstrip() for l in input_file.readlines()]
maze = Maze(lines, value_factory=str) lines = EX2
# Part 1
maze = Maze(lines)
maze.find_portals()
maze.dumb_print()
min_steps = maze.count_min_steps(maze.entry, maze.exit)
print("Min steps:", min_steps)
# Part 2
maze = Maze(lines, recurse=True)
maze.find_portals() maze.find_portals()
maze.dumb_print() maze.dumb_print()
min_steps = maze.count_min_steps(maze.entry, maze.exit) min_steps = maze.count_min_steps(maze.entry, maze.exit)
@ -23,14 +94,15 @@ class Maze(Grid):
ENTRY_DG = "AA" ENTRY_DG = "AA"
EXIT_DG = "ZZ" EXIT_DG = "ZZ"
def __init__(self, lines, *args, **kwargs): def __init__(self, lines, recurse=False):
super().__init__(*args, **kwargs) super().__init__(value_factory=str, lines=lines)
self.entry = None self.entry = None
self.exit = None self.exit = None
self.portals = {} self.portals = {}
for y, line in enumerate(lines): self.recurse = recurse
for x, value in enumerate(line): self.portal_is_outer = {}
self.setv(x, y, value) self.height = len(lines)
self.width = max(len(line) for line in lines)
def find_portals(self): def find_portals(self):
portals = defaultdict(list) portals = defaultdict(list)
@ -41,12 +113,14 @@ class Maze(Grid):
rrv = self.getv(x + 2, y) rrv = self.getv(x + 2, y)
portal_pos = (x + 2, y) if rrv == Maze.TILE_PATH else (x - 1, y) portal_pos = (x + 2, y) if rrv == Maze.TILE_PATH else (x - 1, y)
portals[v + rv].append(portal_pos) portals[v + rv].append(portal_pos)
self.portal_is_outer[portal_pos] = x == 0 or x == self.width - 2
continue continue
dv = self.getv(x, y + 1) dv = self.getv(x, y + 1)
if dv and dv in ascii_uppercase: if dv and dv in ascii_uppercase:
ddv = self.getv(x, y + 2) ddv = self.getv(x, y + 2)
portal_pos = (x, y + 2) if ddv == Maze.TILE_PATH else (x, y - 1) portal_pos = (x, y + 2) if ddv == Maze.TILE_PATH else (x, y - 1)
portals[v + dv].append(portal_pos) portals[v + dv].append(portal_pos)
self.portal_is_outer[portal_pos] = y == 0 or y == self.height - 2
for n, p in portals.items(): for n, p in portals.items():
if n == "AA": if n == "AA":
self.entry = p[0] self.entry = p[0]
@ -60,50 +134,50 @@ class Maze(Grid):
for y, row in self.g.items(): for y, row in self.g.items():
for x, v in row.items(): for x, v in row.items():
if (x, y) in self.portals: if (x, y) in self.portals:
v = "" v = "o" if self.portal_is_outer[(x, y)] else "i"
if (x, y) in (self.entry, self.exit): if (x, y) in (self.entry, self.exit):
v = "" v = ""
print(v, end="") print(v, end="")
print() print()
def bfs(self, start, end): def bfs(self, start, end):
discovered = Grid(value_factory=bool) discovered = defaultdict(lambda: Grid(value_factory=bool))
discovered.setv(start[0], end[1], True) discovered[0].setv(start[0], end[1], True)
parents = {} parents = {}
q = [start] q = [(start, 0)]
while q: while q:
pos = q.pop(0) pos, level = q.pop(0)
if pos == end: if pos == end and (not self.recurse or level == 0):
return parents return parents
nears = self.near_objects(pos) nears = self.near_objects(pos)
is_portal = pos in self.portals
if is_portal:
nears[self.portals[pos]] = Maze.TILE_PATH
for near_pos, near_tile in nears.items(): for near_pos, near_tile in nears.items():
if near_tile != Maze.TILE_PATH: if near_tile != Maze.TILE_PATH:
continue continue
if discovered.getv(near_pos[0], near_pos[1]): near_level = level
if self.recurse and is_portal and near_pos == self.portals[pos]:
near_level += -1 if self.portal_is_outer[pos] else 1
if discovered[near_level].getv(near_pos[0], near_pos[1]):
continue continue
discovered.setv(near_pos[0], near_pos[1], True) discovered[near_level].setv(near_pos[0], near_pos[1], True)
parents[near_pos] = pos parents[(near_pos, near_level)] = (pos, level)
q.append(near_pos) q.append((near_pos, near_level))
return None return None
def count_min_steps(self, start, end): def count_min_steps(self, start, end):
parents = self.bfs(start, end) parents = self.bfs(start, end)
count = 0 count = 0
pos = end pos = (end, 0)
while pos != start: while pos != (start, 0):
pos = parents[pos]
count += 1 count += 1
if pos in self.portals: # One more step to traverse portal. parent = parents[pos]
if pos[1] != parent[1]:
count += 1 count += 1
pos = parent
return count return count
def near_objects(self, p):
nears = super().near_objects(p)
for near in list(nears):
if near in self.portals:
nears[self.portals[near]] = Maze.TILE_PATH
return nears
if __name__ == "__main__": if __name__ == "__main__":
main() main()