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.

92 satır
2.4KB

  1. import collections
  2. import itertools
  3. import sys
  4. from functools import partial
  5. from toolkit import read_image, render, shortest_path
  6. def read_in(text):
  7. grid = read_image(text)
  8. elements = {v: k for k, v in grid.items()}
  9. keys = {c for c in elements if c.isalpha() and c.islower()}
  10. return grid, elements, keys
  11. def move(grid, pos):
  12. for ori in [1, -1, 1j, -1j]:
  13. if grid[pos + ori] != '#':
  14. yield pos + ori
  15. def trim(states, metric):
  16. groups = collections.defaultdict(list)
  17. for state in states:
  18. tips = frozenset(seq[-1] for seq in state)
  19. bulk = frozenset(''.join(state))
  20. groups[bulk, tips].append(state)
  21. return [min(groups[k], key=metric) for k in groups]
  22. def precalc_moves(entrances, keys, elements, grid):
  23. reqs = collections.defaultdict(dict)
  24. lengths = {}
  25. for a, b in itertools.combinations(keys.union(entrances), 2):
  26. try:
  27. path = shortest_path(elements[a], elements[b], partial(move, grid))
  28. lengths[a, b] = lengths[b, a] = len(path) - 1
  29. doors = {c for c in map(grid.get, path) if c.isupper()}
  30. reqs[a][b] = reqs[b][a] = {d.lower() for d in doors}
  31. except RuntimeError:
  32. continue
  33. return reqs, partial(calc_total, lengths)
  34. def calc_total(lengths, state):
  35. return sum(lengths[pair] for seq in state for pair in zip(seq, seq[1:]))
  36. def mod_grid(text):
  37. grid, elements, keys = read_in(text)
  38. pos = elements['@']
  39. grid[pos] = '#'
  40. for i, ori in enumerate([1, -1, 1j, -1j]):
  41. grid[pos + ori] = '#'
  42. grid[pos + ori * (1 + 1j)] = chr(ord('@') - i)
  43. return render(grid)
  44. def solve(text):
  45. grid, elements, keys = read_in(text)
  46. stacks = {k for k in elements if not k.isalnum()} - {'#', '.'}
  47. requirements, metric = precalc_moves(stacks, keys, elements, grid)
  48. states = [stacks]
  49. for _ in keys:
  50. states = trim([
  51. state ^ {seq, seq + new}
  52. for state in states
  53. for seq in state
  54. for new, reqs in requirements[seq[-1]].items()
  55. if new not in seq and reqs.issubset(''.join(state))
  56. ], metric)
  57. best = min(states, key=metric)
  58. print(best)
  59. print(metric(best))
  60. text = sys.stdin.read()
  61. text = '''
  62. #############
  63. #g#f.D#..h#l#
  64. #F###e#E###.#
  65. #dCba...BcIJ#
  66. #####.@.#####
  67. #nK.L...G...#
  68. #M###N#H###.#
  69. #o#m..#i#jk.#
  70. #############
  71. '''
  72. solve(text)
  73. text = mod_grid(text)
  74. solve(text)