pathfinder.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import heapq
  2. import time
  3. t0 = time.time()
  4. class DiscoveryNode(tuple):
  5. def __new__(cls, x, y, cost=0, ancestors=None, matches=None):
  6. n = tuple.__new__(cls, (x, y))
  7. n.cost = cost
  8. n.ancestors = ancestors if ancestors is not None else []
  9. n.matches = matches if matches is not None else []
  10. return n
  11. def __repr__(self):
  12. return f"<{self[0]}, {self[1]}>"
  13. class Queue:
  14. def __init__(self):
  15. self.items = []
  16. def __bool__(self):
  17. return bool(self.items)
  18. def __repr__(self):
  19. return str(self.items)
  20. def values(self):
  21. return (v for _, v in self.items)
  22. def put(self, priority, item):
  23. while priority in [p for p, _ in self.items]:
  24. priority += 1
  25. heapq.heappush(self.items, (priority, item))
  26. def get(self):
  27. return heapq.heappop(self.items)[1]
  28. def get_items(self):
  29. return heapq.heappop(self.items)
  30. def log(*msg):
  31. print("{} - ".format(str(time.time() - t0)[:5]), *msg)
  32. start = (0, 3)
  33. obstacles = [(0, 0), (1, 0), (3, 0), (9, 0), (11, 0), (12, 0), (0, 1), (1, 1), (5, 1), (7, 1), (11, 1), (12, 1), (3, 3), (9, 3)]
  34. neutrals = [(1, 4), (2, 3), (1, 5), (3, 5), (2, 6), (4, 1), (11, 4), (10, 3), (11, 5), (9, 5), (10, 6), (8, 1)]
  35. width = 13
  36. height = 7
  37. def get_neighbors(x, y):
  38. return [(xn, yn) for xn, yn in [(x, y - 1), (x - 1, y), (x + 1, y), (x, y + 1)] if
  39. 0 <= xn < width and 0 <= yn < height]
  40. def can_move_on(pos):
  41. return pos not in obstacles and pos not in neutrals
  42. def discover_multiple(start, key, limit=5):
  43. nodes = Queue()
  44. its, break_on = 0, 20000
  45. origin = DiscoveryNode(*start)
  46. nodes.put(0, origin)
  47. while nodes:
  48. current = nodes.get()
  49. neighbors = get_neighbors(*current)
  50. for pos in neighbors:
  51. its += 1
  52. if its > break_on:
  53. log(f"<!> discovery broke")
  54. break
  55. if not can_move_on(pos):
  56. continue
  57. moving_cost = 1
  58. matches = []
  59. for n in get_neighbors(*pos):
  60. if n not in current.matches and key(n):
  61. matches.append(n)
  62. cost = current.cost + moving_cost
  63. priority = 100 * cost - (200 * (len(current.matches) + len(matches)))
  64. priority += 10 * len([a for a in current.ancestors if a == pos]) # décourage de revenir à une case visitée
  65. node = DiscoveryNode(
  66. pos[0],
  67. pos[1],
  68. cost,
  69. current.ancestors + [current],
  70. current.matches + matches
  71. )
  72. nodes.put(priority, node)
  73. if len(current.matches) >= limit or its > break_on:
  74. break
  75. best_node = nodes.get()
  76. path = best_node.ancestors[1:] + [best_node]
  77. return path, best_node.matches
  78. log("start")
  79. res = discover_multiple(start, key=lambda x: x in neutrals, limit=6)
  80. log(res)