def test_diffuse_total_value_no_change(self): 'make sure total value of all sources do not change' pickle_file = open('test_data/influence_test/turn_1.gamestate', 'r') gamestate = pickle.load(pickle_file) pickle_file.close() inf = Influence(gamestate) inf.map[(23,18)] = 100 inf.map[(10,10)] = 100 inf.map[(26, 18)] = -10 for i in xrange(50): inf.diffuse()
def test_diffuse_with_water(self): 'make sure influence next to water diffuse slower than open locations' pickle_file = open('test_data/influence_test/turn_1.gamestate', 'r') gamestate = pickle.load(pickle_file) pickle_file.close() inf = Influence(gamestate) inf.map[(23,18)] = 10 inf.map[(10,10)] = 10 for i in xrange(5): inf.diffuse() #print ('%f, %f' % (inf.map[(23,18)], inf.map[(10,10)])) self.assertTrue(inf.map[(23,18)] < inf.map[(10,10)])
def test_diffuse_parent_greater_than_children_single_pass(self): 'make sure parent is greater or equal to child after single pass' pickle_file = open('test_data/influence_test/turn_1.gamestate', 'r') gamestate = pickle.load(pickle_file) pickle_file.close() gamestate.map = np.array(gamestate.map) inf = Influence(gamestate) #print(str(gamestate.food_list)) for food_loc in gamestate.food_list: inf.map[food_loc] = 4 for i in xrange(5): inf.diffuse() for food_loc in gamestate.food_list: food_val = inf.map[food_loc] for n_loc in gamestate.get_neighbour_locs(food_loc): #print('%f => %f' % (food_val, inf.map[n_loc])) self.assertTrue(food_val >= inf.map[n_loc])
def test_diffuse_multi_source_overshadow(self): 'make sure larger influence over shadows the smaller opposite influence right next' pickle_file = open('test_data/influence_test/turn_1.gamestate', 'r') gamestate = pickle.load(pickle_file) pickle_file.close() inf = Influence(gamestate) x = 25 y = 20 inf.map[(x, y)] = 10 inf.map[(x+1, y)] = -1 for i in xrange(5): inf.diffuse() self.assertTrue(inf.map[(x,y)] > inf.map[(x+1, y)]) self.assertTrue(inf.map[(x+1,y)] > inf.map[(x+2, y)]) self.assertTrue(inf.map[(x+2,y)] > inf.map[(x+3, y)]) self.assertTrue(inf.map[(x+3,y)] > inf.map[(x+4, y)]) self.assertTrue(inf.map[(x+2,y)] > inf.map[(x+2,y+1)]) self.assertTrue(inf.map[(x+3,y)] > inf.map[(x+3, y-1)])
def test_diffuse_parent_greater_than_children_multi_pass(self): 'make sure each level of parent is greater than its children after multiple passes' pickle_file = open('test_data/influence_test/turn_1.gamestate', 'r') gamestate = pickle.load(pickle_file) pickle_file.close() inf = Influence(gamestate) for food_loc in gamestate.food_list: inf.map[food_loc] += 4 for i in xrange(5): inf.diffuse() for food_loc in gamestate.food_list: food_val = inf.map[food_loc] for n_loc in gamestate.get_neighbour_locs(food_loc): n_val = inf.map[n_loc] self.assertTrue(food_val > n_val) for nn_loc in [loc for loc in gamestate.get_neighbour_locs(n_loc) if loc != food_loc]: nn_val = inf.map[nn_loc] self.assertTrue(n_val > nn_val)
class MyBot: def __init__(self, gamestate): # define class level variables, will be remembered between turns self.gamestate = gamestate self.diffuse_time = 0 self.combat_time_history = deque([0, 0, 0, 0, 0]) self.combat_time = 0 self.explore_time = 0 # do_setup is run once at the start of the game # after the bot has received the game settings def do_setup(self): # initialize data structures after learning the game settings self.food_influence = LinearInfluence(self.gamestate) self.raze_influence = LinearInfluence(self.gamestate) self.defense_influence = LinearInfluence(self.gamestate) self.explore_influence = Influence(self.gamestate) self.planner = Planner(self.gamestate) def log_turn(self, turn_no): debug_logger.debug('turn ' + str(self.gamestate.current_turn)) perf_logger.debug('turn ' + str(self.gamestate.current_turn)) perf_logger.debug('self.diffuse_time = %d' % self.diffuse_time) perf_logger.debug('self.combat_time_history = %s' % str(self.combat_time_history)) perf_logger.debug('self.combat_time = %s' % self.combat_time) perf_logger.debug('self.explore_time = %d' % self.explore_time) def log_detail(self): if DETAIL_LOG and os.path.isdir('pickle') and \ int(self.gamestate.current_turn) > DETAIL_LOG_START and \ int(self.gamestate.current_turn) % 10 == 0 \ : # dump gamestate pickle_file = open('pickle/turn_' + str(self.gamestate.current_turn) + '.gamestate', 'wb') pickle.dump(self.gamestate, pickle_file) pickle_file.close() # dump influence map value # pickle_file = open('pickle/turn_' + str(self.gamestate.current_turn) + '.influence', 'wb') # pickle.dump(self.explore_influence, pickle_file) # pickle_file.close() # do turn is run once per turn def do_turn(self): # detailed logging self.log_turn(self.gamestate.current_turn) # razing nearby hill takes precedence self.raze_override() # update aggressiveness self.planner.update_aggressiveness(self.explore_influence) # handle combat combat_start = self.gamestate.time_remaining() self.issue_combat_task() self.combat_time_history.append(combat_start - self.gamestate.time_remaining()) self.combat_time_history.popleft() self.combat_time = max(self.combat_time_history) # use planner to set new influence perf_logger.debug('food_influence.start = %s' % str(self.gamestate.time_elapsed())) self.planner.update_food_influence(self.food_influence) perf_logger.debug('raze_influence.start = %s' % str(self.gamestate.time_elapsed())) debug_logger.debug('razing distance = %d' % -self.planner.enemy_hill_value) self.planner.update_raze_influence(self.raze_influence) perf_logger.debug('defense_influence.start = %s' % str(self.gamestate.time_elapsed())) self.planner.update_defense_influence(self.defense_influence) perf_logger.debug('explore_influence.start = %s' % str(self.gamestate.time_elapsed())) self.planner.update_explore_influence(self.explore_influence) perf_logger.debug('influences.finish = %s' % str(self.gamestate.time_elapsed())) # decay strategy influence self.explore_influence.decay(DECAY_RATE) # diffuse explore_influence, which is the only one using molecular diffusion perf_logger.debug('explore_influence.diffuse().start = %s' % str(self.gamestate.time_elapsed())) for i in xrange(30): if self.gamestate.time_remaining() < self.diffuse_time + self.explore_time + 50: perf_logger.debug('bailing diffuse after %d times' % (i)) break diffuse_start = self.gamestate.time_remaining() self.explore_influence.diffuse() diffuse_duration = diffuse_start - self.gamestate.time_remaining() self.diffuse_time = max([diffuse_duration, self.diffuse_time]) self.diffuse_time -= 1 perf_logger.debug('explore_influence.diffuse().finish = %s' % str(self.gamestate.time_elapsed())) self.log_detail() # merge all the influences into a temporary map perf_logger.debug('merging map.start = %s' % str(self.gamestate.time_elapsed())) merged_map = self.food_influence.map * FOOD_WEIGHT + self.raze_influence.map * RAZE_WEIGHT + \ self.defense_influence.map * DEFENSE_WEIGHT + self.explore_influence.map perf_logger.debug('merging map.finish = %s' % str(self.gamestate.time_elapsed())) explore_start = self.gamestate.time_remaining() # avoidance explorer self.avoidance_explore(merged_map) perf_logger.debug('self.avoidance_explore.finish = %s' % str(self.gamestate.time_elapsed())) # normal explore self.normal_explore(merged_map) self.explore_time = max([explore_start - self.gamestate.time_remaining(), self.explore_time]) - 1 perf_logger.debug('self.normal_explore.finish = %s' % str(self.gamestate.time_elapsed())) perf_logger.debug('endturn: my_ants count = %d, time_elapsed = %s' % (len(self.gamestate.my_ants()), self.gamestate.time_elapsed())) def raze_override(self): 'razing close-by hill takes precedence over combat' for hill_loc, owner in self.gamestate.enemy_hills(): for n_loc in self.gamestate.neighbour_table[hill_loc]: if n_loc in self.gamestate.my_ants(): self.gamestate.issue_order_by_location(n_loc, hill_loc) def issue_combat_task(self): 'combat logic' perf_logger.debug('issue_combat_task.start = ' + str(self.gamestate.time_elapsed())) battle.do_combat(self.gamestate) perf_logger.debug('issue_combat_task.finish = ' + str(self.gamestate.time_elapsed())) def get_desired_moves(self, ant, map): desired_moves = [] neighbours_and_influences = sorted([(map[loc], loc) for loc in self.gamestate.passable_moves(ant)]) debug_logger.debug('neighbours_and_influences = %s' % str(neighbours_and_influences)) for inf, n_loc in neighbours_and_influences: desired_moves.append(n_loc) return desired_moves def normal_explore(self, merged_map): 'only concern influence' for my_ant in sorted(self.gamestate.my_unmoved_ants()): debug_logger.debug('normal_explore task for %s' % str(my_ant)) desired_moves = self.get_desired_moves(my_ant, merged_map) if len(desired_moves) > 0: # do the move self.gamestate.issue_order_by_location(my_ant, desired_moves[0]) else: debug_logger.debug('ERROR: no valid move for ant = %s' % str(my_ant)) # check if we still have time left to calculate more orders if self.gamestate.time_remaining() < 10: perf_logger.debug('bailing normal explore') break def avoidance_explore(self, merged_map): 'explore under enemies presence' avoidance_distance = self.gamestate.euclidean_distance_add(self.gamestate.attackradius2, 2) for my_ant in self.gamestate.my_unmoved_ants(): enemy_ants = [enemy_ant for enemy_ant, owner in self.gamestate.enemy_ants() if self.gamestate.euclidean_distance2(my_ant, enemy_ant) <= avoidance_distance] if len(enemy_ants) > 0: debug_logger.debug('avoidance_explore task for %s' % str(my_ant)) desired_moves = self.get_desired_moves(my_ant, merged_map) move_distances = {move:min([self.gamestate.euclidean_distance2(move, enemy_ant) for enemy_ant in enemy_ants]) for move in desired_moves} debug_logger.debug('move_distances = %s' % str(move_distances)) safe_distance = self.gamestate.euclidean_distance_add(self.gamestate.attackradius2, 1) # don't initiate 1 on 1 exchange, but don't be afraid desired_distance = self.gamestate.attackradius2 # be safer if more enemies are around # also if only 1 move is within risky zone (risky zone = possible clash if both ants advances) if len(enemy_ants) > 1 or len([d for m,d in move_distances.items() if d < safe_distance]) <= 2: desired_distance = safe_distance # go for lowest influence that's safe best_move = None for move in desired_moves: if move_distances[move] > desired_distance: best_move = move break # if no safe move try largest distance if best_move is None: best_move = max(move_distances, key=move_distances.get) # do the move directions = self.gamestate.direction(my_ant, best_move) + [None] self.gamestate.issue_order_by_location(my_ant, best_move) debug_logger.debug('moving %s' % str((my_ant, directions[0]))) # check if we still have time left to calculate more orders if self.gamestate.time_remaining() < 20: perf_logger.debug('bailing avoidance explore') break # static methods are not tied to a class and don't have self passed in # this is a python decorator @staticmethod def run(): 'parse input, update game state and call the bot classes do_turn method' gamestate = GameState() bot = MyBot(gamestate) map_data = '' while(True): try: current_line = sys.stdin.readline().rstrip('\r\n') # string new line char if current_line.lower() == 'ready': gamestate.setup(map_data) bot.do_setup() gamestate.finish_turn() map_data = '' elif current_line.lower() == 'go': gamestate.update(map_data) # call the do_turn method of the class passed in bot.do_turn() gamestate.finish_turn() map_data = '' else: map_data += current_line + '\n' except EOFError: break except KeyboardInterrupt: raise except: # don't raise error or return so that bot attempts to stay alive traceback.print_exc(file=sys.stderr) sys.stderr.flush()
class MyBot: def __init__(self, gamestate): # define class level variables, will be remembered between turns self.gamestate = gamestate self.diffuse_time = 0 self.combat_time_history = deque([0, 0, 0, 0, 0]) self.combat_time = 0 # do_setup is run once at the start of the game # after the bot has received the game settings def do_setup(self): # initialize data structures after learning the game settings self.strat_influence = Influence(self.gamestate) self.planner = Planner(self.gamestate) def log_turn(self, turn_no): logging.debug('turn ' + str(self.gamestate.current_turn)) logging.debug('self.diffuse_time = %d' % self.diffuse_time) logging.debug('self.combat_time_history = %s' % str(self.combat_time_history)) logging.debug('self.combat_time = %s' % self.combat_time) # logging.debug('self.strat_influence.map over 0.01 count: %d' % # len([key for key in self.strat_influence.map if math.fabs(self.strat_influence.map[key]) > 0.01])) def log_detail(self): if DETAIL_LOG and os.path.isdir('pickle'):# and int(self.gamestate.current_turn) % 10 == 0: # dump gamestate pickle_file = open('pickle/turn_' + str(self.gamestate.current_turn) + '.gamestate', 'wb') pickle.dump(self.gamestate, pickle_file) pickle_file.close() # dump influence map value pickle_file = open('pickle/turn_' + str(self.gamestate.current_turn) + '.influence', 'wb') pickle.dump(self.strat_influence, pickle_file) pickle_file.close() # do turn is run once per turn def do_turn(self): # detailed logging self.log_turn(self.gamestate.current_turn) # decay strategy influence #logging.debug('strat_influence.decay().start = %s' % str(self.gamestate.time_remaining())) self.strat_influence.decay(DECAY_RATE) #self.strat_influence = Influence(self.gamestate) #logging.debug('strat_influence.decay().finish = %s' % str(self.gamestate.time_remaining())) # use planner to set new influence logging.debug('self.planner.do_strategy_plan.start = %s' % str(self.gamestate.time_remaining())) self.planner.do_strategy_plan(self.strat_influence) # for row in range(self.strat_influence.map.shape[0]): # for col in range(self.strat_influence.map.shape[1]): # if math.fabs(self.strat_influence.map[row,col]): # logging.debug('%d, %d = %f' % (row, col, self.strat_influence.map[row,col])) # diffuse strategy influence logging.debug('strat_influence.diffuse().start = %s' % str(self.gamestate.time_remaining())) for i in xrange(10): if self.gamestate.time_remaining() < self.combat_time + 100: logging.debug('bailing diffuse after %d times' % (i)) break diffuse_start = self.gamestate.time_remaining() self.strat_influence.diffuse() diffuse_duration = diffuse_start - self.gamestate.time_remaining() self.diffuse_time = max([diffuse_duration, self.diffuse_time]) self.diffuse_time -= 1 logging.debug('strat_influence.diffuse().finish = %s' % str(self.gamestate.time_remaining())) # razing nearby hill takes precedence self.raze_override() # handle combat combat_start = self.gamestate.time_remaining() self.issue_combat_task() self.combat_time_history.append(combat_start - self.gamestate.time_remaining()) self.combat_time_history.popleft() self.combat_time = max(self.combat_time_history) self.log_detail() # handle explorer self.issue_explore_task() logging.debug('endturn: my_ants count = %d, time_elapsed = %s' % (len(self.gamestate.my_ants()), self.gamestate.time_elapsed())) def raze_override(self): 'razing close-by hill takes precedence over combat' for hill_loc, owner in self.gamestate.enemy_hills(): for n_loc in self.gamestate.neighbour_table[hill_loc]: if n_loc in self.gamestate.my_ants(): direction = self.gamestate.direction(n_loc, hill_loc) + [None] self.gamestate.issue_order((n_loc, direction[0])) def issue_combat_task(self): 'combat logic' logging.debug('issue_combat_task.start = %s' % str(self.gamestate.time_remaining())) zones = battle.get_combat_zones(self.gamestate) logging.debug('get_combat_zones.finish = %s' % str(self.gamestate.time_remaining())) if zones is not None: logging.debug('zones.count = %d' % len(zones)) for zone in zones: if len(zone[0]) > 0: logging.debug('group combat loop for = %s' % str(zone)) logging.debug('do_zone_combat.start = %s' % str(self.gamestate.time_remaining())) battle.do_zone_combat(self.gamestate, zone) logging.debug('do_zone_combat.start = %s' % str(self.gamestate.time_remaining())) # check if we still have time left to calculate more orders if self.gamestate.time_remaining() < 50: break logging.debug('issue_combat_task.finish = ' + str(self.gamestate.time_remaining())) def normal_explore(self, my_ant): 'only concern influence' loc_influences = {} for d in self.gamestate.passable_directions(my_ant): loc_influences[d] = self.strat_influence.map[self.gamestate.destination(my_ant, d)] #logging.debug('my_ant = %s, loc_influences = %s' % (str(my_ant),str(loc_influences))) if len(loc_influences) > 0: best_directions = min(loc_influences, key=loc_influences.get) logging.debug('moving %s to %s' % (str(my_ant), str(best_directions))) self.gamestate.issue_order((my_ant, best_directions)) def avoidance_explore(self, my_ant, enemy_ants): 'explore under enemies presence' safe_distance = self.gamestate.euclidean_distance_add(self.gamestate.attackradius2, 1) nav_info = [] for loc in self.gamestate.neighbour_table[my_ant] + [my_ant]: influence = self.strat_influence.map[loc] distance = min([self.gamestate.euclidean_distance2(loc, enemy_ant) for enemy_ant in enemy_ants]) nav_info.append((influence, distance, loc)) # go for lowest influence that's safe nav_info.sort() best_loc = None for info in nav_info: (influence, distance, loc) = info if distance > safe_distance: best_loc = loc break # if no safe move try largest distance if best_loc is None: (influence, distance, loc) = sorted(nav_info, key=lambda x: x[1])[0] best_loc = loc # do the move directions = self.gamestate.direction(my_ant, best_loc) + [None] self.gamestate.issue_order((my_ant, directions[0])) def issue_explore_task(self): 'explore map based on influence' logging.debug('issue_explore_task.start = %s' % str(self.gamestate.time_remaining())) # loop through all my un-moved ants and set them to explore # the ant_loc is an ant location tuple in (row, col) form avoidance_distance = self.gamestate.euclidean_distance_add(self.gamestate.attackradius2, 2) for my_ant in self.gamestate.my_unmoved_ants(): enemy_ants = [enemy_ant for enemy_ant, owner in self.gamestate.enemy_ants() if self.gamestate.euclidean_distance2(my_ant, enemy_ant) <= avoidance_distance] if len(enemy_ants) > 0: self.avoidance_explore(my_ant, enemy_ants) else: self.normal_explore(my_ant) # check if we still have time left to calculate more orders if self.gamestate.time_remaining() < 10: break logging.debug('issue_explore_task.finish = ' + str(self.gamestate.time_remaining())) # static methods are not tied to a class and don't have self passed in # this is a python decorator @staticmethod def run(): 'parse input, update game state and call the bot classes do_turn method' gamestate = GameState() bot = MyBot(gamestate) map_data = '' while(True): try: current_line = sys.stdin.readline().rstrip('\r\n') # string new line char if current_line.lower() == 'ready': gamestate.setup(map_data) bot.do_setup() gamestate.finish_turn() map_data = '' elif current_line.lower() == 'go': gamestate.update(map_data) # call the do_turn method of the class passed in bot.do_turn() gamestate.finish_turn() map_data = '' else: map_data += current_line + '\n' except EOFError: break except KeyboardInterrupt: raise except: # don't raise error or return so that bot attempts to stay alive traceback.print_exc(file=sys.stderr) sys.stderr.flush()
class MyBot: def __init__(self, gamestate): # define class level variables, will be remembered between turns self.gamestate = gamestate self.diffuse_time = 0 self.combat_time_history = deque([0, 0, 0, 0, 0]) self.combat_time = 0 # do_setup is run once at the start of the game # after the bot has received the game settings def do_setup(self): # initialize data structures after learning the game settings self.food_influence = LinearInfluence(self.gamestate) self.raze_influence = LinearInfluence(self.gamestate) self.defense_influence = LinearInfluence(self.gamestate) self.explore_influence = Influence(self.gamestate) self.planner = Planner(self.gamestate) def log_turn(self, turn_no): debug_logger.debug("turn " + str(self.gamestate.current_turn)) perf_logger.debug("turn " + str(self.gamestate.current_turn)) perf_logger.debug("self.diffuse_time = %d" % self.diffuse_time) perf_logger.debug("self.combat_time_history = %s" % str(self.combat_time_history)) perf_logger.debug("self.combat_time = %s" % self.combat_time) def log_detail(self): if DETAIL_LOG and os.path.isdir("pickle"): # and int(self.gamestate.current_turn) % 10 == 0: # dump gamestate pickle_file = open("pickle/turn_" + str(self.gamestate.current_turn) + ".gamestate", "wb") pickle.dump(self.gamestate, pickle_file) pickle_file.close() # dump influence map value pickle_file = open("pickle/turn_" + str(self.gamestate.current_turn) + ".influence", "wb") pickle.dump(self.explore_influence, pickle_file) pickle_file.close() # do turn is run once per turn def do_turn(self): # detailed logging self.log_turn(self.gamestate.current_turn) # razing nearby hill takes precedence self.raze_override() # handle combat combat_start = self.gamestate.time_remaining() self.issue_combat_task() self.combat_time_history.append(combat_start - self.gamestate.time_remaining()) self.combat_time_history.popleft() self.combat_time = max(self.combat_time_history) # use planner to set new influence perf_logger.debug("self.update_influences.start = %s" % str(self.gamestate.time_elapsed())) self.planner.update_food_influence(self.food_influence) perf_logger.debug("self.update_food_influence.finish = %s" % str(self.gamestate.time_elapsed())) self.planner.update_raze_influence(self.raze_influence) perf_logger.debug("self.update_raze_influence.finish = %s" % str(self.gamestate.time_elapsed())) self.planner.update_defense_influence(self.defense_influence) perf_logger.debug("self.update_defense_influence.finish = %s" % str(self.gamestate.time_elapsed())) self.planner.update_explore_influence(self.explore_influence) perf_logger.debug("self.update_explore_influence.finish = %s" % str(self.gamestate.time_elapsed())) # decay strategy influence self.explore_influence.decay(DECAY_RATE) # diffuse explore_influence, which is the only one using molecular diffusion perf_logger.debug("explore_influence.diffuse().start = %s" % str(self.gamestate.time_elapsed())) expected_explore_time = len(self.gamestate.my_ants()) / 2 + 20 for i in xrange(30): if self.gamestate.time_remaining() < self.diffuse_time + expected_explore_time: perf_logger.debug("bailing diffuse after %d times" % (i)) break diffuse_start = self.gamestate.time_remaining() self.explore_influence.diffuse() diffuse_duration = diffuse_start - self.gamestate.time_remaining() self.diffuse_time = max([diffuse_duration, self.diffuse_time]) self.diffuse_time -= 1 perf_logger.debug("explore_influence.diffuse().finish = %s" % str(self.gamestate.time_elapsed())) self.log_detail() # avoidance explorer self.avoidance_explore() perf_logger.debug("self.avoidance_explore.finish = %s" % str(self.gamestate.time_elapsed())) # normal explore self.normal_explore() perf_logger.debug("self.normal_explore.finish = %s" % str(self.gamestate.time_elapsed())) perf_logger.debug( "endturn: my_ants count = %d, time_elapsed = %s" % (len(self.gamestate.my_ants()), self.gamestate.time_elapsed()) ) def raze_override(self): "razing close-by hill takes precedence over combat" for hill_loc, owner in self.gamestate.enemy_hills(): for n_loc in self.gamestate.neighbour_table[hill_loc]: if n_loc in self.gamestate.my_ants(): direction = self.gamestate.direction(n_loc, hill_loc) + [None] self.gamestate.issue_order((n_loc, direction[0])) def issue_combat_task(self): "combat logic" perf_logger.debug("issue_combat_task.start = %s" % str(self.gamestate.time_remaining())) zones = battle.get_combat_zones(self.gamestate) perf_logger.debug("get_combat_zones.finish = %s" % str(self.gamestate.time_remaining())) if zones is not None: debug_logger.debug("zones.count = %d" % len(zones)) i = 0 for zone in zones: i += 1 # debug_logger.debug('group combat loop for = %s' % str(zone)) # perf_logger.debug('do_zone_combat.start = %s' % str(self.gamestate.time_remaining())) battle.do_zone_combat(self.gamestate, zone) # perf_logger.debug('do_zone_combat.start = %s' % str(self.gamestate.time_remaining())) # check if we still have time left to calculate more orders if self.gamestate.time_remaining() < 100: debug_logger.debug("bailing combat zone after %d times" % (i)) break perf_logger.debug("issue_combat_task.finish = " + str(self.gamestate.time_remaining())) def get_desired_moves(self, ant): desired_moves = [] # food desired_moves.extend(self.get_desired_move_from_linear_influence(ant, self.food_influence)) debug_logger.debug("desired_moves.food = %s" % str(desired_moves)) # defend hill desired_moves.extend(self.get_desired_move_from_linear_influence(ant, self.defense_influence)) debug_logger.debug("desired_moves.defense = %s" % str(desired_moves)) # raze hill desired_moves.extend(self.get_desired_move_from_linear_influence(ant, self.raze_influence)) debug_logger.debug("desired_moves.raze = %s" % str(desired_moves)) # explore desired_moves.extend(self.get_desired_move_from_molecular_influence(ant, self.explore_influence)) debug_logger.debug("desired_moves.explore = %s" % str(desired_moves)) # uniquify seen = set() seen_add = seen.add desired_moves = [move for move in desired_moves if move not in seen and not seen_add(move)] return desired_moves def get_desired_move_from_molecular_influence(self, ant, influence): desired_moves = [] neighbours_and_influences = sorted( [(influence.map[loc], loc) for loc in [ant] + self.gamestate.passable_neighbours(ant)] ) debug_logger.debug("neighbours_and_influences = %s" % str(neighbours_and_influences)) for inf, n_loc in neighbours_and_influences: desired_moves.append(n_loc) return desired_moves def get_desired_move_from_linear_influence(self, ant, influence): desired_moves = [] if influence.map[ant] != 0: neighbours_and_influences = sorted( [(influence.map[loc], loc) for loc in [ant] + self.gamestate.passable_neighbours(ant)] ) debug_logger.debug("neighbours_and_influences = %s" % str(neighbours_and_influences)) for inf, n_loc in neighbours_and_influences: if inf < influence.map[ant]: desired_moves.append(n_loc) return desired_moves def normal_explore(self): "only concern influence" for my_ant in self.gamestate.my_unmoved_ants(): debug_logger.debug("normal explore task for %s" % str(my_ant)) desired_moves = self.get_desired_moves(my_ant) if len(desired_moves) > 0: move = desired_moves[0] # do the move directions = self.gamestate.direction(my_ant, move) + [None] self.gamestate.issue_order((my_ant, directions[0])) # check if we still have time left to calculate more orders if self.gamestate.time_remaining() < 10: break def avoidance_explore(self): "explore under enemies presence" avoidance_distance = self.gamestate.euclidean_distance_add(self.gamestate.attackradius2, 2) for my_ant in self.gamestate.my_unmoved_ants(): enemy_ants = [ enemy_ant for enemy_ant, owner in self.gamestate.enemy_ants() if self.gamestate.euclidean_distance2(my_ant, enemy_ant) <= avoidance_distance ] if len(enemy_ants) > 0: # don't initiate 1 on 1 exchange, but don't be afraid safe_distance = self.gamestate.attackradius2 if len(enemy_ants) > 1: # be safer if more enemies are around safe_distance = self.gamestate.euclidean_distance_add(self.gamestate.attackradius2, 1) desired_moves = self.get_desired_moves(my_ant) move_distances = { move: min([self.gamestate.euclidean_distance2(move, enemy_ant) for enemy_ant in enemy_ants]) for move in desired_moves } debug_logger.debug("move_distances = %s" % str(move_distances)) # go for lowest influence that's safe best_move = None for move in desired_moves: if move_distances[move] > safe_distance: best_move = move break # if no safe move try largest distance if best_move is None: best_move = max(move_distances, key=move_distances.get) # do the move directions = self.gamestate.direction(my_ant, best_move) + [None] self.gamestate.issue_order((my_ant, directions[0])) debug_logger.debug("moving %s" % str((my_ant, directions[0]))) # check if we still have time left to calculate more orders if self.gamestate.time_remaining() < 30: break # static methods are not tied to a class and don't have self passed in # this is a python decorator @staticmethod def run(): "parse input, update game state and call the bot classes do_turn method" gamestate = GameState() bot = MyBot(gamestate) map_data = "" while True: try: current_line = sys.stdin.readline().rstrip("\r\n") # string new line char if current_line.lower() == "ready": gamestate.setup(map_data) bot.do_setup() gamestate.finish_turn() map_data = "" elif current_line.lower() == "go": gamestate.update(map_data) # call the do_turn method of the class passed in bot.do_turn() gamestate.finish_turn() map_data = "" else: map_data += current_line + "\n" except EOFError: break except KeyboardInterrupt: raise except: # don't raise error or return so that bot attempts to stay alive traceback.print_exc(file=sys.stderr) sys.stderr.flush()