cig.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import heapq
  2. import sys
  3. def log(*msg):
  4. print(*msg, file=sys.stderr)
  5. class Queue():
  6. def __init__(self):
  7. self.items = []
  8. def __bool__(self):
  9. return bool(self.items)
  10. def __repr__(self):
  11. return str(self.items)
  12. def values(self):
  13. return (v for _, v in self.items)
  14. def put(self, item, priority):
  15. heapq.heappush(self.items, (priority, item))
  16. def fput(self, item, priority):
  17. while priority in [p for p, _ in self.items]:
  18. priority += 1
  19. self.put(item, priority)
  20. def get(self):
  21. return heapq.heappop(self.items)[1]
  22. class PathNode(tuple):
  23. def __new__(self, x, y, parent=None):
  24. n = tuple.__new__(self, (x, y))
  25. n.parent = parent
  26. n.cost = 0
  27. return n
  28. def __repr__(self):
  29. return f"<{self[0]}, {self[1]}, c:{self.cost}>"
  30. h, w, n = [int(input()) for _ in range(3)]
  31. log("INIT: ", h, w, n)
  32. LEFT = "E"
  33. RIGHT = "A"
  34. DOWN = "D"
  35. UP = "C"
  36. STAY = "B"
  37. ACTIONS = [LEFT, RIGHT, DOWN, UP, STAY]
  38. MOVES = [UP, RIGHT, DOWN, LEFT] # WARNING: order matters here
  39. class Grid():
  40. UNKNOWN = 0
  41. WALL = 1
  42. GROUND = 2
  43. VECT = {UP: (0,-1), RIGHT: (1,0), DOWN: (0,1), LEFT: (-1,0)}
  44. INV = {UP: DOWN, RIGHT: LEFT, LEFT: RIGHT, DOWN: UP, STAY: STAY}
  45. def __init__(self, w, h):
  46. self.w = w
  47. self.h = h
  48. self.cells = {(x, y): Grid.UNKNOWN for x in range(self.w) for y in range(self.h)}
  49. self.player = []
  50. self.ghosts = []
  51. self.deadends = []
  52. def update(self, player, ghosts, available_moves):
  53. self.player = player
  54. self.ghosts = ghosts
  55. self.available_moves = available_moves
  56. for p in [self.player] + self.ghosts:
  57. self.cells[p] = Grid.GROUND
  58. for m in MOVES:
  59. p = grid.pos_after(self.player, m)
  60. self.cells[p] = Grid.GROUND if m in available_moves else Grid.WALL
  61. self.propagate()
  62. def _repr_cell(self, p):
  63. if p == self.player:
  64. return "P"
  65. elif p in self.ghosts:
  66. return "G"
  67. elif self.cells[p] == Grid.WALL:
  68. return "x"
  69. elif self.cells[p] == Grid.GROUND:
  70. return "_"
  71. else:
  72. return "."
  73. def graph(self):
  74. return "\n".join(["".join([self._repr_cell((x, y)) for x in range(self.w)]) for y in range(self.h) if abs(y - self.player[1]) < 15])
  75. def set_visited(self, p):
  76. self.cells[p] = Grid.GROUND
  77. def set_wall(self, p):
  78. self.cells[p] = Grid.WALL
  79. @staticmethod
  80. def pos_after(current, move):
  81. x, y = current
  82. dx, dy = Grid.VECT[move]
  83. return x + dx, y + dy
  84. @staticmethod
  85. def dist(p1, p2):
  86. xa, ya = p1
  87. xb, yb = p2
  88. return abs(xa - xb) + abs(ya - yb)
  89. def neighbors(self, x, y, diags=False):
  90. neighs = [(x, y - 1), (x - 1, y), (x + 1, y), (x, y + 1)]
  91. if diags:
  92. neighs += [(x - 1, y - 1), (x + 1, y - 1), (x - 1, y + 1), (x + 1, y + 1)]
  93. return [(x, y) for x, y in neighs if 0 <= x < self.w and 0 <= y < self.h]
  94. def path(self, start, target):
  95. nodes = Queue()
  96. origin = PathNode(*start)
  97. nodes.put(origin, 0)
  98. neighbors = []
  99. while nodes:
  100. current = nodes.get()
  101. if current == target:
  102. path = []
  103. previous = current
  104. while previous:
  105. if previous != start:
  106. path.insert(0, previous)
  107. previous = previous.parent
  108. return path
  109. neighbors = self.neighbors(*current)
  110. for x, y in neighbors:
  111. if (x, y) == current.parent:
  112. continue
  113. if self.cells[(x, y)] == Grid.WALL:
  114. continue
  115. cost = current.cost + 1
  116. priority = cost + Grid.dist((x, y), target)
  117. node = PathNode(x, y, current)
  118. node.cost = cost
  119. nodes.put(node, priority)
  120. return None
  121. def propagate(self):
  122. self.propagation = {}
  123. for m in self.available_moves:
  124. self.propagation[m] = 0
  125. visited= set()
  126. start = self.pos_after(self.player, m)
  127. buffer = [start]
  128. visited |= set(buffer)
  129. while buffer:
  130. new_buffer = []
  131. if any(p in self.ghosts for p in buffer):
  132. break
  133. for p in buffer:
  134. for n in self.neighbors(*p):
  135. if not n in visited and \
  136. not n in new_buffer and \
  137. not n == self.player and \
  138. not self.cells[n] == Grid.WALL:
  139. new_buffer.append(n)
  140. visited.add(n)
  141. self.propagation[m] += 2 if self.cells[n] == Grid.UNKNOWN else 1
  142. buffer = new_buffer
  143. grid = Grid(w, h)
  144. turn = 0
  145. previous = STAY
  146. while True:
  147. turn += 1
  148. available_moves = [m for m in MOVES if input() == "_"]
  149. log(available_moves)
  150. l = [tuple([int(j) for j in input().split()]) for _ in range(n)]
  151. *ghosts, player= l
  152. grid.update(player, ghosts, available_moves)
  153. log(player, ghosts)
  154. log(grid.graph())
  155. log(grid.propagation)
  156. q = Queue()
  157. for m in available_moves:
  158. visited = 20*(m == grid.INV[previous])
  159. danger = sum([grid.dist(grid.pos_after(player, m), e) for e in ghosts])
  160. danger = 100 * danger // (len(ghosts) * (w + h))
  161. perspective = grid.propagation[m]
  162. perspective = 100 * perspective // (max(grid.propagation.values()) or 1)
  163. interest = visited - danger - perspective
  164. log(m, visited, danger, perspective, interest)
  165. q.fput(m, interest)
  166. move = q.get()
  167. previous = move
  168. print(move)