Commits

Peter Ward committed b469aa4

add moar bots

  • Participants
  • Parent commits cfea483

Comments (0)

Files changed (2)

             if random.random() < self.variance:
                 result = random.choice('ULDR')
             elif predecessors[y][x]:
-                result = predecessors[y][x]
+                result = random.choice(predecessors[y][x])
             elif state.allegiances.get((x, y)) != whoami:
                 result = 'P'
             else:
+# Strategiser
+# Goals
+
+from collections import defaultdict
+from functools import lru_cache
+from random import choice
+
+import robots
+
+from robots.algorithms import distance_relaxer
+from robots.constants import City
+from robots.utils import add_spawns
+
+def get_close_spawns(whoami, state, threshold):
+    distances = []
+    for y, row in enumerate(state.cities):
+        distances.append([])
+        for x, cell in enumerate(row):
+            if cell not in City.traversable:
+                d = None
+            elif (
+                cell == City.FACTORY and
+                state.allegiances.get((x, y)) != whoami
+            ):
+                d = 0
+            else:
+                d = float('inf')
+            distances[-1].append(d)
+
+    predecessors = distance_relaxer(distances, threshold)
+    return predecessors, distances
+
+@lru_cache(maxsize=16)
+def closest_unpainted(whoami, state):
+    distances = []
+    for y, row in enumerate(state.cities):
+        distances.append([])
+        for x, cell in enumerate(row):
+            if cell == City.GHOST:
+                d = None
+            elif state.allegiances.get((x, y)) != whoami:
+                d = 0
+            else:
+                d = float('inf')
+            distances[-1].append(d)
+
+    return distance_relaxer(distances)
+
+def good_moves(state, x, y, moves):
+    return [
+        move
+        for move in moves
+        if not state.robots[state.expected_position(x, y, move)]
+    ]
+
+class Napoleon:
+    def __init__(self, capturers_frac=1, dampening=0.01):
+        # (x, y) -> (goal, goal_state)
+        self.bot_goals = {}
+        self.capturers_frac = capturers_frac
+        self.dampening = dampening
+
+    def goal_capture(self, whoami, state, x, y, goal_state):
+        moves = goal_state
+        good = good_moves(state, x, y, moves)
+        if good:
+            return choice(good)
+        return choice(moves or 'P')
+
+    def goal_paint(self, whoami, state, x, y, goal_state):
+        predecessors = closest_unpainted(whoami, state)
+        moves = predecessors[y][x]
+        good = good_moves(state, x, y, moves)
+        if good:
+            return choice(good)
+        return choice(moves or 'P')
+
+    def strategise(self, whoami, state):
+        bot_goals = {}
+
+        robots = state.robots_by_player[whoami]
+
+        if len(state.factories_by_player[whoami]) <= 3:
+            self.capturers_frac = 1.0
+        if len(robots) <= 10:
+            self.capturers_frac = 1.0
+
+#        threshold = 10 if early_stages else None
+        threshold = None
+        spawns, spawns_dist = get_close_spawns(whoami, state, threshold)
+
+        max_capturers = max(3, int(len(robots) * self.capturers_frac))
+        self.capturers_frac = max(0.01, self.capturers_frac - self.dampening)
+        capturers = {
+            (x, y)
+            for x, y, _ in sorted(
+                robots,
+                key=lambda r: spawns_dist[r[1]][r[0]],
+            )[:max_capturers]
+            if threshold is None or spawns_dist[y][x] <= threshold
+        }
+        assert len(capturers) <= max_capturers
+
+        for x, y, energy in robots:
+            if (x, y) in capturers:
+                goal = 'capture'
+                goal_state = spawns[y][x]
+            else:
+                goal = 'paint'
+                goal_state = None
+
+            bot_goals[x, y] = (goal, goal_state)
+
+#        print(whoami, ''.join(v[0].upper() for v, _ in bot_goals.values()))
+        return bot_goals
+
+    def __call__(self, whoami, state):
+        self.bot_goals = self.strategise(whoami, state)
+
+        results = {}
+
+        todo = self.bot_goals.keys()
+
+        for _ in range(2):
+            by_dest = defaultdict(list)
+
+            for x, y in todo:
+                goal, goal_state = self.bot_goals[x, y]
+                goal_fn = getattr(self, 'goal_' + goal)
+                action = goal_fn(whoami, state, x, y, goal_state)
+                by_dest[state.expected_position(x, y, action)].append((x, y))
+                results[x, y] = action
+
+            todo = []
+            for robots in by_dest.values():
+                if len(robots) > 1:
+                    todo.extend(robots)
+
+        return ''.join(
+            results[x, y]
+            for x, y, _ in state.robots_by_player[whoami]
+        )
+
+if __name__ == '__main__':
+#    import random
+
+#    random.seed(42)
+    map_ = robots.border_map(42, 22, 0)
+
+    add_spawns(map_, 10, 'X')
+    add_spawns(map_, 20, '+')
+    add_spawns(map_, 6)
+
+    game = robots.Game(map_)
+
+    game.add_bot(Napoleon(), 'Napoleon')
+
+    from capturer import CaptureSpawns
+    game.add_bot(CaptureSpawns(), 'Alice')
+    game.add_bot(CaptureSpawns(), 'Bob')
+    game.add_bot(CaptureSpawns(), 'Charlie')
+    game.add_bot(CaptureSpawns(), 'Doug')
+
+    viewer = robots.CursesViewer(game)
+    viewer.run()