Browse Source

Day 17.

master
Chris Smith 7 years ago
parent
commit
885b5c0311
1 changed files with 62 additions and 0 deletions
  1. 62
    0
      17.py

+ 62
- 0
17.py View File

@@ -0,0 +1,62 @@
1
+#!/usr/bin/python3
2
+
3
+import functools
4
+import hashlib
5
+import heapq
6
+import itertools
7
+
8
+salt = 'njfxhljp'
9
+moves = {'U': (0, -1), 'D': (0, +1), 'L': (-1, 0), 'R': (+1, 0)}
10
+
11
+
12
+def open_doors(path):
13
+    """Gives a list of open doors around the current square, given the path used to reach it.
14
+
15
+    Does not take into account whether doors actually exist or not.
16
+
17
+    :param path: The path taken to reach the square (as a string, one char per move; e.g. 'UDUDRLRLLL')
18
+    :return: Iterator of directions which have open doors
19
+    """
20
+    return itertools.compress('UDLR', [x > 'a' for x in hashlib.md5((salt + path).encode('UTF-8')).hexdigest()[:4]])
21
+
22
+
23
+def next_moves(coords, path):
24
+    """Gets a list of all possible moves from the given position tuple.
25
+
26
+     Takes in to account the maze bounds and door states.
27
+
28
+     A move's "priority" is the length of the path taken so far, plus the Manhattan distance to the end goal.
29
+     This allows us to use a best-first searching algorithm like A* to efficiently find paths to the end.
30
+
31
+    :param coords: The current co-ordinates, as a tuple of (x, y).
32
+    :param path: The path taken to reach the square (as a string, one char per move; e.g. 'UDUDRLRLLL')
33
+    :return: An (unsorted) iterator of new potential positions, as tuples of (priority, co-ordinates, path)
34
+    """
35
+    for direction in open_doors(path):
36
+        new_coords = tuple(map(sum, zip(coords, moves[direction])))
37
+        if 0 <= new_coords[0] <= 3 and 0 <= new_coords[1] <= 3:
38
+            yield ((len(path) + 7 - coords[0] - coords[1], new_coords, path + direction))
39
+
40
+
41
+def find_paths():
42
+    """Finds all paths from the starting point of (0, 0) to the end point of (3, 3).
43
+
44
+    Paths are searched in priority order, meaning the shortest path is returned first and the longest path returned
45
+    last.
46
+
47
+    :return: A generator which emits paths (as strings of directions, e.g. 'UDDDRRR...') from shortest to longest
48
+    """
49
+    queue = []
50
+    heapq.heappush(queue, (6, (0, 0), ''))
51
+    while len(queue):
52
+        _, coords, path = heapq.heappop(queue)
53
+        if coords == (3, 3):
54
+            yield path
55
+        else:
56
+            for move in next_moves(coords, path):
57
+                heapq.heappush(queue, move)
58
+
59
+
60
+paths = find_paths()
61
+print('Step 1: %s' % next(paths))
62
+print('Step 2: %s' % len(functools.reduce(lambda x, y: y, paths)))

Loading…
Cancel
Save