|
|
@@ -7,7 +7,6 @@ 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!
|
|
|
@@ -17,7 +16,6 @@ t0 = time.time()
|
|
|
# si la tension autour d'une case est trop forte et que cette case est un "noeud" question passage,
|
|
|
# ne pas bouger et défendre!
|
|
|
|
|
|
-
|
|
|
def log(*msg):
|
|
|
if debug:
|
|
|
print("{} - ".format(str(time.time() - t0)[1:5]), *msg, file=sys.stderr, flush=True)
|
|
|
@@ -120,7 +118,7 @@ class Cell(BaseClass):
|
|
|
|
|
|
@property
|
|
|
def lifetime(self):
|
|
|
- return 10000 if not self.in_range_of_recycler else self.amount
|
|
|
+ return 10000000 if not self.in_range_of_recycler else self.amount
|
|
|
|
|
|
@property
|
|
|
def unmovable_next_round(self):
|
|
|
@@ -190,6 +188,14 @@ class Recycler(BaseClass):
|
|
|
f"{self.immediately_available_amount}, {self.total_available_amount}, {self.lifetime()}>"
|
|
|
|
|
|
|
|
|
+class Block(BaseClass):
|
|
|
+ def __init__(self, id_, start):
|
|
|
+ self.id = id_
|
|
|
+ self.start = start
|
|
|
+ self.area = [start]
|
|
|
+ self.owner = Player.NONE
|
|
|
+
|
|
|
+
|
|
|
class Contig(BaseClass):
|
|
|
UNKNOWN = 'U'
|
|
|
OWNED = 'O'
|
|
|
@@ -197,11 +203,13 @@ class Contig(BaseClass):
|
|
|
CONFLICTUAL = 'C'
|
|
|
NOT_OWNED = 'N'
|
|
|
|
|
|
- def __init__(self, start):
|
|
|
+ def __init__(self, id_, start):
|
|
|
+ self.id = id_
|
|
|
self.start = start
|
|
|
self.area = [start]
|
|
|
self.status = Contig.UNKNOWN
|
|
|
self.has_robots = False
|
|
|
+ self.blocks = []
|
|
|
|
|
|
def __repr__(self):
|
|
|
return f"<Contig(start: {self.start}, size: {len(self.area)}, status: {self.status}, robots: {self.has_robots})>"
|
|
|
@@ -221,10 +229,11 @@ class MoveOrder(BaseClass):
|
|
|
|
|
|
|
|
|
class MoveOrderCandidate(BaseClass):
|
|
|
- def __init__(self, order, unit, destination):
|
|
|
+ def __init__(self, order, unit, destination, next_pos=None):
|
|
|
self.order = order
|
|
|
self.unit = unit
|
|
|
self.destination = destination
|
|
|
+ self.next_pos = next_pos
|
|
|
|
|
|
|
|
|
class Action(BaseClass):
|
|
|
@@ -295,6 +304,7 @@ class Grid(BaseClass):
|
|
|
self._neighbors_cache = {}
|
|
|
self._distance_cache = {}
|
|
|
self.index_contigs = {}
|
|
|
+ self.index_blocks = {}
|
|
|
self.index_tensions = {}
|
|
|
self.index_threats = {}
|
|
|
self.index_nearest_enemy = {}
|
|
|
@@ -322,8 +332,8 @@ class Grid(BaseClass):
|
|
|
return dist
|
|
|
|
|
|
def neighbors(self, x, y, diags=False):
|
|
|
- # if (x, y, diags) in self._neighbors_cache:
|
|
|
- # return self._neighbors_cache[(x, y, diags)]
|
|
|
+ if (x, y, diags) in self._neighbors_cache:
|
|
|
+ return self._neighbors_cache[(x, y, diags)]
|
|
|
|
|
|
n = [(x, y - 1), (x - 1, y), (x + 1, y), (x, y + 1)]
|
|
|
if diags:
|
|
|
@@ -351,6 +361,8 @@ class Grid(BaseClass):
|
|
|
self.me.matter = my_matter
|
|
|
self.opponent.matter = opp_matter
|
|
|
|
|
|
+ self.path_cache = {}
|
|
|
+
|
|
|
for y in range(self.height):
|
|
|
for x in range(self.width):
|
|
|
scrap_amount, owner, units, recycler, can_build, can_spawn, in_range_of_recycler = [int(k) for k in
|
|
|
@@ -382,6 +394,7 @@ class Grid(BaseClass):
|
|
|
|
|
|
self.update_possessions()
|
|
|
self.update_contigs()
|
|
|
+ self.update_blocks()
|
|
|
self.update_tension_map()
|
|
|
self.update_threat_map()
|
|
|
self.update_nearest_enemy()
|
|
|
@@ -428,12 +441,14 @@ class Grid(BaseClass):
|
|
|
self.index_contigs = {}
|
|
|
|
|
|
seen = []
|
|
|
+ id_ = 0
|
|
|
# build contigs
|
|
|
for c in self.cells.values():
|
|
|
if c.pos in seen or c.is_grass:
|
|
|
continue
|
|
|
|
|
|
- contig = Contig(c.pos)
|
|
|
+ id_ += 1
|
|
|
+ contig = Contig(id_, c.pos)
|
|
|
|
|
|
candidates = self.neighbors(*c.pos)
|
|
|
|
|
|
@@ -477,6 +492,35 @@ class Grid(BaseClass):
|
|
|
else:
|
|
|
contig.status = Contig.NOT_OWNED
|
|
|
|
|
|
+ def update_blocks(self):
|
|
|
+ id_ = 0
|
|
|
+
|
|
|
+ for contig in self.contigs:
|
|
|
+ seen = []
|
|
|
+
|
|
|
+ for pos in contig.area:
|
|
|
+ if pos in seen:
|
|
|
+ continue
|
|
|
+
|
|
|
+ buffer = {pos}
|
|
|
+ id_ += 1
|
|
|
+ block = Block(id_, pos)
|
|
|
+ block.owner = self.cells[pos].owner
|
|
|
+ seen.append(pos)
|
|
|
+
|
|
|
+ while buffer:
|
|
|
+ p = buffer.pop()
|
|
|
+ for candidate in self.neighbors(*p):
|
|
|
+ if not candidate in block.area and self.cells[candidate].owner == block.owner:
|
|
|
+ buffer.add(candidate)
|
|
|
+ block.area.append(candidate)
|
|
|
+ seen.append(candidate)
|
|
|
+
|
|
|
+ contig.blocks.append(block)
|
|
|
+
|
|
|
+ for p in block.area:
|
|
|
+ self.index_blocks[p] = block
|
|
|
+
|
|
|
def update_tension_map(self):
|
|
|
self.index_tensions = {}
|
|
|
|
|
|
@@ -525,8 +569,11 @@ class Grid(BaseClass):
|
|
|
self.index_nearest_enemy[c.pos] = nearest[1]
|
|
|
|
|
|
def path(self, start, target):
|
|
|
+ if (start, target) in self.path_cache:
|
|
|
+ return self.path_cache[(start, target)]
|
|
|
+
|
|
|
nodes = Queue()
|
|
|
- its, break_on = 0, 500
|
|
|
+ its, break_on = 0, 150
|
|
|
|
|
|
origin = PathNode(*start)
|
|
|
nodes.put(0, origin)
|
|
|
@@ -757,9 +804,9 @@ class Grid(BaseClass):
|
|
|
k_position_distance = 3000
|
|
|
k_position_opponents = -5000
|
|
|
k_position_destroy = -3000
|
|
|
- k_dist_to_enemy = 1000
|
|
|
- k_colonize = -5000
|
|
|
- k_consolidate = -2000
|
|
|
+ k_dist_to_enemy = 2000
|
|
|
+ k_colonize = -100 * self.width
|
|
|
+ k_consolidate = -50 * self.width
|
|
|
k_expand = -600
|
|
|
k_threat = -1500
|
|
|
ki = 0 # little hack to avoid the while in queue.put
|
|
|
@@ -817,12 +864,33 @@ class Grid(BaseClass):
|
|
|
continue
|
|
|
units.append(unit)
|
|
|
|
|
|
+ # limit pathfinding to the priority orders
|
|
|
+ pathfinding_limit = 4
|
|
|
+ pathfinding_dist_limit = 5
|
|
|
+
|
|
|
q = Queue()
|
|
|
for unit in units:
|
|
|
+ if not unit.initial_amount:
|
|
|
+ continue
|
|
|
+
|
|
|
+ pathfinding_it = 0
|
|
|
+
|
|
|
for order in orders:
|
|
|
destination = order.pos
|
|
|
+
|
|
|
+ next_pos = None
|
|
|
dist = self.manhattan(unit.pos, destination)
|
|
|
|
|
|
+ if dist == 1:
|
|
|
+ next_pos = order.pos
|
|
|
+
|
|
|
+ elif pathfinding_it < pathfinding_limit and dist <= pathfinding_dist_limit:
|
|
|
+ pathfinding_it += 1
|
|
|
+ path = self.path(unit.pos, order.pos)
|
|
|
+ if path is not None:
|
|
|
+ dist = len(path)
|
|
|
+ next_pos = path[0]
|
|
|
+
|
|
|
priority = order.priority
|
|
|
priority += k_position_distance * dist
|
|
|
|
|
|
@@ -837,7 +905,7 @@ class Grid(BaseClass):
|
|
|
or self.me.side == Player.SIDE_EAST and order.pos[0] == unit.pos[0]:
|
|
|
priority += k_consolidate
|
|
|
|
|
|
- candidate = MoveOrderCandidate(order, unit, destination)
|
|
|
+ candidate = MoveOrderCandidate(order, unit, destination, next_pos)
|
|
|
|
|
|
q.put(priority, candidate)
|
|
|
|
|
|
@@ -914,6 +982,15 @@ class Grid(BaseClass):
|
|
|
|
|
|
print(";".join([a.do() for a in actions]))
|
|
|
|
|
|
+ @staticmethod
|
|
|
+ def debug_print_block(cell):
|
|
|
+ if cell.pos in grid.index_contigs and grid.index_contigs[cell.pos]:
|
|
|
+ contig_id = f"{grid.index_contigs[cell.pos].id:02d}"
|
|
|
+ block_id = f"{grid.index_blocks[cell.pos].id:02d}" if contig_id != 'xx' else 'xx'
|
|
|
+ return f"{contig_id}.{block_id}"
|
|
|
+ else:
|
|
|
+ return 'xx.xx'
|
|
|
+
|
|
|
|
|
|
grid = Grid.create()
|
|
|
|
|
|
@@ -922,6 +999,6 @@ while True:
|
|
|
log(f"round {grid.round}")
|
|
|
# for contig in grid.contigs:
|
|
|
# log(contig)
|
|
|
- # grid.print(lambda x: f"{grid.threat(x):02d}")
|
|
|
+ grid.print(grid.debug_print_block)
|
|
|
|
|
|
grid.act()
|