def move_to_closest_of(c, targets): # type: (SquadDrone, List[Union[RoomPosition, RoomObject, SquadDrone]]) -> bool target = None distance = Infinity for test_target in targets: test_distance = movement.chebyshev_distance_room_pos( c.pos, robjs.pos(test_target)) if test_distance < distance: distance = test_distance target = test_target target = robjs.pos(target) if c.pos.roomName == target.roomName: if c.pos.isNearTo(target): return False else: c.move_to(target) return True elif movement.chebyshev_distance_room_pos(c.pos, target) < 100: c.move_to(target) return True else: # TODO: this should accommodate reroutes _faster_? reroute = portals.recommended_reroute(c.pos, target) if reroute is not None: target = reroute[0] c.move_to(target) return True
def force_basic_move_to(self, target, creep_cond=lambda x: True): # type: (Union[RoomObject, RoomPosition], Callable[[Creep], bool]) -> bool """ Tries to do a basic move in the direction, forcing place switching with an creep for which creep_cond(creep) returns True. :param target: The location (pos or object with pos property) to move to :param creep_cond: The condition with which this creep will take the place of another creep (default is "lambda x: True") :return: True if moved, False otherwise. """ if self.creep.fatigue > 0: return True target = robjs.pos(target) if self.pos.isNearTo(target): return True adx = target.x - self.pos.x ady = target.y - self.pos.y dx = Math.sign(adx) dy = Math.sign(ady) if dx and dy: if self._try_force_move_to(self.pos.x + dx, self.pos.y + dy, creep_cond): return True elif adx == 1 and ady == 1: return False elif self._try_force_move_to(self.pos.x + dx, self.pos.y, creep_cond): return True elif self._try_force_move_to(self.pos.x, self.pos.y + dy, creep_cond): return True elif dx: if self._try_force_move_to(self.pos.x + dx, self.pos.y, creep_cond): return True elif adx == 1: return False elif self._try_force_move_to(self.pos.x + dx, self.pos.y + 1, creep_cond): return True elif self._try_force_move_to(self.pos.x + dx, self.pos.y - 1, creep_cond): return True elif dy: if self._try_force_move_to(self.pos.x, self.pos.y + dy, creep_cond): return True elif ady == 1: return False elif self._try_force_move_to(self.pos.x + 1, self.pos.y + dy, creep_cond): return True elif self._try_force_move_to(self.pos.x - 1, self.pos.y + dy, creep_cond): return True return False
def set_origin(self, origin): # type: (Union[RoomPosition, RoomObject]) -> None origin = robjs.pos(origin) origin = __new__(RoomPosition(origin.x, origin.y, origin.roomName)) self.__origin = origin self.mem[squadmemkey_origin] = { 'xy': positions.serialize_pos_xy(origin), 'room': origin.roomName }
def get_military_path_length(self, spawn, target, opts = None): # type: (RoomPosition, RoomPosition, Dict[str, Any]) -> int spawn = robjs.pos(spawn) target = robjs.pos(target) if opts: path_opts = opts else: path_opts = {} # if distance_squared_room_pos(spawn, target) > math.pow(200, 2): # # TODO: handle this better (this is for not having multiple super-duper-long cached paths) # intermediate = __new__(RoomPosition(25, 25, target.roomName)) # path_opts.max_ops = 30000 # path_opts.max_rooms = 30 # path_opts.use_roads = False # path1 = self.hive.honey.find_path(spawn, intermediate, path_opts) # path2 = self.hive.honey.find_path(intermediate, target, path_opts) # return len(path1) + 20 + len(path2) # else: path_opts.max_ops = chebyshev_distance_room_pos(spawn, target) * 150 path_opts.max_rooms = math.ceil(chebyshev_distance_room_pos(spawn, target) / 5) return self.hive.honey.find_path_length(spawn, target, path_opts)
def move_to(self, _target, opts=None): # type: (Union[RoomPosition, RoomObject, RoleBase], Dict[str, Any]) -> None if self.creep.fatigue <= 0: target = robjs.pos(_target) result = self._try_move_to(target, opts) if result == ERR_NO_BODYPART: self.log("Couldn't move, all move parts dead!") if not (self.room.my and self.room.defense.healing_capable()) and \ not _.some(self.room.find(FIND_MY_CREEPS), lambda c: c.hasActiveBodyparts(HEAL)): self.creep.suicide() self.home.check_all_creeps_next_tick() elif result != OK: if result != ERR_NOT_FOUND and ( result != ERR_NO_PATH or (self.pos.x != 49 and self.pos.y != 49 and self.pos.x != 0 and self.pos.y != 0)): self.log( "WARNING: Unknown result from ({} at {}:{},{}).moveTo({}:{},{} ({})): {}", self.memory.role, self.pos.roomName, self.pos.x, self.pos.y, target.roomName, target.x, target.y, target, result)
def follow_energy_path(self, origin, target, mine=None): # type: (Union[RoomObject, RoomPosition], Union[RoomObject, RoomPosition], Any) -> None origin = robjs.pos(origin) target = robjs.pos(target) if self.creep.fatigue > 0: return if origin.isNearTo(target): if self.pos.roomName == target.roomName: self.move_to(target) return else: origin = self.home.spawn.pos if mine: opts = { 'current_room': self.pos.roomName, 'paved_for': mine, } elif self.carry_sum() == 0: opts = {'current_room': self.pos.roomName, 'use_roads': False} else: opts = {'current_room': self.pos.roomName} path = self.hive.honey.find_serialized_path(origin, target, opts) # TODO: manually check the next position, and if it's a creep check what direction it's going result = self.creep.moveByPath(path) if result == ERR_NOT_FOUND or result == ERR_NO_PATH: if self.pos.isNearTo(target): self.basic_move_to(target) return if not self.memory.next_ppos or self.memory.off_path_for > 10 or movement.chebyshev_distance_room_pos( self.pos, self.memory.next_ppos) > CREEP_LIFE_TIME: self.memory.off_path_for = 0 # Recalculate next_ppos if we're off path for a long time all_positions = self.hive.honey.list_of_room_positions_in_path( origin, target, opts) closest = None closest_distance = Infinity for index, pos in enumerate(all_positions): if movement.chebyshev_distance_room_pos(pos, origin) < 3 \ or movement.chebyshev_distance_room_pos(pos, target) < 3: room = self.hive.get_room(pos.roomName) if room and not movement.is_block_clear( room, pos.x, pos.y): continue # Don't try and target where the miner is right now! # subtract how far we are on the path! # NOTE: 0.7 is used in building._repath_roads_for and should be changed there if changed here. distance = movement.chebyshev_distance_room_pos( self.pos, pos) - index * 0.7 if pos.roomName != self.pos.roomName or pos.x < 2 or pos.x > 48 or pos.y < 2 or pos.y > 48: distance += 10 if distance < closest_distance: closest_distance = distance closest = pos if not closest or closest_distance >= CREEP_LIFE_TIME: portals = cast( List[StructurePortal], _.filter(self.room.find(FIND_STRUCTURES), {'structureType': STRUCTURE_PORTAL})) if len(portals) and movement.chebyshev_distance_room_pos(self.pos, portals[0].pos) \ + movement.chebyshev_distance_room_pos(portals[0].destination, target) \ < movement.chebyshev_distance_room_pos(self.pos, target): self.memory.next_ppos = self.pos.findClosestByRange( portals).pos else: self.log( "WARNING: Transport creep off path, with no positions to return to. I'm at {}, going" " from {} to {}. All positions: {}!".format( self.pos, origin, target, all_positions)) if mine and self.home.mining.is_mine_linked(mine): self.log( "I'm a hauler for a linked mine! Suiciding.") self.memory.role = role_recycling if not len(all_positions): if Game.time % 20 == 10: honey.clear_cached_path(origin, target) return self.memory.next_ppos = closest mtarget = self.memory.next_ppos new_target = __new__( RoomPosition(mtarget.x, mtarget.y, mtarget.roomName)) if self.pos.isEqualTo(new_target): del self.memory.next_ppos if not self.memory.tried_new_next_ppos: self.memory.tried_new_next_ppos = True else: del self.memory.tried_new_next_ppos # the path is incorrect! self.log( "WARNING: Path from {} to {} found to be cached incorrectly - it should contain {}, but" " it doesn't.".format(origin, target, new_target)) self.log( "Path (tbd) retrieved from HoneyTrails with options ({}):\n{}" .format(opts, JSON.stringify(path, 0, 4))) honey.clear_cached_path(origin, target, opts) elif self.pos.isNearTo(new_target): if movement.is_block_clear(self.room, new_target.x, new_target.y): self.basic_move_to(new_target) else: del self.memory.next_ppos del self.memory.tried_new_next_ppos return else: del self.memory.tried_new_next_ppos self.move_to(new_target) if not self.memory.off_path_for: self.memory.off_path_for = 1 else: self.memory.off_path_for += 1 elif result != OK: self.log( "Unknown result from follow_energy_path: {}. Going from {} to {} (path {}, in {})" .format(result, origin, target, path, self.pos.roomName)) else: del self.memory.tried_new_next_ppos if self.memory.off_path_for: self.memory.on_path_for = 1 del self.memory.off_path_for elif self.memory.on_path_for: self.memory.on_path_for += 1 if self.memory.on_path_for >= 2: del self.memory.next_ppos del self.memory.on_path_for serialized_pos = self.pos.x | (self.pos.y << 6) if self.memory.last_pos == serialized_pos: if 'standstill_for' in self.memory: self.memory.standstill_for += 1 else: self.memory.standstill_for = 1 if self.memory.standstill_for % 10 == 5 and \ (not self.memory.filling or not _.find(self.room.find_in_range(FIND_MY_CREEPS, 1, self.pos), lambda c: c.memory.role != role_hauler and c.memory.role != role_miner)): del self.memory.next_ppos found_mine = False for pos in self.hive.honey.find_path(origin, target, opts): if pos.x == self.pos.x and pos.y == self.pos.y: found_mine = True elif found_mine: if movement.is_block_clear(self.room, pos.x, pos.y): self.memory.next_ppos = { "x": pos.x, "y": pos.y, "roomName": self.pos.roomName } self.move_to( __new__( RoomPosition(pos.x, pos.y, self.pos.roomName))) break elif not self.creep.fatigue: self.memory.last_pos = serialized_pos del self.memory.standstill_for
def basic_move_to(self, target): # type: (Union[RoomPosition, RoomObject]) -> bool if self.creep.fatigue > 0: return True pos = robjs.pos(target) if self.pos.isEqualTo(target): return False adx = pos.x - self.pos.x ady = pos.y - self.pos.y if pos.roomName != self.pos.roomName: room1x, room1y = movement.parse_room_to_xy(self.pos.roomName) room2x, room2y = movement.parse_room_to_xy(pos.roomName) adx += (room2x - room1x) * 50 ady += (room2y - room1y) * 50 dx = Math.sign(adx) dy = Math.sign(ady) if dx and dy: if movement.is_block_clear(self.room, self.pos.x + dx, self.pos.y + dy): self.creep.move(movement.dxdy_to_direction(dx, dy)) return True elif adx == 1 and ady == 1: return False elif movement.is_block_clear(self.room, self.pos.x + dx, self.pos.y): self.creep.move(movement.dxdy_to_direction(dx, 0)) return True elif movement.is_block_clear(self.room, self.pos.y + dy, self.pos.x): self.creep.move(movement.dxdy_to_direction(0, dy)) return True elif dx: if movement.is_block_clear(self.room, self.pos.x + dx, self.pos.y): self.creep.move(movement.dxdy_to_direction(dx, 0)) return True elif adx == 1: return False elif movement.is_block_clear(self.room, self.pos.x + dx, self.pos.y + 1): self.creep.move(movement.dxdy_to_direction(dx, 1)) return True elif movement.is_block_clear(self.room, self.pos.x + dx, self.pos.y - 1): self.creep.move(movement.dxdy_to_direction(dx, -1)) return True elif dy: if movement.is_block_clear(self.room, self.pos.x, self.pos.y + dy): self.creep.move(movement.dxdy_to_direction(0, dy)) return True elif ady == 1: return False elif movement.is_block_clear(self.room, self.pos.x + 1, self.pos.y + dy): self.creep.move(movement.dxdy_to_direction(1, dy)) return True elif movement.is_block_clear(self.room, self.pos.x - 1, self.pos.y + dy): self.creep.move(movement.dxdy_to_direction(-1, dy)) return True if dx or dy: self.creep.move(movement.dxdy_to_direction(dx, dy)) return False
def follow_military_path(self, origin, target, opts = None): # type: (RoomPosition, RoomPosition, Dict[str, Any]) -> None origin = robjs.pos(origin) target = robjs.pos(target) if opts and "to_home" in opts: to_home = opts["to_home"] else: to_home = False if not origin: origin = movement.find_an_open_space(self.memory.home) if self.creep.fatigue > 0: return if self.pos.getRangeTo(target) < 10 or self.pos.roomName == target.roomName: self.move_to(target) return path_opts = { 'current_room': self.pos.roomName, } if opts: path_opts = _.merge(path_opts, opts) # TODO: this is all stupid, PathFinder is stupid for multiple rooms! if chebyshev_distance_room_pos(origin, target) > 900 \ and not is_path_portal(origin, target) and target.roomName != 'W11S56': path_opts.max_ops = chebyshev_distance_room_pos(origin, target) * 150 path_opts.max_rooms = math.ceil(chebyshev_distance_room_pos(origin, target) / 5) # TODO: handle this better (this is for not having multiple super-duper-long cached paths) if to_home: intermediate = find_an_open_space(origin.roomName) origin = intermediate else: intermediate = center_pos(target.roomName) if self.pos.roomName != intermediate.roomName: target = intermediate path_opts.range = max(path_opts.range or 0, 10) else: # If we're this far away, let's just get to the room using a cached path and then do # basic pathfinding to get to our actual target. self.move_to(target) return pass origin_midpoint = find_midpoint(self.pos, self.pos, origin) if origin_midpoint is not None: origin = origin_midpoint dest_midpoint = find_midpoint(self.pos, origin, target) if dest_midpoint is not None: if self.pos.roomName == dest_midpoint.roomName: origin = dest_midpoint else: target = dest_midpoint path_opts.range = max(path_opts.range or 0, 10) path = self.hive.honey.find_serialized_path(origin, target, path_opts) # TODO: manually check the next position, and if it's a creep check what direction it's going result = self.creep.moveByPath(path) if result == ERR_NOT_FOUND: if self.memory.manual: self.move_to(target) elif not self.memory.next_ppos or movement.chebyshev_distance_room_pos(self.pos, self.memory.next_ppos) \ > CREEP_LIFE_TIME: all_positions = self.hive.honey.list_of_room_positions_in_path(origin, target, path_opts) closest = None closest_distance = Infinity for pos in all_positions: distance = chebyshev_distance_room_pos(self.pos, pos) if distance < closest_distance: closest_distance = distance closest = pos if closest and closest_distance < CREEP_LIFE_TIME: self.memory.next_ppos = closest if closest.isEqualTo(self.pos): self.log("WARNING: ERR_NOT_FOUND when actually still on military path! Path retrieved:\n{}" "\nPos: {}.".format(path, self.pos)) if chebyshev_distance_room_pos(self.pos, target) <= 50: self.memory.manual = True self.move_to(target) return else: portals = cast(List[StructurePortal], _.filter(self.room.find(FIND_STRUCTURES), {'structureType': STRUCTURE_PORTAL})) if len(portals) and movement.chebyshev_distance_room_pos(self.pos, portals[0].pos) \ + movement.chebyshev_distance_room_pos(portals[0].destination, target) \ < movement.chebyshev_distance_room_pos(self.pos, target): self.memory.next_ppos = self.pos.findClosestByRange(portals).pos else: self.log("WARNING: Couldn't find closest position on path from {} to {} near {}!" "\nMoving manually... (all pos: {})" .format(origin, target, self.pos, all_positions)) self.memory.next_ppos = target mtarget = self.memory.next_ppos if mtarget: new_target = __new__(RoomPosition(mtarget.x, mtarget.y, mtarget.roomName)) if self.pos.isNearTo(new_target): self.creep.move(self.pos.getDirectionTo(new_target)) else: if self.memory.checkpoint and movement.chebyshev_distance_room_pos(self.pos, new_target) > 20: del self.memory.checkpoint del self.memory.next_ppos del self.memory.lost_path_at del self.memory.off_path_for return self.move_to(new_target) if self.pos.isEqualTo(new_target): del self.memory.next_ppos if not self.memory.off_path_for: self.memory.off_path_for = 1 self.memory.lost_path_at = self.pos else: if not self.memory.lost_path_at: self.memory.lost_path_at = self.pos self.memory.off_path_for += 1 if self.memory.off_path_for > 10: if chebyshev_distance_room_pos(self.memory.lost_path_at, self.pos) < 5 \ and not self.pos.isEqualTo(new_target) \ and not self.pos.isEqualTo(get_entrance_for_exit_pos(new_target)): honey.clear_cached_path(origin, target, path_opts) del self.memory.off_path_for del self.memory.lost_path_at del self.memory.next_ppos self.log("Lost the path from {} to {}! Pos: {}. Retargeting to: {} (path: {})".format( origin, target, self.pos, new_target, ', '.join([ "({},{})".format(p.x, p.y) for p in Room.deserializePath( _.get(self.memory, ['_move', 'path'], '')) ]))) elif result != OK: self.log("Unknown result from follow_military_path: {}".format(result)) else: # String.fromCodePoint(pos.x | (pos.y << 6)); # var val = str.charCodeAt(i); # var x = (val & 0x3F); # var y = ((val >> 6) & 0x3F); # return {x: x, y: y}; if self.memory.off_path_for: del self.memory.next_ppos del self.memory.off_path_for del self.memory.lost_path_at serialized_pos = self.pos.x | (self.pos.y << 6) if self.memory.last_pos == serialized_pos: self.log("standstill! at: {}", self.pos) if self.memory.standstill_for: self.memory.standstill_for += 1 else: self.memory.standstill_for = 1 if self.memory.standstill_for == 5: del self.memory.next_ppos found_mine = False for pos in self.hive.honey.find_path(origin, target, path_opts): if pos.x == self.pos.x and pos.y == self.pos.y: found_mine = True elif found_mine: if is_block_clear(self.room, pos.x, pos.y): self.memory.next_ppos = {"x": pos.x, "y": pos.y, "roomName": self.pos.roomName} self.move_to(__new__(RoomPosition(pos.x, pos.y, self.pos.roomName))) break if self.memory.standstill_for > 10: del self.memory.last_position del self.memory.standstill_for del self.memory.next_ppos honey.clear_cached_path(origin, target, path_opts) self.move_to(target) else: self.memory.last_pos = serialized_pos del self.memory.standstill_for