Advent of Code 2016 solutions https://adventofcode.com/2016/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

17.py 2.3KB

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