def extend_settlement_with_storage(self, target_position): """Build a storage to extend the settlement towards the given position. Return a BUILD_RESULT constant.""" if not self.have_resources(BUILDINGS.STORAGE): return BUILD_RESULT.NEED_RESOURCES storage_class = Entities.buildings[BUILDINGS.STORAGE] storage_spots = self.island.terrain_cache.get_buildability_intersection( storage_class.terrain_type, storage_class.size, self.settlement.buildability_cache, self.buildability_cache) storage_surrounding_offsets = Rect.get_surrounding_offsets( storage_class.size) coastline = self.land_manager.coastline options = [] for (x, y) in sorted(storage_spots): builder = BasicBuilder.create(BUILDINGS.STORAGE, (x, y), 0) alignment = 1 for (dx, dy) in storage_surrounding_offsets: coords = (x + dx, y + dy) if coords in coastline or coords not in self.plan or self.plan[ coords][0] != BUILDING_PURPOSE.NONE: alignment += 1 distance = distances.distance_rect_rect(target_position, builder.position) value = distance - alignment * 0.7 options.append((-value, builder)) return self.build_best_option(options, BUILDING_PURPOSE.STORAGE)
def extend_settlement_with_storage(self, target_position): """Build a storage to extend the settlement towards the given position. Return a BUILD_RESULT constant.""" if not self.have_resources(BUILDINGS.STORAGE): return BUILD_RESULT.NEED_RESOURCES storage_class = Entities.buildings[BUILDINGS.STORAGE] storage_spots = self.island.terrain_cache.get_buildability_intersection(storage_class.terrain_type, storage_class.size, self.settlement.buildability_cache, self.buildability_cache) storage_surrounding_offsets = Rect.get_surrounding_offsets(storage_class.size) coastline = self.land_manager.coastline options = [] for (x, y) in sorted(storage_spots): builder = BasicBuilder.create(BUILDINGS.STORAGE, (x, y), 0) alignment = 1 for (dx, dy) in storage_surrounding_offsets: coords = (x + dx, y + dy) if coords in coastline or coords not in self.plan or self.plan[coords][0] != BUILDING_PURPOSE.NONE: alignment += 1 distance = distances.distance_rect_rect(target_position, builder.position) value = distance - alignment * 0.7 options.append((-value, builder)) return self.build_best_option(options, BUILDING_PURPOSE.STORAGE)
def _enlarge_collector_area(self): if not self.production_builder.have_resources(BUILDINGS.STORAGE): return BUILD_RESULT.NEED_RESOURCES moves = [(-1, 0), (0, -1), (0, 1), (1, 0)] # valid moves for collectors collector_area = self.production_builder.get_collector_area() coastline = self.land_manager.coastline # area_label contains free tiles in the production area and all road tiles area_label = dict.fromkeys( self.land_manager.roads) # {(x, y): area_number, ...} for coords, (purpose, _) in self.production_builder.plan.iteritems(): if coords not in coastline and purpose == BUILDING_PURPOSE.NONE: area_label[coords] = None areas = 0 for coords in collector_area: assert coords not in coastline if coords in area_label and area_label[coords] is not None: continue queue = deque([coords]) while queue: x, y = queue[0] queue.popleft() for dx, dy in moves: coords2 = (x + dx, y + dy) if coords2 in area_label and area_label[coords2] is None: area_label[coords2] = areas queue.append(coords2) areas += 1 coords_set_by_area = defaultdict(lambda: set()) for coords, area_number in area_label.iteritems(): if coords in self.production_builder.plan and self.production_builder.plan[ coords][ 0] == BUILDING_PURPOSE.NONE and coords not in collector_area: coords_set_by_area[area_number].add(coords) storage_class = Entities.buildings[BUILDINGS.STORAGE] storage_spots = self.island.terrain_cache.get_buildability_intersection( storage_class.terrain_type, storage_class.size, self.settlement.buildability_cache, self.production_builder.buildability_cache) storage_surrounding_offsets = Rect.get_surrounding_offsets( storage_class.size) options = [] num_offsets = int( len(self._radius_offsets) * self.personality.overlap_precision) radius_offsets = self.session.random.sample(self._radius_offsets, num_offsets) for coords in sorted(storage_spots): if coords not in area_label: continue x, y = coords area_number = area_label[coords] area_coords_set = coords_set_by_area[area_number] useful_area = 0 for dx, dy in radius_offsets: coords = (x + dx, y + dy) if coords in area_coords_set: useful_area += 1 if not useful_area: continue alignment = 1 builder = BasicBuilder.create(BUILDINGS.STORAGE, (x, y), 0) for (dx, dy) in storage_surrounding_offsets: coords = (x + dx, y + dy) if coords in coastline or coords not in self.production_builder.plan or self.production_builder.plan[ coords][0] != BUILDING_PURPOSE.NONE: alignment += 1 value = useful_area + alignment * self.personality.alignment_coefficient options.append((value, builder)) if options: return self.production_builder.build_best_option( options, BUILDING_PURPOSE.STORAGE) # enlarge the settlement area instead since just enlarging the collector area is impossible if self.village_builder.tent_queue: tent_size = Entities.buildings[BUILDINGS.RESIDENTIAL].size tent_radius = Entities.buildings[BUILDINGS.RESIDENTIAL].radius best_coords = None best_area = 0 for x, y in self.village_builder.tent_queue: new_area = 0 for coords in Rect.init_from_topleft_and_size( x, y, tent_size[0], tent_size[1]).get_radius_coordinates(tent_radius): if coords in area_label and coords not in self.land_manager.roads and coords not in collector_area: new_area += 1 if new_area > best_area: best_coords = (x, y) best_area = new_area if best_coords is not None: return self.village_builder.extend_settlement_with_tent( Rect.init_from_topleft_and_size_tuples( best_coords, tent_size)) return BUILD_RESULT.IMPOSSIBLE
def _enlarge_collector_area(self): if not self.production_builder.have_resources(BUILDINGS.STORAGE): return BUILD_RESULT.NEED_RESOURCES moves = [(-1, 0), (0, -1), (0, 1), (1, 0)] # valid moves for collectors collector_area = self.production_builder.get_collector_area() coastline = self.land_manager.coastline # area_label contains free tiles in the production area and all road tiles area_label = dict.fromkeys(self.land_manager.roads) # {(x, y): area_number, ...} for coords, (purpose, _) in self.production_builder.plan.iteritems(): if coords not in coastline and purpose == BUILDING_PURPOSE.NONE: area_label[coords] = None areas = 0 for coords in collector_area: assert coords not in coastline if coords in area_label and area_label[coords] is not None: continue queue = deque([coords]) while queue: x, y = queue.popleft() for dx, dy in moves: coords2 = (x + dx, y + dy) if coords2 in area_label and area_label[coords2] is None: area_label[coords2] = areas queue.append(coords2) areas += 1 coords_set_by_area = defaultdict(set) for coords, area_number in area_label.iteritems(): if coords in self.production_builder.plan and self.production_builder.plan[coords][0] == BUILDING_PURPOSE.NONE and coords not in collector_area: coords_set_by_area[area_number].add(coords) storage_class = Entities.buildings[BUILDINGS.STORAGE] storage_spots = self.island.terrain_cache.get_buildability_intersection(storage_class.terrain_type, storage_class.size, self.settlement.buildability_cache, self.production_builder.buildability_cache) storage_surrounding_offsets = Rect.get_surrounding_offsets(storage_class.size) options = [] num_offsets = int(len(self._radius_offsets) * self.personality.overlap_precision) radius_offsets = self.session.random.sample(self._radius_offsets, num_offsets) for coords in sorted(storage_spots): if coords not in area_label: continue x, y = coords area_number = area_label[coords] area_coords_set = coords_set_by_area[area_number] useful_area = 0 for dx, dy in radius_offsets: coords = (x + dx, y + dy) if coords in area_coords_set: useful_area += 1 if not useful_area: continue alignment = 1 builder = BasicBuilder.create(BUILDINGS.STORAGE, (x, y), 0) for (dx, dy) in storage_surrounding_offsets: coords = (x + dx, y + dy) if coords in coastline or coords not in self.production_builder.plan or self.production_builder.plan[coords][0] != BUILDING_PURPOSE.NONE: alignment += 1 value = useful_area + alignment * self.personality.alignment_coefficient options.append((value, builder)) if options: return self.production_builder.build_best_option(options, BUILDING_PURPOSE.STORAGE) # enlarge the settlement area instead since just enlarging the collector area is impossible if self.village_builder.tent_queue: tent_size = Entities.buildings[BUILDINGS.RESIDENTIAL].size tent_radius = Entities.buildings[BUILDINGS.RESIDENTIAL].radius best_coords = None best_area = 0 for x, y in self.village_builder.tent_queue: new_area = 0 for coords in Rect.init_from_topleft_and_size(x, y, tent_size[0], tent_size[1]).get_radius_coordinates(tent_radius): if coords in area_label and coords not in self.land_manager.roads and coords not in collector_area: new_area += 1 if new_area > best_area: best_coords = (x, y) best_area = new_area if best_coords is not None: return self.village_builder.extend_settlement_with_tent(Rect.init_from_topleft_and_size_tuples(best_coords, tent_size)) return BUILD_RESULT.IMPOSSIBLE