summaryrefslogtreecommitdiffstats
path: root/19/py/d18.py
diff options
context:
space:
mode:
Diffstat (limited to '19/py/d18.py')
-rw-r--r--19/py/d18.py246
1 files changed, 246 insertions, 0 deletions
diff --git a/19/py/d18.py b/19/py/d18.py
new file mode 100644
index 0000000..61a5cb0
--- /dev/null
+++ b/19/py/d18.py
@@ -0,0 +1,246 @@
+import time
+import collections
+import heapq as heap
+import sys
+
+f = open("../input/18", "r").readlines()
+
+def neighbours(p):
+ return [(p[0]+1, p[1]), (p[0]-1, p[1]), \
+ (p[0], p[1]+1), (p[0], p[1]-1)]
+
+def path(start: tuple, end:tuple, board:dict, keys={}, doors={}, draw_search=False):
+ ''' Find the shortest path between two points '''
+ if start == end:
+ return collections.deque()
+ visited = set()
+ h = []
+ heap.heappush(h, (0, start, collections.deque()))
+ while True:
+ if len(h) == 0:
+ print(visited)
+ return
+ cur = heap.heappop(h)
+ for n in neighbours(cur[1]):
+ if n == end:
+ cur[2].append(n)
+ if draw_search:
+ print(draw(board, path=cur[2], visited=visited))
+ return cur[2]
+ if n in visited or n not in board or board[n] == "#":
+ continue
+ if n in keys or n in doors:
+ continue
+ new_path = collections.deque(cur[2])
+ new_path.append(n)
+ visited.add(n)
+ heap.heappush(h, (cur[0] + 1, n, new_path))
+ if draw_search:
+ time.sleep(0.0075)
+ print(draw(board, keys=keys, doors=doors, path=new_path, visited=visited))
+ print(end)
+ print(n)
+
+def avail_keys(map: dict, pos: tuple, keys: dict, visited_keys: set, doors: dict, dead=set(), draw_search=False) -> list:
+ ''' Return a list of tuples consisting of ( dist, pos ) '''
+ visited = set()
+ avail = []
+ q = collections.deque()
+ q.append((0, pos))
+ while len(q) != 0:
+ cur = q.popleft()
+ if cur[1] in keys and keys[cur[1]] not in visited_keys:
+ avail.append(cur)
+ continue
+ for n in neighbours(cur[1]):
+ if n in map and map[n] == "#":
+ continue
+ if n in dead:
+ continue
+ if n in doors and doors[n].lower() not in visited_keys:
+ continue
+ if n in visited:
+ continue
+ visited.add(n)
+ q.append((cur[0]+1, n))
+ if draw_search:
+ print(draw(map, keys=keys, doors=doors, visited=visited, dead=dead))
+ print(visited_keys)
+ time.sleep(0.01)
+ return avail
+
+def avail_keys_doors(map: dict, pos: tuple, keys: dict, doors: dict, dead=set(), draw_search=False) -> list:
+ ''' Return a list of tuples consisting of ( dist, pos ) '''
+ visited = set()
+ avail = []
+ q = collections.deque()
+ q.append((0, pos))
+ while len(q) != 0:
+ cur = q.popleft()
+ if cur[1] in keys and cur[1] != pos:
+ avail.append((cur[0], cur[1], keys[cur[1]]))
+ continue
+ if cur[1] in doors and cur[1] != pos:
+ avail.append((cur[0], cur[1], doors[cur[1]]))
+ continue
+ for n in neighbours(cur[1]):
+ if n in map and map[n] == "#":
+ continue
+ if n in dead:
+ continue
+ if n in visited:
+ continue
+ visited.add(n)
+ q.append((cur[0]+1, n))
+ if draw_search:
+ print(draw(map, keys=keys, doors=doors, visited=visited, dead=dead))
+ print(visited_keys)
+ time.sleep(0.01)
+ return avail
+
+def draw(map, pos=None, keys={}, doors={}, path={}, visited={}, dead=set()) -> str:
+ min_x=max_x=min_y=max_y = 0
+ for p in map:
+ min_x = min(p[0], min_x)
+ max_x = max(p[0], max_x)
+ min_y = min(p[1], min_y)
+ max_y = max(p[1], max_y)
+ s = ""
+ for y in range(min_y-1, max_y+2):
+ s += "\n"
+ for x in range(min_x-1, max_x+2):
+ point = (x, y)
+ if pos is not None and point == pos:
+ s += "@"
+ elif point in keys:
+ s += keys[point]
+ elif point in doors:
+ s += doors[point]
+ elif point in path:
+ s += "\u2591"
+ elif point in visited:
+ s += "."
+ elif point in dead:
+ s += "X"
+ elif point in map:
+ if map[point] == "#":
+ s += "\u2588"
+ else:
+ s += " "
+ else:
+ s += "."
+ return s
+
+def pos(s, keys, doors):
+ for iter in (keys, doors):
+ for k, v in keys.items():
+ if s == v:
+ return k
+
+walls = set()
+walkables = set()
+map = {}
+dead = set() # dead ends, tiles which are effectivly walls
+keys = {}
+doors = {}
+keys_doors = {}
+for y in range(len(f)):
+ for x in range(len(f[y].strip())):
+ if f[y][x] == "@":
+ start = (x,y)
+ map[(x,y)] = "."
+ elif f[y][x].isalpha():
+ if f[y][x].isupper():
+ doors[(x,y)] = f[y][x]
+ keys_doors[(x,y)] = f[y][x]
+ else:
+ keys[(x,y)] = f[y][x]
+ keys_doors[(x,y)] = f[y][x]
+ map[(x,y)] = "?"
+ elif f[y][x] == "#":
+ map[(x,y)] = "#"
+ walls.add((x,y))
+ elif f[y][x] == ".":
+ map[(x,y)] = "."
+ walkables.add((x,y))
+
+for _ in range(10):
+ ends = set()
+ intersections = set()
+ for tile in walkables:
+ num_walls = 0
+ for n in neighbours(tile):
+ if n in walls or n in dead:
+ num_walls += 1
+ if num_walls == 3:
+ ends.add(tile)
+ if num_walls < 2:
+ intersections.add(tile)
+
+ for end in ends:
+ cur = end
+ while cur not in intersections and cur not in keys and cur not in doors:
+ # is normal point
+ dead.add(cur)
+ if cur in walkables:
+ walkables.remove(cur)
+ for n in neighbours(cur):
+ if n not in walls and n not in dead:
+ cur = n
+ break
+
+paths = {}
+start_paths = {}
+
+# GRAPH: {node: [children as tuples with name and distance]}
+graph = {}
+
+for ent, s in keys_doors.items():
+ for avail in avail_keys_doors(map, ent, keys, doors, draw_search=False):
+ paths[(s, avail[2])] = avail[0]
+ graph[s] = graph.get(s, []) + [(avail[2], avail[0])]
+used = set()
+for path in paths:
+ if (path[1], path[0]) in used:
+ continue
+ print(path[0],"--",path[1])
+ used.add((path[0], path[1]))
+for avail in avail_keys(map, start, keys, set(), doors):
+ start_paths[("@", keys[avail[1]])] = avail[0]
+ print("start", "->", keys[avail[1]])
+
+print(graph)
+h = []
+
+for path, dist in start_paths.items():
+ visited = []
+ unlocked = set()
+ visited.append(path[1])
+ heap.heappush(h, (dist, path[1], visited, unlocked))
+
+m = 0
+while True:
+ cur = heap.heappop(h)
+ dist = cur[0]
+ last = cur[1]
+ visited = cur[2]
+ unlocked = cur[3]
+
+ if len(unlocked) > m:
+ m = len(unlocked)
+ print(m, visited)
+ if len(unlocked) == len(keys):
+ break
+
+ neighbours = graph[last]
+ for n in neighbours:
+ if n[0] in visited:
+ continue
+ if n[0].isupper() and n[0] not in unlocked:
+ continue
+ visited = visited.copy()
+ unlocked = unlocked.copy()
+ visited.append(n[0])
+ if n[0].islower():
+ unlocked.add(n[0].upper())
+ heap.heappush(h, (dist+n[1], n[0], visited.copy(), unlocked.copy()))