solution_wood.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. '''
  2. @author: olivier.massot, 2019
  3. '''
  4. import heapq
  5. import sys
  6. DEBUG = False
  7. def log(x):
  8. if DEBUG:
  9. print(x, file=sys.stderr)
  10. # Cells
  11. START_0 = '0'
  12. START_1 = '1'
  13. BLUEBERRIES_CRATE = "B"
  14. ICE_CREAM_CRATE = "I"
  15. WINDOW = "W"
  16. EMPTY_TABLE = "#"
  17. DISHWASHER = "D"
  18. FLOOR_CELL = "."
  19. # Items
  20. NONE = "NONE"
  21. DISH = "DISH"
  22. ICE_CREAM = "ICE_CREAM"
  23. BLUEBERRIES = "BLUEBERRIES"
  24. location = {DISH: DISHWASHER,
  25. ICE_CREAM: ICE_CREAM_CRATE,
  26. BLUEBERRIES: BLUEBERRIES_CRATE,
  27. WINDOW: WINDOW}
  28. special = (BLUEBERRIES_CRATE, ICE_CREAM_CRATE, WINDOW, DISHWASHER)
  29. class Base():
  30. def __repr__(self):
  31. return f"<{self.__class__.__name__}: {self.__dict__}>"
  32. class Customer(Base):
  33. def __init__(self, item, award):
  34. self.item = item
  35. self.award = int(award)
  36. class Cook(Base):
  37. def __init__(self, x, y, item):
  38. self.x = int(x)
  39. self.y = int(y)
  40. self.item = item
  41. self.order = ""
  42. @property
  43. def pos(self):
  44. return (self.x, self.y)
  45. def use(self, x, y, msg=""):
  46. print("USE", x, y, msg)
  47. def move(self, x, y):
  48. print("MOVE", x, y)
  49. def wait(self):
  50. print("WAIT")
  51. class Table(Base):
  52. def __init__(self, x, y, item):
  53. self.x = int(x)
  54. self.y = int(y)
  55. self.item = item
  56. class Oven(Base):
  57. def __init__(self, contents, timer):
  58. self.contents = contents
  59. self.timer = int(timer)
  60. class PathNode(tuple):
  61. def __new__(self, x, y, parent=None):
  62. n = tuple.__new__(self, (x, y))
  63. n.parent = parent
  64. n.cost = 0
  65. return n
  66. class Grid(Base):
  67. def __init__(self, cells):
  68. self.cells = cells
  69. self.w, self.h = len(cells[0]), len(cells)
  70. self.add_costs = {}
  71. def at(self, x, y):
  72. return self.cells[y][x]
  73. def flatten(self):
  74. return [(x, y, c) for y, row in enumerate(self.cells) for x, c in enumerate(row)]
  75. @property
  76. def coords(self):
  77. return [(x, y) for y in range(self.h) for x in range(self.w)]
  78. def where_are(self, content):
  79. return [(x, y) for x, y, c in self.flatten() if c == content]
  80. @staticmethod
  81. def distance(from_, to_):
  82. return abs(from_[0] - to_[0]) + abs(from_[1] - to_[1])
  83. def closest(self, from_, content):
  84. return sorted([(c, Grid.distance(from_, c)) for c in self.where_are(content)], key=lambda k: k[1])[0]
  85. def neighbors(self, x, y, diags=False):
  86. neighs = [(x, y - 1), (x - 1, y), (x + 1, y), (x, y + 1)]
  87. if diags:
  88. neighs += [(x - 1, y - 1), (x + 1, y - 1), (x - 1, y + 1), (x + 1, y + 1)]
  89. return [(x, y) for x, y in neighs if 0 <= x < self.w and 0 <= y < self.h]
  90. def passable(self, x, y):
  91. return self.at(x, y) in (FLOOR_CELL, START_0, START_1)
  92. def cost(self, x, y):
  93. return 10 + self.add_costs.get((x, y), 0)
  94. def path(self, origin, target, incl_start=False):
  95. nodes = []
  96. origin = PathNode(*origin)
  97. targets = grid.neighbors(*target, diags=True)
  98. heapq.heappush(nodes, (0, origin))
  99. while nodes:
  100. current = heapq.heappop(nodes)[1]
  101. if current in targets:
  102. path = []
  103. next_ = current
  104. while next_:
  105. if next_ != origin or incl_start:
  106. path.insert(0, next_)
  107. next_ = next_.parent
  108. return path
  109. neighbors = self.neighbors(*current, False)
  110. for x, y in neighbors:
  111. if not self.passable(x, y):
  112. continue
  113. cost = current.cost + self.cost(x, y)
  114. priority = cost + 10 * (abs(x - target[0]) + abs(y - target[1]))
  115. node = PathNode(x, y, current)
  116. node.cost = cost
  117. heapq.heappush(nodes, (priority, node))
  118. else:
  119. return None
  120. # input vars
  121. num_all_customers = int(input())
  122. all_customers = [Customer(*input().split()) for _ in range(num_all_customers)]
  123. grid = Grid([list(input()) for i in range(7)])
  124. log(f"{num_all_customers} customers: {all_customers}")
  125. log(f"grid: {grid}")
  126. class Task(Base):
  127. def __init__(self, name, from_):
  128. self.name = name
  129. self.loc = location[name]
  130. self.pos, self.dist = grid.closest(from_, self.loc)
  131. while True:
  132. turns_remaining = int(input())
  133. player = Cook(*input().split())
  134. log(f"*** player: {player}")
  135. partner = Cook(*input().split())
  136. log(f"*** partner: {partner}")
  137. num_tables_with_items = int(input()) # the number of tables in the kitchen that currently hold an item
  138. tables = [Table(*input().split()) for _ in range(num_tables_with_items)]
  139. log(f"*** tables: {tables}")
  140. oven = Oven(*input().split())
  141. log(f"*** oven: {oven}")
  142. num_customers = int(input()) # the number of customers currently waiting for food
  143. customers = [Customer(*input().split()) for _ in range(num_customers)]
  144. log(f"*** customers: {customers}")
  145. ### SCRIPT
  146. # if no current order, take the most beneficial
  147. if not player.order:
  148. queue = sorted(customers, reverse=True, key=lambda x: x.award)
  149. player.order = queue[0].item.split('-')
  150. log(f'>>> new order taken: {player.order}')
  151. todo = [Task(t, player.pos) for t in player.order if t not in player.item]
  152. log(f"todo: {todo}")
  153. if not todo:
  154. # no task left, target is the window
  155. next_task = Task(WINDOW, player.pos)
  156. player.order, todo = [], []
  157. elif player.item != NONE and not DISH in player.item:
  158. # cook has something in its hands and no dish, he have to take one
  159. next_task = next((t for t in todo if t.name == "DISH"))
  160. else:
  161. # else, go for the closest task
  162. tasks = sorted(todo, key= lambda x: x.dist)
  163. next_task = next(iter(tasks), None)
  164. log(f"next_task: {next_task}")
  165. # update grid movement costs following the probability of finding the partner here
  166. partner_could_be_there = [(x, y) for x, y in grid.coords if grid.passable(x, y) and grid.distance(partner.pos, (x, y)) <= 4]
  167. grid.add_costs = {}
  168. for x, y in partner_could_be_there:
  169. k1 = 2 if (x, y) == partner.pos else 1
  170. # cell is next to a special cell, partner has more chance to stop there
  171. k2 = 2 if any((c for c in grid.neighbors(x, y) if grid.at(*c) in special )) else 1
  172. grid.add_costs[(x, y)] = k1 * k2 * 3
  173. log(grid.add_costs)
  174. path = grid.path(player.pos, next_task.pos)
  175. log(path)
  176. if path is not None:
  177. if len(path) > 0:
  178. if len(path) > 4:
  179. player.move(*path[3])
  180. else:
  181. player.move(*path[-1])
  182. else:
  183. player.use(*next_task.pos)
  184. else:
  185. player.use(*next_task.pos)