Forráskód Böngészése

add buildings and better training loop

olinox 6 éve
szülő
commit
572b8f8cdc
1 módosított fájl, 151 hozzáadás és 48 törlés
  1. 151 48
      i_n_f/script.py

+ 151 - 48
i_n_f/script.py

@@ -6,6 +6,15 @@ import heapq
 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)
+
 debug = True
 t0 = time.time()
 
@@ -61,7 +70,8 @@ class Action(Base):
         self.interest = 0
         self.target = target
         self.x, self.y = target.pos
-        self.eval(target, *args, **kwargs)
+        
+        self.eval()
         
     def __lt__(self, other):
         return self.interest < other.interest
@@ -70,54 +80,98 @@ class Action(Base):
         raise NotImplementedError
         
 class Move(Action):
-    def eval(self, target, unit):
+    def __init__(self, target, unit):
+        self.unit = unit
+        super().__init__(target, unit)
+        
+    def eval(self):
         # the lower the better
+        
         self.interest = 0
-        if target.owned and target.active:
+        if self.target.owned and self.target.active:
             self.interest += 15 # already owned and active
             
-        elif target.opponents and target.active:
+        elif self.target.opponents and self.target.active:
             self.interest -= 15 # owned by opponents and active
             
         # non-passable cells around
-        self.interest += 3 * len([n for n in target.neighbors if not grid[n].movable])
+        self.interest += 3 * len([n for n in self.target.neighbors if not grid[n].movable])
         
         # an ennemy here
-        if target.unit and target.unit.opponents:
-            self.interest -= 20
+        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
-        if any((grid[n].unit and grid[n].unit.opponents) for n in target.neighbors):
-            self.interest += 15
+        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
         
         # priorize adjacent cells 
-        self.interest -= (2 * len([n for n in target.neighbors if grid[n].owned]))
+        self.interest -= (2 * len([n for n in self.target.neighbors if grid[n].owned]))
         
         # include 'heatmap'
-        self.interest += 3 * target.heat
+        self.interest += 3 * self.target.heat
+        
+        # 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
+            
+        
+    def resolution(self):
+        return f"MOVE {self.unit.id_} {self.target.x} {self.target.y}"
         
 class Train(Move):
-    def eval(self, target):
+    def __init__(self, target, level=1):
+        self.level = level
+        super().__init__(target, level)
+    
+    def eval(self):
         # the lower the better
         self.interest = 0
-        if target.owned and target.active:
+        if self.target.owned and self.target.active:
             self.interest += 15 # already owned and active
             
-        elif target.opponents and target.active:
+        elif self.target.opponents and self.target.active:
             self.interest -= 15 # owned by opponents and active
             
         # non-passable cells around
-        self.interest += 3 * len([n for n in target.neighbors if not grid[n].movable])
-        
-        # an ennemy nearby
-        if any((grid[n].unit and grid[n].unit.opponents) for n in target.neighbors):
-            self.interest += 15
+        self.interest += 2 * len([n for n in self.target.neighbors if not grid[n].movable])
         
         # priorize adjacent cells 
-        self.interest -= (2 * len([n for n in target.neighbors if grid[n].owned]))
+        self.interest -= (2 * len([n for n in self.target.neighbors if grid[n].owned]))
         
         # include 'heatmap'
-        self.interest += 3 * target.heat
+        self.interest += 3 * self.target.heat
+        
+        # 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
+        
+    def resolution(self):
+        return f"TRAIN {self.level} {self.target.x} {self.target.y}"
+        
+class Build(Action):
+    def __init__(self, target, type_):
+        self.type_ = type_
+        super().__init__(target, type_)
+    
+    def eval(self):
+        # the lower the better
+        self.interest = 0
+        self.interest -= self.target.heat
+        
+    def resolution(self):
+        str_type = {1: "MINE", 2: "TOWER"}[self.type_]
+        return f"BUILD {str_type} {self.target.x} {self.target.y}"
         
 class BaseLoc(Base):
     def __init__(self, x, y):
@@ -128,7 +182,7 @@ class BaseLoc(Base):
     def pos(self):
         return self.x, self.y
 
-class Mine(BaseLoc):
+class MineSite(BaseLoc):
     def __init__(self, x, y):
         super().__init__(x, y)
 
@@ -147,6 +201,8 @@ class BaseOwnedLoc(BaseLoc):
 
 class Building(BaseOwnedLoc):
     HQ = 0
+    MINE = 1
+    TOWER = 2
     
     def __init__(self, owner, type_, x, y):
         super().__init__(x, y, owner)
@@ -157,6 +213,8 @@ class Building(BaseOwnedLoc):
         return self.type_ == Building.HQ
 
 class Unit(BaseOwnedLoc):
+    training_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)
         self.id_ = id_
@@ -186,6 +244,7 @@ class Cell(Base):
         self.neighbors = []
         self.unit = None
         self.building = None
+        self.mine_site = None
         
         self.heat = 0
         
@@ -233,7 +292,7 @@ class Grid(Base):
     dim = 12
     hqs = [(0,0), (11,11)]
     
-    def __init__(self, mines = []):
+    def __init__(self, mines_sites = []):
         
         self.cells = {(x, y): Cell(x, y) for x in range(Grid.dim) for y in range(Grid.dim)}
         for pos, cell in self.cells.items():
@@ -241,7 +300,9 @@ class Grid(Base):
             
         self.units = {}
         self.buildings = {}
-        self.mines = {(m.x, m.y): m for m in mines}
+        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])
@@ -267,6 +328,7 @@ class Grid(Base):
                                           self.units.get((x, y), None), 
                                           self.buildings.get((x, y), None))
 
+        self.reservation = []
         self.update_heat_map()
 
     @staticmethod
@@ -309,19 +371,28 @@ class Grid(Base):
         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 not self.cells[p].occupied)
+        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)
     
-    def get_next_training(self):
+    def get_next_training(self, max_level=3):
         q = InterestQueue()
         for cell in self.training_places():
             q.put(Train(cell))
         if not q:
             return None
-        return q.get()
+        action = q.get()
+    
+        level = 1
+        for ennemy in opponent.units:
+            if Grid.manhattan(action.target.pos, ennemy.pos) < 4:
+                level = min(ennemy.level + 1, max_level)
+                break
+        action.level = level
+        
+        return action
     
     def moving_zone(self, unit):
         return (self.cells[p] for p in self.cells[unit.pos].neighbors 
-                if self.cells[p].movable and
+                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))
         
@@ -332,6 +403,20 @@ class Grid(Base):
         if not q:
             return None
         return q.get()
+    
+    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]
+        else:
+            return []
+
+    def get_building_site(self, type_):
+        q = InterestQueue()
+        for cell in self.building_zone(type_):
+            q.put(Build(cell, type_))
+        if not q:
+            return None
+        return q.get()
 
 
 # ******** MAIN *************
@@ -344,18 +429,13 @@ else:
     mines_input = [input() for _ in range(int(input()))]
 log(mines_input)
 
-mines = [Mine(*[int(j) for j in item.split()]) for item in mines_input]
-log(f"* mines: {mines}")
+mines_sites = [MineSite(*[int(j) for j in item.split()]) for item in mines_input]
+# log(f"* mines: {mines_sites}")
 
-grid = Grid()
+grid = Grid(mines_sites)
 player = Player(ME)
 opponent = Player(OPPONENT)
 
-def cmd_train(target, level=1):
-    return f"TRAIN {level} {target.x} {target.y}"
-
-def cmd_move(unit, target):
-    return f"MOVE {unit.id_} {target.x} {target.y}"
 
 def cmd_wait():
     return "WAIT"
@@ -391,10 +471,10 @@ while True:
     new_grid = [list(row) for row in new_grid_input]
     
     buildings = [Building(*[int(j) for j in item.split()]) for item in buildings_input]
-    log(f"* buildings: {buildings}")    
+#     log(f"* buildings: {buildings}")    
     
     units = [Unit(*[int(j) for j in item.split()]) for item in units_input]
-    log(f"* units: {units}")   
+#     log(f"* units: {units}")   
     # --->
     
     # <--- update
@@ -402,27 +482,50 @@ while True:
 #     log(f"grid:\n{grid.print_grid()}")
     
     player.update(gold, income, units, buildings)
-    log(f"player: {player}")
+#     log(f"player: {player}")
     
     opponent.update(opponent_gold, opponent_income, units, buildings)
-    log(f"opponent: {opponent}")
+#     log(f"opponent: {opponent}")
     # --->
     
     commands = []
     
     # start
-    if not player.units or (player.gold > 20 and player.income > (5 * len(player.units))):
-        target = grid.get_next_training()
-        if target:
-            commands.append(cmd_train(target))
-    
+    log("# Moving")
     for unit in player.units:
-        target = grid.get_next_move(unit)
-        if target:
-            commands.append(cmd_move(unit, target))
+        action = grid.get_next_move(unit)
+        if action:
+            grid.reservation.append(action.target.pos)
+            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)
+        action = grid.get_next_training(max_level)
+        if action:
+            grid.reservation.append(action.target.pos)
+            spent += Unit.training_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])
+    if (player.gold - spent) > mine_cost:
+        action = grid.get_building_site(Building.MINE)
+        if action:
+            spent += mine_cost
+            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"]
+        
     log(commands)
     print(";".join(commands))