diff options
-rw-r--r-- | capturer.py | 2 | ||||
-rw-r--r-- | napoleon.py | 165 |
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() |