main.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. import heapq
  2. import sys
  3. import math
  4. import time
  5. debug = True
  6. t0 = time.time()
  7. def log(*msg):
  8. if debug:
  9. print("{} - ".format(str(time.time() - t0)[1:5]), *msg, file=sys.stderr, flush=True)
  10. def time_to(total, step):
  11. """ number of steps to reach total """
  12. return total // step + (1 if total % step > 0 else 0)
  13. class BaseClass:
  14. def __repr__(self):
  15. return f"<{self.__class__.__name__}: {self.__dict__}>"
  16. class Queue(BaseClass):
  17. def __init__(self):
  18. self.items = []
  19. def __bool__(self):
  20. return bool(self.items)
  21. def __repr__(self):
  22. return str(self.items)
  23. def values(self):
  24. return (v for _, v in self.items)
  25. def put(self, priority, item):
  26. while priority in [p for p, _ in self.items]:
  27. priority += 1
  28. heapq.heappush(self.items, (priority, item))
  29. def get(self):
  30. return heapq.heappop(self.items)[1]
  31. def get_items(self):
  32. return heapq.heappop(self.items)
  33. class Player(BaseClass):
  34. def __init__(self, id_):
  35. self.id = id_
  36. ME = Player(int(input())) # Input gives the player id: 0 plays first
  37. OPPONENT = Player(1 - ME.id)
  38. PLAYERS_INDEX = {p.id: p for p in [ME, OPPONENT]}
  39. PLAYERS_ORDER = sorted([ME, OPPONENT], key=lambda p: p.id)
  40. class Unit(BaseClass):
  41. TYPE_CULTIST = 0
  42. TYPE_CULT_LEADER = 1
  43. OWNER_0 = 0
  44. OWNER_1 = 1
  45. NO_OWNER = 2
  46. SHOOTING_RANGE = 6
  47. SHOOTING_MAX_DAMAGE = 7
  48. def __init__(self, id_):
  49. self.id = id_
  50. self.hp = 10
  51. self.x = None
  52. self.y = None
  53. self.owner = None
  54. @property
  55. def pos(self):
  56. return self.x, self.y
  57. @property
  58. def owned(self):
  59. return self.owner == ME.id
  60. @property
  61. def opponent(self):
  62. return self.owner == OPPONENT.id
  63. @property
  64. def neutral(self):
  65. return self.owner == self.NO_OWNER
  66. class CultLeader(Unit):
  67. pass
  68. class Action(BaseClass):
  69. pass
  70. class ActionWait(Action):
  71. def exec(self):
  72. print("WAIT")
  73. class ActionMove(Action):
  74. def __init__(self, unit, pos):
  75. self.unit = unit
  76. self.pos = pos
  77. def exec(self):
  78. print(f"{self.unit.id} MOVE {self.pos[0]} {self.pos[1]}")
  79. class ActionShoot(Action):
  80. def __init__(self, unit, target):
  81. self.unit = unit
  82. self.target = target
  83. def exec(self, id_, target_id):
  84. print(f"{self.unit.id} SHOOT {self.target.id}")
  85. class ActionConvert(Action):
  86. def __init__(self, unit, target):
  87. self.unit = unit
  88. self.target = target
  89. def exec(self):
  90. print(f"{self.unit.id} CONVERT {self.target.id}")
  91. class Grid(BaseClass):
  92. def __init__(self, width, height):
  93. self.width = width
  94. self.height = height
  95. self.obstacles = []
  96. self.index = {}
  97. self.units = {}
  98. self.round = 0
  99. def update_unit(self, id_, type_, hp, x, y, owner):
  100. if id_ not in self.units:
  101. self.units[id_] = Unit(id_) if type_ != Unit.TYPE_CULT_LEADER else CultLeader(id_)
  102. unit = self.units[id_]
  103. unit.hp = hp
  104. unit.x = x
  105. unit.y = y
  106. unit.owner = owner
  107. def update_index(self):
  108. self.index = {}
  109. for unit in self.units.values():
  110. self.index[(unit.x, unit.y)] = unit
  111. def own_cult_leader(self):
  112. return next(u for u in self.units.values() if type(u) is CultLeader and u.owned)
  113. def opponent_cult_leader(self):
  114. return [u for u in self.units.values() if type(u) is CultLeader and not u.owned]
  115. def allied_cultists(self):
  116. return [u for u in self.units.values() if type(u) is not CultLeader and u.owned]
  117. def opponent_cultists(self):
  118. return [u for u in self.units.values() if type(u) is not CultLeader and not u.owned]
  119. def neutrals(self):
  120. return [u for u in self.units.values() if u.neutral]
  121. def list_actions(self):
  122. actions = Queue()
  123. cult_leader = self.own_cult_leader()
  124. for n in self.neutrals():
  125. action = ActionConvert(cult_leader, n)
  126. distance = self.manhattan(cult_leader.pos, n.pos)
  127. actions.put(distance, action)
  128. return actions
  129. def in_grid(self, pos):
  130. return 0 <= pos[0] < self.width and 0 <= pos[1] < self.height
  131. def can_see_trough(self, pos):
  132. return self.in_grid(pos) and pos not in self.obstacles + list(self.index.values())
  133. def can_move_on(self, pos):
  134. return self.in_grid(pos) and pos not in self.obstacles + list(self.index.values())
  135. @staticmethod
  136. def manhattan(from_, to_):
  137. xa, ya = from_
  138. xb, yb = to_
  139. return abs(xa - xb) + abs(ya - yb)
  140. @classmethod
  141. def line(cls, from_, to_):
  142. """ Implementation of bresenham's algorithm """
  143. xa, ya = from_
  144. xb, yb = to_
  145. if (xa, ya) == (xb, yb):
  146. return [(xa, ya)]
  147. # diagonal symmetry
  148. vertically_oriented = (abs(yb - ya) > abs(xb - xa))
  149. if vertically_oriented:
  150. ya, xa, yb, xb = xa, ya, xb, yb
  151. # horizontal symmetry
  152. reversed_sym = (xa > xb)
  153. if reversed_sym:
  154. xb, yb, xa, ya = xa, ya, xb, yb
  155. # angle
  156. dx, dy = xb - xa, yb - ya
  157. alpha = (abs(dy) / dx)
  158. offset = 0.0
  159. step = 1 if dy > 0 else -1
  160. result = []
  161. y_ = ya
  162. for x_ in range(xa, xb + 1):
  163. result.append((y_, x_) if vertically_oriented else (x_, y_))
  164. offset += alpha
  165. if offset > 0.5:
  166. y_ += step
  167. offset -= 1.0
  168. if reversed_sym:
  169. result.reverse()
  170. return result
  171. # Create grid
  172. GRID = Grid(*[int(i) for i in input().split()])
  173. GRID.obstacles = [(i, j) for i in range(GRID.height) for j, val in enumerate(input()) if val == 'x']
  174. while 1:
  175. for _ in range(int(input())):
  176. GRID.update_unit(*[int(j) for j in input().split()])
  177. GRID.update_index()
  178. actions = GRID.list_actions()
  179. log(actions)
  180. try:
  181. action = actions.get()
  182. except IndexError:
  183. action = ActionWait()
  184. action.exec()
  185. GRID.round += 1