|
|
@@ -7,13 +7,13 @@ import sys
|
|
|
import time
|
|
|
|
|
|
# TODO
|
|
|
-# * always priorize the attack if level allows it
|
|
|
-# * take levels in account
|
|
|
-# * priorize the attack of the ennemy hq
|
|
|
-# x build lvl2 and 3 units
|
|
|
-# * priorize move on mines
|
|
|
-# * priorize move on 'pivot-cells' which would cause desactivation of others
|
|
|
-# * don't let ennemies inside the defense (=> nearer of the QG than other units)
|
|
|
+# x update the grid state after each action
|
|
|
+# * improve the conditions for training
|
|
|
+# * estimate the threat level of ennemies (proximity to HQ or to a pivot, unprotected cells around or inferior units)
|
|
|
+# * use a propagation loop to estimate the weight of a pivot (both opponents and owned)
|
|
|
+# * modelize fronts
|
|
|
+# * moving part: order units action order by proximity to an ennemy unit / building
|
|
|
+# * add the 'lasso' technique
|
|
|
|
|
|
debug = True
|
|
|
t0 = time.time()
|
|
|
@@ -82,46 +82,59 @@ class Action(Base):
|
|
|
class Move(Action):
|
|
|
def __init__(self, target, unit):
|
|
|
self.unit = unit
|
|
|
+
|
|
|
+ self.possession = 0
|
|
|
+ self.pivot = False
|
|
|
+ self.prey_level = 0
|
|
|
+ self.union = 0
|
|
|
+ self.depth = 0
|
|
|
+ self.hq = False
|
|
|
+ self.tower = False
|
|
|
+ self.mine = False
|
|
|
+ self.dist_to_goal = 0
|
|
|
+
|
|
|
super().__init__(target, unit)
|
|
|
|
|
|
def eval(self):
|
|
|
# the lower the better
|
|
|
|
|
|
self.interest = 0
|
|
|
- if self.target.owned and self.target.active:
|
|
|
- self.interest += 15 # already owned and active
|
|
|
-
|
|
|
- elif self.target.opponents and self.target.active:
|
|
|
- self.interest -= 15 # owned by opponents and active
|
|
|
+
|
|
|
+ if self.target.active_owned:
|
|
|
+ self.possession = 1
|
|
|
+ elif self.target.active_opponent:
|
|
|
+ self.possession = -1
|
|
|
|
|
|
- # non-passable cells around
|
|
|
- self.interest += 3 * len([n for n in self.target.neighbors if not grid[n].movable])
|
|
|
+ self.pivot = self.target.pivot
|
|
|
+
|
|
|
+ self.dist_to_goal = Grid.manhattan(self.target.pos, opponent.hq.pos)
|
|
|
|
|
|
# an ennemy here
|
|
|
if self.target.unit and self.target.unit.opponents:
|
|
|
if self.target.unit.level < self.unit.level or self.unit.level == 3:
|
|
|
- self.interest -= 30
|
|
|
-
|
|
|
- # an ennemy nearby
|
|
|
- for n in self.target.neighbors:
|
|
|
- if grid[n].unit and grid[n].unit.opponents and grid[n].unit.level >= self.unit.level:
|
|
|
- self.interest += 15
|
|
|
+ self.prey_level = self.target.unit.level
|
|
|
|
|
|
# priorize adjacent cells
|
|
|
- self.interest -= (2 * len([n for n in self.target.neighbors if grid[n].owned]))
|
|
|
+ self.union = len([n for n in self.target.neighbors if grid[n].active_owned])
|
|
|
|
|
|
- # include 'heatmap'
|
|
|
- self.interest += 3 * self.target.heat
|
|
|
+ # include 'depthmap'
|
|
|
+ self.depth = self.target.depth
|
|
|
|
|
|
# priorize mines or HQ
|
|
|
- if self.target.building:
|
|
|
- if self.target.building.type_ == Building.HQ:
|
|
|
- self.interest -= 50
|
|
|
- if self.target.building.type_ == Building.MINE:
|
|
|
- self.interest -= 15
|
|
|
- if self.target.building.type_ == Building.TOWER and self.unit.level == 3:
|
|
|
- self.interest -= 15
|
|
|
-
|
|
|
+ if self.target.building and self.target.building.opponents:
|
|
|
+ self.hq = self.target.building.type_ == Building.HQ
|
|
|
+ self.tower = self.target.building.type_ == Building.TOWER and self.unit.level == 3
|
|
|
+ self.mine = self.target.building.type_ == Building.MINE
|
|
|
+
|
|
|
+ self.interest = 15 * self.possession - 10 * self.pivot - 10 * self.prey_level \
|
|
|
+ - 2 * self.union + 3 * self.depth + self.dist_to_goal \
|
|
|
+ - 30 * self.tower - 15 * self.mine \
|
|
|
+ - 100 * self.hq
|
|
|
+
|
|
|
+ def __repr__(self):
|
|
|
+ return f"<{self.__class__.__name__} ({self.x}, {self.y}), {self.unit.id_}: {self.interest} (" \
|
|
|
+ f"{self.possession} / {self.pivot} / {self.prey_level} / {self.union} / {self.dist_to_goal}" \
|
|
|
+ f" / {self.depth} / {self.tower} / {self.mine} / {self.hq})>"
|
|
|
|
|
|
def resolution(self):
|
|
|
return f"MOVE {self.unit.id_} {self.target.x} {self.target.y}"
|
|
|
@@ -129,33 +142,55 @@ class Move(Action):
|
|
|
class Train(Move):
|
|
|
def __init__(self, target, level=1):
|
|
|
self.level = level
|
|
|
+
|
|
|
+ self.possession = 0
|
|
|
+ self.pivot = False
|
|
|
+ self.prey_level = 0
|
|
|
+ self.union = 0
|
|
|
+ self.depth = 0
|
|
|
+ self.hq = False
|
|
|
+ self.tower = False
|
|
|
+ self.mine = False
|
|
|
+ self.dist_to_goal = 0
|
|
|
+
|
|
|
super().__init__(target, level)
|
|
|
+
|
|
|
|
|
|
def eval(self):
|
|
|
# the lower the better
|
|
|
self.interest = 0
|
|
|
- if self.target.owned and self.target.active:
|
|
|
- self.interest += 15 # already owned and active
|
|
|
-
|
|
|
- elif self.target.opponents and self.target.active:
|
|
|
- self.interest -= 15 # owned by opponents and active
|
|
|
+
|
|
|
+ if self.target.active_owned:
|
|
|
+ self.possession = 1
|
|
|
+ elif self.target.active_opponent:
|
|
|
+ self.possession = -1
|
|
|
|
|
|
- # non-passable cells around
|
|
|
- self.interest += 2 * len([n for n in self.target.neighbors if not grid[n].movable])
|
|
|
+ self.pivot = self.target.pivot
|
|
|
+
|
|
|
+ self.dist_to_goal = Grid.manhattan(self.target.pos, opponent.hq.pos)
|
|
|
+
|
|
|
+ # an ennemy here
|
|
|
+ if self.target.unit and self.target.unit.opponents:
|
|
|
+ if self.target.unit.level < self.level or self.level == 3:
|
|
|
+ self.prey_level = self.target.unit.level
|
|
|
|
|
|
# priorize adjacent cells
|
|
|
- self.interest -= (2 * len([n for n in self.target.neighbors if grid[n].owned]))
|
|
|
-
|
|
|
- # include 'heatmap'
|
|
|
- self.interest += 3 * self.target.heat
|
|
|
+ self.union = len([n for n in self.target.neighbors if grid[n].active_owned])
|
|
|
|
|
|
- # needs reinforcement?
|
|
|
- for n in self.target.neighbors:
|
|
|
- if grid[n].unit and grid[n].unit.owned:
|
|
|
- self.interest += 5
|
|
|
- if grid[n].unit and grid[n].unit.opponents:
|
|
|
- self.interest -= 5
|
|
|
+ # include 'depthmap'
|
|
|
+ self.depth = self.target.depth
|
|
|
+
|
|
|
+ # priorize mines or HQ
|
|
|
+ if self.target.building and self.target.building.opponents:
|
|
|
+ self.hq = self.target.building.type_ == Building.HQ
|
|
|
+ self.tower = self.target.building.type_ == Building.TOWER and self.level == 3
|
|
|
+ self.mine = self.target.building.type_ == Building.MINE
|
|
|
|
|
|
+ self.interest = 15 * self.possession - 10 * self.pivot - 10 * self.prey_level \
|
|
|
+ - 2 * self.union + 3 * self.depth + self.dist_to_goal \
|
|
|
+ - 30 * self.tower - 15 * self.mine \
|
|
|
+ - 100 * self.hq
|
|
|
+
|
|
|
def resolution(self):
|
|
|
return f"TRAIN {self.level} {self.target.x} {self.target.y}"
|
|
|
|
|
|
@@ -167,7 +202,13 @@ class Build(Action):
|
|
|
def eval(self):
|
|
|
# the lower the better
|
|
|
self.interest = 0
|
|
|
- self.interest -= self.target.heat
|
|
|
+
|
|
|
+ if self.type_ == Building.MINE:
|
|
|
+ self.interest -= self.target.depth
|
|
|
+
|
|
|
+ elif self.type_ == Building.TOWER:
|
|
|
+ if self.target.pivot:
|
|
|
+ self.interest -= 20
|
|
|
|
|
|
def resolution(self):
|
|
|
str_type = {1: "MINE", 2: "TOWER"}[self.type_]
|
|
|
@@ -204,6 +245,9 @@ class Building(BaseOwnedLoc):
|
|
|
MINE = 1
|
|
|
TOWER = 2
|
|
|
|
|
|
+ cost = {0: 0, 1: 20, 2: 15}
|
|
|
+ maintenance = {0: 0, 1: 0, 2: 0}
|
|
|
+
|
|
|
def __init__(self, owner, type_, x, y):
|
|
|
super().__init__(x, y, owner)
|
|
|
self.type_ = type_
|
|
|
@@ -213,7 +257,7 @@ class Building(BaseOwnedLoc):
|
|
|
return self.type_ == Building.HQ
|
|
|
|
|
|
class Unit(BaseOwnedLoc):
|
|
|
- training_cost = {1: 10, 2: 20, 3: 30}
|
|
|
+ cost = {1: 10, 2: 20, 3: 30}
|
|
|
maintenance = {1: 1, 2: 4, 3: 20}
|
|
|
def __init__(self, owner, id_, level, x, y):
|
|
|
super().__init__(x, y, owner)
|
|
|
@@ -246,7 +290,16 @@ class Cell(Base):
|
|
|
self.building = None
|
|
|
self.mine_site = None
|
|
|
|
|
|
- self.heat = 0
|
|
|
+ self.under_tower = False
|
|
|
+ self.depth = 0
|
|
|
+ self.pivot = False
|
|
|
+ self.needs_unit = False
|
|
|
+
|
|
|
+ # front cells
|
|
|
+ self.facing = []
|
|
|
+ self.support = []
|
|
|
+ self.threat = 0
|
|
|
+ self.in_front_of = []
|
|
|
|
|
|
@property
|
|
|
def pos(self):
|
|
|
@@ -261,8 +314,41 @@ class Cell(Base):
|
|
|
self.unit = unit
|
|
|
self.building = building
|
|
|
|
|
|
- self.heat = 0
|
|
|
-
|
|
|
+ self.under_tower = False
|
|
|
+ self.depth = 0
|
|
|
+ self.pivot = False
|
|
|
+ self.needs_unit = False
|
|
|
+
|
|
|
+ self.facing = []
|
|
|
+ self.support = []
|
|
|
+ self.threat = 0
|
|
|
+ self.in_front_of = []
|
|
|
+
|
|
|
+ def update_threat(self):
|
|
|
+
|
|
|
+ self.threat = 0
|
|
|
+
|
|
|
+ for n in self.neighbors:
|
|
|
+ ncell = grid[n]
|
|
|
+ if ncell.active_owned:
|
|
|
+ self.support.append(ncell)
|
|
|
+ else:
|
|
|
+ ncell.in_front_of.append(self)
|
|
|
+ self.facing.append(ncell)
|
|
|
+ if ncell.opponents:
|
|
|
+ self.threat -= 1
|
|
|
+
|
|
|
+ if self.unit:
|
|
|
+ self.threat -= 2 * self.unit.level
|
|
|
+
|
|
|
+ for cell in self.support:
|
|
|
+ if cell.unit:
|
|
|
+ self.threat -= cell.unit.level
|
|
|
+
|
|
|
+ for cell in self.facing:
|
|
|
+ if cell.unit:
|
|
|
+ self.threat += 2 * cell.unit.level
|
|
|
+
|
|
|
@property
|
|
|
def movable(self):
|
|
|
return self._content != "#"
|
|
|
@@ -275,6 +361,10 @@ class Cell(Base):
|
|
|
def opponents(self):
|
|
|
return self._content.lower() == "x"
|
|
|
|
|
|
+ @property
|
|
|
+ def owner(self):
|
|
|
+ return ME if self.owned else OPPONENT
|
|
|
+
|
|
|
@property
|
|
|
def headquarter(self):
|
|
|
return self.pos in Grid.hqs
|
|
|
@@ -287,7 +377,26 @@ class Cell(Base):
|
|
|
def active(self):
|
|
|
return self._content.isupper()
|
|
|
|
|
|
+ @property
|
|
|
+ def active_owned(self):
|
|
|
+ return self._content == "O"
|
|
|
+
|
|
|
+ @property
|
|
|
+ def active_opponent(self):
|
|
|
+ return self._content == "X"
|
|
|
+
|
|
|
+ def owned_unit(self):
|
|
|
+ if self.unit and self.unit.owned:
|
|
|
+ return self.unit
|
|
|
|
|
|
+ def owned_building(self):
|
|
|
+ if self.building and self.building.owned:
|
|
|
+ return self.building
|
|
|
+
|
|
|
+ def take_possession(self):
|
|
|
+ self._content = "O"
|
|
|
+
|
|
|
+
|
|
|
class Grid(Base):
|
|
|
dim = 12
|
|
|
hqs = [(0,0), (11,11)]
|
|
|
@@ -298,11 +407,10 @@ class Grid(Base):
|
|
|
for pos, cell in self.cells.items():
|
|
|
cell.neighbors = [p for p in self.neighbors(*pos) if p in self.cells]
|
|
|
|
|
|
- self.units = {}
|
|
|
- self.buildings = {}
|
|
|
+ self.units = []
|
|
|
+ self.buildings = []
|
|
|
for m in mines_sites:
|
|
|
self.cells[(m.x, m.y)].mine_site = m
|
|
|
- self.reservation = []
|
|
|
|
|
|
def print_grid(self):
|
|
|
return "\n".join(["".join([c for c in row]) for row in self.grid])
|
|
|
@@ -319,17 +427,22 @@ class Grid(Base):
|
|
|
return self.cells[key]
|
|
|
|
|
|
def update(self, grid, buildings, units):
|
|
|
- self.buildings = {(b.x, b.y): b for b in buildings}
|
|
|
- self.units = {(u.x, u.y): u for u in units}
|
|
|
+ buildings_ix = {(b.x, b.y): b for b in buildings}
|
|
|
+ units_ix= {(u.x, u.y): u for u in units}
|
|
|
+
|
|
|
+ self.buildings = list(buildings)
|
|
|
+ self.units = list(units)
|
|
|
|
|
|
for y, row in enumerate(grid):
|
|
|
for x, c in enumerate(row):
|
|
|
self.cells[(x, y)].update(c,
|
|
|
- self.units.get((x, y), None),
|
|
|
- self.buildings.get((x, y), None))
|
|
|
+ units_ix.get((x, y), None),
|
|
|
+ buildings_ix.get((x, y), None))
|
|
|
|
|
|
- self.reservation = []
|
|
|
- self.update_heat_map()
|
|
|
+ self.update_tower_areas()
|
|
|
+ self.update_frontlines()
|
|
|
+ self.update_depth_map()
|
|
|
+ self.update_pivots()
|
|
|
|
|
|
@staticmethod
|
|
|
def manhattan(from_, to_):
|
|
|
@@ -346,55 +459,100 @@ class Grid(Base):
|
|
|
def get_hq(self, player):
|
|
|
return next((b for b in self.buildings if b.owner == player and b.hq))
|
|
|
|
|
|
- def update_heat_map(self):
|
|
|
+ def update_tower_areas(self):
|
|
|
+ for b in self.buildings:
|
|
|
+ if b.type_ == Building.TOWER:
|
|
|
+ self.cells[b.pos].under_tower = True
|
|
|
+ for n in self.cells[b.pos].neighbors:
|
|
|
+ self.cells[n].under_tower = True
|
|
|
+
|
|
|
+ def update_frontlines(self):
|
|
|
+ # update the current frontlines
|
|
|
+ self.frontline = []
|
|
|
+ self.frontex = []
|
|
|
|
|
|
- frontier = []
|
|
|
- for p, cell in self.cells.items():
|
|
|
- if not p in frontier and cell.owned and cell.active \
|
|
|
- and any(self.cells[c].movable and (not self.cells[c].owned or not self.cells[c].active) for c in cell.neighbors):
|
|
|
- cell.heat = 1
|
|
|
- frontier.append(p)
|
|
|
+ for cell in self.cells.values():
|
|
|
+ if cell.active_owned:
|
|
|
+ if any(self.cells[c].movable and not self.cells[c].active_owned
|
|
|
+ for c in cell.neighbors):
|
|
|
+ cell.update_threat()
|
|
|
+ self.frontline.append(cell)
|
|
|
+
|
|
|
|
|
|
- buffer = frontier
|
|
|
+
|
|
|
+
|
|
|
+ def update_depth_map(self):
|
|
|
+ buffer = [c.pos for c in self.frontline]
|
|
|
+ for p in buffer:
|
|
|
+ self.cells[p].depth = 1
|
|
|
+
|
|
|
next_buffer = []
|
|
|
while buffer:
|
|
|
for p in buffer:
|
|
|
for n in self.cells[p].neighbors:
|
|
|
- if self.cells[n].owned and self.cells[n].active and not self.cells[n].heat:
|
|
|
- self.cells[n].heat = self.cells[p].heat + 1
|
|
|
+ if self.cells[n].active_owned and not self.cells[n].depth:
|
|
|
+ self.cells[n].depth = self.cells[p].depth + 1
|
|
|
next_buffer.append(n)
|
|
|
|
|
|
buffer = list(next_buffer)
|
|
|
next_buffer = []
|
|
|
|
|
|
+ def update_pivot_for(self, player_id):
|
|
|
+# for cell in self.cells.values():
|
|
|
+# if cell.owner == player_id and \
|
|
|
+# len([n for n in cell.neighbors if self.cells[n].owner == player_id]) == 2:
|
|
|
+# cell.pivot = True
|
|
|
+ pass
|
|
|
+
|
|
|
+ def update_pivots(self):
|
|
|
+ self.update_pivot_for(ME)
|
|
|
+ self.update_pivot_for(OPPONENT)
|
|
|
+
|
|
|
def training_places(self):
|
|
|
owned, neighbors = {p for p, c in self.cells.items() if c.owned}, set()
|
|
|
for p in owned:
|
|
|
neighbors |= set(self.neighbors(*p))
|
|
|
- return (self.cells[p] for p in (owned | neighbors) if self.cells[p].movable and not self.cells[p].occupied and not p in self.reservation)
|
|
|
+ return (self.cells[p] for p in (owned | neighbors) if self.can_move(p))
|
|
|
|
|
|
def get_next_training(self, max_level=3):
|
|
|
q = InterestQueue()
|
|
|
for cell in self.training_places():
|
|
|
- q.put(Train(cell))
|
|
|
+ q.put(Train(cell, max_level))
|
|
|
if not q:
|
|
|
return None
|
|
|
+
|
|
|
action = q.get()
|
|
|
|
|
|
+ if max_level < 3:
|
|
|
+ while action.target.under_tower:
|
|
|
+ try:
|
|
|
+ action = q.get()
|
|
|
+ except IndexError:
|
|
|
+ return None
|
|
|
level = 1
|
|
|
for ennemy in opponent.units:
|
|
|
- if Grid.manhattan(action.target.pos, ennemy.pos) < 4:
|
|
|
+ if Grid.manhattan(action.target.pos, ennemy.pos) < 3:
|
|
|
level = min(ennemy.level + 1, max_level)
|
|
|
break
|
|
|
+
|
|
|
action.level = level
|
|
|
-
|
|
|
return action
|
|
|
|
|
|
+ def can_move(self, pos, level=1):
|
|
|
+ cell = self.cells[pos]
|
|
|
+ can_move = True
|
|
|
+
|
|
|
+ can_move &= cell.movable
|
|
|
+ can_move &= not cell.owned_unit()
|
|
|
+ can_move &= not cell.owned_building()
|
|
|
+ if level != 3:
|
|
|
+ can_move &= (cell.unit is None or cell.unit.level < level)
|
|
|
+ can_move &= not cell.under_tower
|
|
|
+ return can_move
|
|
|
+
|
|
|
def moving_zone(self, unit):
|
|
|
return (self.cells[p] for p in self.cells[unit.pos].neighbors
|
|
|
- if self.cells[p].movable and not p in self.reservation and
|
|
|
- (not self.cells[p].building or self.cells[p].building.opponents) and
|
|
|
- (not self.cells[p].unit or self.cells[p].unit.opponents))
|
|
|
+ if self.can_move(p, unit.level))
|
|
|
|
|
|
def get_next_move(self, unit):
|
|
|
q = InterestQueue()
|
|
|
@@ -402,11 +560,16 @@ class Grid(Base):
|
|
|
q.put(Move(cell, unit))
|
|
|
if not q:
|
|
|
return None
|
|
|
- return q.get()
|
|
|
+ log(q.items)
|
|
|
+ action = q.get()
|
|
|
+ if action.interest > Move(self.cells[unit.pos], unit).interest:
|
|
|
+ # move has lower interest than staying there
|
|
|
+ return None
|
|
|
+ return action
|
|
|
|
|
|
def building_zone(self, type_):
|
|
|
if type_ == Building.MINE:
|
|
|
- return [cell for cell in self.cells.values() if cell.mine_site and cell.heat > 3]
|
|
|
+ return [cell for cell in self.cells.values() if cell.mine_site and cell.depth > 3]
|
|
|
else:
|
|
|
return []
|
|
|
|
|
|
@@ -418,6 +581,76 @@ class Grid(Base):
|
|
|
return None
|
|
|
return q.get()
|
|
|
|
|
|
+ def apply(self, action):
|
|
|
+ if type(action) is Move:
|
|
|
+ unit, new_cell = action.unit, action.target
|
|
|
+ old_cell = self.cells[unit.pos]
|
|
|
+
|
|
|
+ if new_cell.unit:
|
|
|
+ if new_cell.unit.owned:
|
|
|
+ log(f"ERROR: impossible {action}")
|
|
|
+ return
|
|
|
+ if unit.level < 3 and new_cell.unit.level >= unit.level:
|
|
|
+ log(f"ERROR: impossible {action}")
|
|
|
+ return
|
|
|
+ # cell is occupied by an opponent's unit with an inferior level
|
|
|
+ opponent.units.remove(new_cell.unit)
|
|
|
+ self.units.remove(new_cell.unit)
|
|
|
+ new_cell.unit = None
|
|
|
+
|
|
|
+ if new_cell.building and new_cell.building.type_ == Building.TOWER:
|
|
|
+ if unit.level < 3:
|
|
|
+ log(f"ERROR: impossible {action}")
|
|
|
+ opponent.buildings.remove(new_cell.building)
|
|
|
+ self.buildings.remove(new_cell.building)
|
|
|
+ new_cell.building = None
|
|
|
+
|
|
|
+ old_cell.unit = None
|
|
|
+ unit.x, unit.y = new_cell.pos
|
|
|
+ new_cell.unit = unit
|
|
|
+
|
|
|
+ new_cell.take_possession()
|
|
|
+
|
|
|
+ elif type(action) is Train:
|
|
|
+ level, new_cell = action.level, action.target
|
|
|
+ unit = Unit(ME, None, level, *new_cell.pos)
|
|
|
+
|
|
|
+ if new_cell.unit:
|
|
|
+ if new_cell.unit.owned:
|
|
|
+ log(f"ERROR: impossible {action}")
|
|
|
+ return
|
|
|
+ if unit.level < 3 and new_cell.unit.level >= unit.level:
|
|
|
+ log(f"ERROR: impossible {action}")
|
|
|
+ return
|
|
|
+ # cell is occupied by an opponent's unit with an inferior level
|
|
|
+ opponent.units.remove(new_cell.unit)
|
|
|
+ self.units.remove(new_cell.unit)
|
|
|
+ new_cell.unit = None
|
|
|
+
|
|
|
+ if new_cell.building and new_cell.building.type_ == Building.TOWER:
|
|
|
+ if unit.level < 3:
|
|
|
+ log(f"ERROR: impossible {action}")
|
|
|
+ opponent.buildings.remove(new_cell.building)
|
|
|
+ self.buildings.remove(new_cell.building)
|
|
|
+ new_cell.building = None
|
|
|
+
|
|
|
+ new_cell.unit = unit
|
|
|
+ new_cell.take_possession()
|
|
|
+
|
|
|
+ elif type(action) is Build:
|
|
|
+ type_, new_cell = action.type_, action.target
|
|
|
+ building = Building(ME, type_, *new_cell.pos)
|
|
|
+ new_cell.building = building
|
|
|
+
|
|
|
+ if building.type_ == Building.TOWER:
|
|
|
+ new_cell.under_tower = True
|
|
|
+ for n in new_cell.neighbors:
|
|
|
+ self.cells[n].under_tower = True
|
|
|
+
|
|
|
+ else:
|
|
|
+ log(f"unknown action : {type(action)}")
|
|
|
+
|
|
|
+
|
|
|
|
|
|
# ******** MAIN *************
|
|
|
|
|
|
@@ -427,7 +660,6 @@ if test:
|
|
|
mines_input = []
|
|
|
else:
|
|
|
mines_input = [input() for _ in range(int(input()))]
|
|
|
-log(mines_input)
|
|
|
|
|
|
mines_sites = [MineSite(*[int(j) for j in item.split()]) for item in mines_input]
|
|
|
# log(f"* mines: {mines_sites}")
|
|
|
@@ -491,38 +723,55 @@ while True:
|
|
|
commands = []
|
|
|
|
|
|
# start
|
|
|
- log("# Moving")
|
|
|
- for unit in player.units:
|
|
|
- action = grid.get_next_move(unit)
|
|
|
- if action:
|
|
|
- grid.reservation.append(action.target.pos)
|
|
|
- commands.append(action.resolution())
|
|
|
+
|
|
|
+
|
|
|
+ has_move = []
|
|
|
+ for i in range(2):
|
|
|
+ log(f"# Moving (turn {i})")
|
|
|
+ for unit in player.units:
|
|
|
+ if unit in has_move:
|
|
|
+ continue
|
|
|
+
|
|
|
+ action = grid.get_next_move(unit)
|
|
|
+ if action:
|
|
|
+ grid.apply(action)
|
|
|
+ has_move.append(unit)
|
|
|
+ commands.append(action.resolution())
|
|
|
|
|
|
log("# Training")
|
|
|
spent, charges = 0, 0
|
|
|
- while (player.income - charges) > len(player.units) and (player.gold - spent) > 10:
|
|
|
- max_level = next((i for i in range(3, 0, -1) if (player.gold - spent) > Unit.training_cost[i] and (player.income - charges) > Unit.maintenance[i]), 1)
|
|
|
+
|
|
|
+ while player.income - charges > 0 and \
|
|
|
+ player.gold - spent > 10 and \
|
|
|
+ len(player.units) < len(grid.frontline):
|
|
|
+
|
|
|
+ max_level = next((i for i in range(3, 0, -1) if (player.gold - spent) > Unit.cost[i] and (player.income - charges) > Unit.maintenance[i]), 1)
|
|
|
+
|
|
|
action = grid.get_next_training(max_level)
|
|
|
if action:
|
|
|
- grid.reservation.append(action.target.pos)
|
|
|
- spent += Unit.training_cost[action.level]
|
|
|
+ grid.apply(action)
|
|
|
+ spent += Unit.cost[action.level]
|
|
|
charges += Unit.maintenance[action.level]
|
|
|
commands.append(action.resolution())
|
|
|
else:
|
|
|
break
|
|
|
|
|
|
log("# Building")
|
|
|
- mine_cost = 20 + 4 * len([b for b in player.buildings if b.type_ == Building.MINE])
|
|
|
+ mine_cost = Building.cost[Building.MINE] + 4 * len([b for b in player.buildings if b.type_ == Building.MINE])
|
|
|
if (player.gold - spent) > mine_cost:
|
|
|
action = grid.get_building_site(Building.MINE)
|
|
|
if action:
|
|
|
spent += mine_cost
|
|
|
+ grid.apply(action)
|
|
|
+ commands.append(action.resolution())
|
|
|
+
|
|
|
+ if player.gold - spent > Building.cost[Building.TOWER]:
|
|
|
+ action = grid.get_building_site(Building.TOWER)
|
|
|
+ if action:
|
|
|
+ spent += Building.cost[Building.TOWER]
|
|
|
+ grid.apply(action)
|
|
|
commands.append(action.resolution())
|
|
|
|
|
|
-# if player.gold - spent > 15:
|
|
|
-# action = grid.get_building_site(Building.TOWER)
|
|
|
-
|
|
|
-
|
|
|
if not commands:
|
|
|
log("nothing to do: wait")
|
|
|
commands = ["WAIT"]
|