summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--capturer.py2
-rw-r--r--napoleon.py165
2 files changed, 166 insertions, 1 deletions
diff --git a/capturer.py b/capturer.py
index 92f85b7..7a698ee 100644
--- a/capturer.py
+++ b/capturer.py
@@ -37,7 +37,7 @@ class CaptureSpawns(object):
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:
diff --git a/napoleon.py b/napoleon.py
new file mode 100644
index 0000000..2a1048c
--- /dev/null
+++ b/napoleon.py
@@ -0,0 +1,165 @@
+# 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()