|
|
@@ -6,13 +6,14 @@ debug = True
|
|
|
|
|
|
t0 = time.time()
|
|
|
|
|
|
+
|
|
|
# Debugger : il y a des mouvements qui ne se font pas, faut tout vérifier
|
|
|
# Faire des zones à l'intérieur des contigs, pour attirer le mouvement vers les zones à coloniser
|
|
|
# Limiter les constructions agressives en fonction de ces zones
|
|
|
# penser les recycleurs comme des obstacles!
|
|
|
# Identifier le moment où la situation est "verrouillée" (plus d'accès entre alliés et ennemis)
|
|
|
# et cesser les construction de recycleurs
|
|
|
-
|
|
|
+# Limiter la consommation de ressources des mouvements
|
|
|
|
|
|
def log(*msg):
|
|
|
if debug:
|
|
|
@@ -65,11 +66,17 @@ class Player(BaseClass):
|
|
|
OPPONENT = 0
|
|
|
NONE = -1
|
|
|
|
|
|
+ SIDE_WEST = -1
|
|
|
+ SIDE_EAST = 1
|
|
|
+
|
|
|
def __init__(self, id_):
|
|
|
self.id = id_
|
|
|
self.matter = 0
|
|
|
self.territory = 0
|
|
|
|
|
|
+ self.side = None
|
|
|
+
|
|
|
+
|
|
|
class Cell(BaseClass):
|
|
|
def __init__(self, x, y):
|
|
|
self.x = x
|
|
|
@@ -154,6 +161,7 @@ class RobotGroup(BaseClass):
|
|
|
def has_played(self):
|
|
|
return self.amount_played >= self.initial_amount
|
|
|
|
|
|
+
|
|
|
class Recycler(BaseClass):
|
|
|
COST = 10
|
|
|
|
|
|
@@ -285,6 +293,7 @@ class Grid(BaseClass):
|
|
|
self._distance_cache = {}
|
|
|
self.index_contigs = {}
|
|
|
self.index_tensions = {}
|
|
|
+ self.index_threats = {}
|
|
|
self.index_nearest_enemy = {}
|
|
|
|
|
|
@property
|
|
|
@@ -314,7 +323,7 @@ class Grid(BaseClass):
|
|
|
n = [(x, y - 1), (x - 1, y), (x + 1, y), (x, y + 1)]
|
|
|
if diags:
|
|
|
n += [(x - 1, y - 1), (x + 1, y - 1), (x - 1, y + 1), (x + 1, y + 1)]
|
|
|
-
|
|
|
+
|
|
|
neighbors = [
|
|
|
(x, y)
|
|
|
for x, y in n
|
|
|
@@ -331,6 +340,7 @@ class Grid(BaseClass):
|
|
|
return Grid(w, h, me, opponent)
|
|
|
|
|
|
def update(self):
|
|
|
+ self.round += 1
|
|
|
my_matter, opp_matter = [int(i) for i in input().split()]
|
|
|
|
|
|
self.me.matter = my_matter
|
|
|
@@ -343,6 +353,11 @@ class Grid(BaseClass):
|
|
|
self.cells[(x, y)].update(scrap_amount, owner, units, recycler, can_build, can_spawn,
|
|
|
in_range_of_recycler)
|
|
|
|
|
|
+ if owner == Player.ME and self.me.side is None:
|
|
|
+ self.me.side = Player.SIDE_WEST if x <= (self.width // 2) else Player.SIDE_EAST
|
|
|
+ if owner == Player.OPPONENT and self.opponent.side is None:
|
|
|
+ self.opponent.side = Player.SIDE_WEST if x <= (self.width // 2) else Player.SIDE_EAST
|
|
|
+
|
|
|
log('update')
|
|
|
|
|
|
# update robots
|
|
|
@@ -363,6 +378,7 @@ class Grid(BaseClass):
|
|
|
self.update_possessions()
|
|
|
self.update_contigs()
|
|
|
self.update_tension_map()
|
|
|
+ self.update_threat_map()
|
|
|
self.update_nearest_enemy()
|
|
|
|
|
|
def owned_units(self):
|
|
|
@@ -380,6 +396,9 @@ class Grid(BaseClass):
|
|
|
def tension(self, cell):
|
|
|
return self.index_tensions.get(cell.pos, 0)
|
|
|
|
|
|
+ def threat(self, cell):
|
|
|
+ return self.index_threats.get(cell.pos, 0)
|
|
|
+
|
|
|
def current_winner(self):
|
|
|
if self.me.territory > self.opponent.territory:
|
|
|
return Player.ME
|
|
|
@@ -464,6 +483,17 @@ class Grid(BaseClass):
|
|
|
tension = k * c.units
|
|
|
self.index_tensions[c.pos] = self.index_tensions.get(c.pos, 0) + tension
|
|
|
|
|
|
+ def update_threat_map(self):
|
|
|
+ self.index_threats = {}
|
|
|
+
|
|
|
+ for c in self.cells.values():
|
|
|
+ if not c.is_opponents or not c.units:
|
|
|
+ continue
|
|
|
+
|
|
|
+ threat = c.units
|
|
|
+ for n in self.neighbors(*c.pos):
|
|
|
+ self.index_threats[n] = self.index_threats.get(n, 0) + threat
|
|
|
+
|
|
|
def update_nearest_enemy(self):
|
|
|
self.index_nearest_enemy = {pos: 0 for pos in self.cells}
|
|
|
|
|
|
@@ -554,6 +584,7 @@ class Grid(BaseClass):
|
|
|
k_aggressive_build = 8000
|
|
|
k_aggressive_build_for_defense = -6000
|
|
|
k_aggressive_build_tension = -1000
|
|
|
+ k_first_rounds = 5000 # on décourage le build lors des premiers rounds, il bloque l'avancée
|
|
|
|
|
|
k0_recyclers_owned = k_recyclers_owned * len(self.owned_recyclers())
|
|
|
|
|
|
@@ -569,6 +600,9 @@ class Grid(BaseClass):
|
|
|
|
|
|
k = k0
|
|
|
|
|
|
+ if self.round <= 3:
|
|
|
+ k += (4 - self.round) * k_first_rounds
|
|
|
+
|
|
|
area = [place.pos] + self.neighbors(*place.pos)
|
|
|
destination = Build.SUPPORT if not any(self.cells[p].is_opponents for p in area) else Build.AGGRESSIVE
|
|
|
k0_already_exploited = 0
|
|
|
@@ -616,12 +650,12 @@ class Grid(BaseClass):
|
|
|
k_opportunities = -1000
|
|
|
k_conquest = -3000
|
|
|
k_tension = -1000
|
|
|
+ k_tension_bridgehead = -1000
|
|
|
+ k_tension_around = -300
|
|
|
+ k_tension_around_bridgehead = -300
|
|
|
k_dist_to_enemy = 400
|
|
|
k_bridgehead = -6000
|
|
|
k_reinforcement = 500
|
|
|
-
|
|
|
- amount_default = 1
|
|
|
- amount_bridgehead = 3
|
|
|
ki = 0 # little hack to avoid the while in queue.put
|
|
|
|
|
|
for contig in self.contigs:
|
|
|
@@ -635,7 +669,6 @@ class Grid(BaseClass):
|
|
|
|
|
|
for p in contig.area:
|
|
|
place = self.cells[p]
|
|
|
- amount = amount_default
|
|
|
|
|
|
if not place.owned or not place.is_movable or place.unmovable_next_round:
|
|
|
continue
|
|
|
@@ -643,10 +676,12 @@ class Grid(BaseClass):
|
|
|
k = k0 + ki
|
|
|
ki += 1
|
|
|
|
|
|
+ is_bridgehead = sum(self.cells[n].is_opponents for n in self.neighbors(p[0], p[1], True)) > 5
|
|
|
+
|
|
|
tension = self.index_tensions.get(place.pos, 0)
|
|
|
for pos in self.neighbors(place.pos[0], place.pos[1], True):
|
|
|
tension += self.index_tensions.get(pos, 0)
|
|
|
- k += k_tension * tension
|
|
|
+ k += k_tension * tension + k_tension_bridgehead * is_bridgehead
|
|
|
|
|
|
for pos in self.neighbors(*place.pos):
|
|
|
n = self.cells[pos]
|
|
|
@@ -658,11 +693,9 @@ class Grid(BaseClass):
|
|
|
elif n.is_opponents:
|
|
|
k += k_conquest
|
|
|
|
|
|
- k += k_dist_to_enemy * self.index_nearest_enemy[p]
|
|
|
+ k += k_tension_around * self.tension(n) + k_tension_around_bridgehead * is_bridgehead
|
|
|
|
|
|
- if sum(self.cells[n].is_opponents for n in self.neighbors(p[0], p[1], True)) > 5:
|
|
|
- k += k_bridgehead
|
|
|
- amount = amount_bridgehead
|
|
|
+ k += k_dist_to_enemy * self.index_nearest_enemy[p]
|
|
|
|
|
|
amount = max(sum([self.index_tensions.get(n, 0) for n in self.neighbors(*p)]), 1)
|
|
|
|
|
|
@@ -674,7 +707,8 @@ class Grid(BaseClass):
|
|
|
build_actions.put(k, action)
|
|
|
k += k_reinforcement
|
|
|
|
|
|
- # for action in build_actions.items[:8]:
|
|
|
+ # log('---')
|
|
|
+ # for action in sorted([a for a in build_actions.values() if type(a) is Spawn], key=lambda x: x.priority)[:10]:
|
|
|
# log(action)
|
|
|
|
|
|
if not build_actions:
|
|
|
@@ -692,6 +726,8 @@ class Grid(BaseClass):
|
|
|
for pos in area:
|
|
|
self.cells[pos].in_range_of_recycler = True
|
|
|
|
|
|
+ self.update_contigs()
|
|
|
+
|
|
|
if type(action) is Spawn:
|
|
|
place.units += action.amount
|
|
|
|
|
|
@@ -712,11 +748,14 @@ class Grid(BaseClass):
|
|
|
move_actions = []
|
|
|
|
|
|
k0_position = 60000
|
|
|
- k_position_distance = 2000
|
|
|
+ k_position_distance = 3000
|
|
|
k_position_opponents = -5000
|
|
|
k_position_destroy = -3000
|
|
|
k_dist_to_enemy = 1000
|
|
|
+ k_colonize = -3000
|
|
|
k_expand = -800
|
|
|
+ k_threat = -1500
|
|
|
+ ki = 0 # little hack to avoid the while in queue.put
|
|
|
|
|
|
for contig in self.contigs:
|
|
|
orders = []
|
|
|
@@ -726,39 +765,49 @@ class Grid(BaseClass):
|
|
|
# nothing to do
|
|
|
continue
|
|
|
|
|
|
- # list destinations
|
|
|
+ # on ne conserve que les cases voisines ou limitrophes du territoire
|
|
|
+ move_area = []
|
|
|
for pos in contig.area:
|
|
|
+ owned_neighbor, not_owned_neighbor = False, False
|
|
|
+ for n in self.neighbors(*pos):
|
|
|
+ owned = self.cells[n].owned
|
|
|
+ if owned:
|
|
|
+ owned_neighbor = True
|
|
|
+ else:
|
|
|
+ not_owned_neighbor = True
|
|
|
+
|
|
|
+ if owned_neighbor and not_owned_neighbor:
|
|
|
+ move_area.append(pos)
|
|
|
+ break
|
|
|
+
|
|
|
+ # list destinations
|
|
|
+ for pos in move_area:
|
|
|
c = self.cells[pos]
|
|
|
- priority = k0_position
|
|
|
+ k = k0_position + ki
|
|
|
+ ki += 1
|
|
|
|
|
|
if c.owned or not c.is_movable:
|
|
|
continue
|
|
|
|
|
|
- if not any((self.cells[n].owned and not self.cells[n].recycler) for n in self.neighbors(*pos)):
|
|
|
- # on ne conserve que les cases voisines du territoire allié
|
|
|
- continue
|
|
|
-
|
|
|
amount_needed = 1
|
|
|
|
|
|
if c.is_opponents:
|
|
|
- priority += k_position_opponents
|
|
|
+ k += k_position_opponents
|
|
|
if c.units:
|
|
|
- priority += k_position_destroy
|
|
|
+ k += k_position_destroy
|
|
|
amount_needed = c.amount
|
|
|
|
|
|
- priority += k_dist_to_enemy * self.index_nearest_enemy[pos]
|
|
|
+ k += k_dist_to_enemy * self.index_nearest_enemy[pos]
|
|
|
+ k += k_threat * self.threat(c)
|
|
|
|
|
|
for n in self.neighbors(pos[0], pos[1], True):
|
|
|
if not self.cells[n].is_grass and not self.cells[n].owned:
|
|
|
- priority += k_expand
|
|
|
+ k += k_expand
|
|
|
|
|
|
- order = MoveOrder(pos, amount_needed, priority)
|
|
|
+ order = MoveOrder(pos, amount_needed, k)
|
|
|
|
|
|
orders.append(order)
|
|
|
|
|
|
- # for move_order in orders:
|
|
|
- # log(move_order)
|
|
|
-
|
|
|
# Prioritize units per distance
|
|
|
for pos in contig.area:
|
|
|
if pos in self.units:
|
|
|
@@ -776,9 +825,16 @@ class Grid(BaseClass):
|
|
|
priority = order.priority
|
|
|
priority += k_position_distance * dist
|
|
|
|
|
|
+ if self.me.side == Player.SIDE_WEST and order.pos[0] >= unit.pos[0] \
|
|
|
+ or self.me.side == Player.SIDE_EAST and order.pos[0] <= unit.pos[0]:
|
|
|
+ priority += k_colonize
|
|
|
+
|
|
|
candidate = MoveOrderCandidate(order, unit, destination)
|
|
|
q.put(priority, candidate)
|
|
|
|
|
|
+ for priority, candidate in sorted(q.items, key=lambda x: x[0])[:18]:
|
|
|
+ log(f"{candidate.unit.pos} -> {candidate.order.pos}, p:{priority}, a:{candidate.order.amount}")
|
|
|
+
|
|
|
# affect orders
|
|
|
while q:
|
|
|
k, candidate = q.get_items()
|
|
|
@@ -792,6 +848,10 @@ class Grid(BaseClass):
|
|
|
|
|
|
from_, to_ = candidate.unit.pos, candidate.destination
|
|
|
|
|
|
+ if from_ == to_:
|
|
|
+ # already there
|
|
|
+ continue
|
|
|
+
|
|
|
action = Move(from_, to_, amount, k)
|
|
|
|
|
|
candidate.unit.amount_played += amount
|
|
|
@@ -853,6 +913,6 @@ while True:
|
|
|
|
|
|
# for contig in grid.contigs:
|
|
|
# log(contig)
|
|
|
- # grid.print(lambda x: f"{grid.tension(x):02d}")
|
|
|
+ grid.print(lambda x: f"{grid.threat(x):02d}")
|
|
|
|
|
|
grid.act()
|