Day 20 part 2 (WIP)
This commit is contained in:
parent
b1108e98a4
commit
961247863c
128
2019/day20.py
128
2019/day20.py
|
@ -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()
|
||||||
|
|
Loading…
Reference in a new issue