Beispiel #1
0
	def execute(self):
		# TODO Add a check that figures out if all trees that should be planted are in range of the settlement.
		# If not, return range missing result
		(result, building) = super(LumberjackEvaluator, self).execute()
		if result != BUILD_RESULT.OK:
			return (result, None)

		production_builder = self.area_builder
		coastline = production_builder.land_manager.coastline
		island_ground_map = production_builder.island.ground_map
		forest_coords_list = []
		for coords in building.position.get_radius_coordinates(Entities.buildings[BUILDINGS.LUMBERJACK].radius):
			if coords in production_builder.plan and production_builder.plan[coords][0] == BUILDING_PURPOSE.NONE and coords not in coastline:
				if island_ground_map[coords].object is not None and island_ground_map[coords].object.id == BUILDINGS.TREE:
					forest_coords_list.append(coords)
				elif island_ground_map[coords].settlement is not None and island_ground_map[coords].settlement.owner is self.area_builder.owner:
					builder = BasicBuilder(BUILDINGS.TREE, coords, 0)
					if not builder.have_resources(production_builder.land_manager):
						break
					if builder:
						assert builder.execute(production_builder.land_manager)
						forest_coords_list.append(coords)

		production_builder.register_change_list(forest_coords_list, BUILDING_PURPOSE.TREE, None)

		return (BUILD_RESULT.OK, building)
	def execute(self):
		(result, building) = super(LumberjackEvaluator, self).execute()
		if result != BUILD_RESULT.OK:
			return (result, None)

		production_builder = self.area_builder
		coastline = production_builder.land_manager.coastline
		island_ground_map = production_builder.island.ground_map
		forest_coords_list = []
		for coords in building.position.get_radius_coordinates(Entities.buildings[BUILDINGS.LUMBERJACK].radius):
			if coords in production_builder.plan and production_builder.plan[coords][0] == BUILDING_PURPOSE.NONE and coords not in coastline:
				ok = False
				if island_ground_map[coords].object is not None and island_ground_map[coords].object.id == BUILDINGS.TREE:
					ok = True
				else:
					builder = BasicBuilder(BUILDINGS.TREE, coords, 0)
					if not builder.have_resources(production_builder.land_manager):
						break
					if builder:
						assert builder.execute(production_builder.land_manager)
						ok = True
				if ok:
					forest_coords_list.append(coords)

		production_builder.register_change_list(forest_coords_list, BUILDING_PURPOSE.TREE, None)

		return (BUILD_RESULT.OK, building)
	def _reached_destination_area(self):
		self.log.info('%s reached BO area', self)

		builder = BasicBuilder(BUILDINGS.WAREHOUSE, self.coords, 0)
		self.warehouse = builder.execute(self.land_manager, ship=self.ship)
		if not self.warehouse:
			self.report_failure('Unable to build the warehouse')
			return

		self.land_manager.settlement = self.warehouse.settlement
		self.log.info('%s built the warehouse', self)

		self._unload_all_resources(self.land_manager.settlement)
		self.report_success('Built the warehouse, transferred resources')
	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)
Beispiel #5
0
	def create(cls, area_builder, x, y, new_field_purpose):
		building_id = None
		if new_field_purpose == BUILDING_PURPOSE.POTATO_FIELD:
			building_id = BUILDINGS.POTATO_FIELD
		elif new_field_purpose == BUILDING_PURPOSE.PASTURE:
			building_id = BUILDINGS.PASTURE
		elif new_field_purpose == BUILDING_PURPOSE.SUGARCANE_FIELD:
			building_id = BUILDINGS.SUGARCANE_FIELD
		elif new_field_purpose == BUILDING_PURPOSE.TOBACCO_FIELD:
			building_id = BUILDINGS.TOBACCO_FIELD

		value = 0
		personality = area_builder.owner.personality_manager.get('ModifiedFieldEvaluator')
		if new_field_purpose == BUILDING_PURPOSE.POTATO_FIELD:
			value += personality.add_potato_field_value
		elif new_field_purpose == BUILDING_PURPOSE.PASTURE:
			value += personality.add_pasture_value
		elif new_field_purpose == BUILDING_PURPOSE.SUGARCANE_FIELD:
			value += personality.add_sugarcane_field_value
		elif new_field_purpose == BUILDING_PURPOSE.TOBACCO_FIELD:
			value += personality.add_tobacco_field_value

		old_field_purpose = area_builder.plan[(x, y)][0]
		if old_field_purpose == BUILDING_PURPOSE.POTATO_FIELD:
			value -= personality.remove_unused_potato_field_penalty
		elif old_field_purpose == BUILDING_PURPOSE.PASTURE:
			value -= personality.remove_unused_pasture_penalty
		elif old_field_purpose == BUILDING_PURPOSE.SUGARCANE_FIELD:
			value -= personality.remove_unused_sugarcane_field_penalty
		elif old_field_purpose == BUILDING_PURPOSE.TOBACCO_FIELD:
			value -= personality.remove_unused_tobacco_field_penalty

		builder = BasicBuilder.create(building_id, (x, y), 0)
		return ModifiedFieldEvaluator(area_builder, builder, value, old_field_purpose)
Beispiel #6
0
	def create(cls, area_builder, x, y, orientation):
		coords = (x, y)
		rect_rect_distance_func = distances.distance_rect_rect
		builder = BasicBuilder.create(BUILDINGS.FISHER, coords, orientation)

		shallow_water_body = area_builder.session.world.shallow_water_body
		fisher_shallow_water_body_ids = set()
		for fisher_coords in builder.position.tuple_iter():
			if fisher_coords in shallow_water_body:
				fisher_shallow_water_body_ids.add(shallow_water_body[fisher_coords])
		fisher_shallow_water_body_ids = list(fisher_shallow_water_body_ids)
		assert fisher_shallow_water_body_ids

		tiles_used = 0
		fish_value = 0.0
		last_usable_tick = Scheduler().cur_tick - 60 * GAME_SPEED.TICKS_PER_SECOND # TODO: use a direct calculation
		for fish in area_builder.session.world.fish_indexer.get_buildings_in_range(coords):
			if shallow_water_body[fish.position.origin.to_tuple()] not in fisher_shallow_water_body_ids:
				continue # not in the same shallow water body as the fisher => unreachable
			if fish.last_usage_tick > last_usable_tick:
				continue # the fish deposit seems to be already in use

			distance = rect_rect_distance_func(builder.position, fish.position) + 1.0
			if tiles_used >= cls.refill_cycle_in_tiles:
				fish_value += min(1.0, (3 * cls.refill_cycle_in_tiles - tiles_used) / distance) / 10.0
			else:
				fish_value += min(1.0, (cls.refill_cycle_in_tiles - tiles_used) / distance)

			tiles_used += distance
			if tiles_used >= 3 * cls.refill_cycle_in_tiles:
				break

		if fish_value < 1.5:
			return None
		return FisherEvaluator(area_builder, builder, fish_value)
Beispiel #7
0
	def create(cls, area_builder, x, y, orientation):
		builder = BasicBuilder.create(BUILDINGS.WEAVER, (x, y), orientation)

		distance_to_farm = None
		for building in area_builder.settlement.buildings_by_id.get(BUILDINGS.FARM, []):
			distance = builder.position.distance(building.position)
			if distance <= Entities.buildings[BUILDINGS.WEAVER].radius:
				wool_producer = False
				for provider in building.get_providers():
					if isinstance(provider, Entities.buildings[BUILDINGS.PASTURE]):
						wool_producer = True
						break
				if wool_producer:
					distance_to_farm = distance if distance_to_farm is None or distance < distance_to_farm else distance_to_farm

		distance_to_collector = cls._distance_to_nearest_collector(area_builder, builder)
		if distance_to_collector is None:
			return None # require weavers to have a collector building in range

		personality = area_builder.owner.personality_manager.get('WeaverEvaluator')
		distance_penalty = Entities.buildings[BUILDINGS.WEAVER].radius * personality.distance_penalty
		alignment = cls._get_alignment(area_builder, builder.position.tuple_iter())
		distance = cls._weighted_distance(distance_to_collector, [(personality.farm_distance_importance, distance_to_farm)], distance_penalty)
		value = float(Entities.buildings[BUILDINGS.WEAVER].radius) / distance + alignment * personality.alignment_importance
		return WeaverEvaluator(area_builder, builder, value)
Beispiel #8
0
	def _reached_destination_area(self):
		self.log.info('%s reached BO area', self)

		builder = BasicBuilder(BUILDINGS.WAREHOUSE, self.coords, 0)
		if not builder.have_resources(self.land_manager, ship=self.ship):
			self.report_failure('Not enough resources for a warehouse at %s' % str(self.coords))
			return

		self.warehouse = builder.execute(self.land_manager, ship=self.ship)
		assert self.warehouse

		self.land_manager.settlement = self.warehouse.settlement
		self.log.info('%s built the warehouse', self)

		self._unload_all_resources(self.land_manager.settlement)
		self.report_success('Built the warehouse, transferred resources')
    def create(cls, area_builder, x, y, orientation):
        builder = BasicBuilder.create(BUILDINGS.SMELTERY, (x, y), orientation)

        distance_to_iron_mine = cls._distance_to_nearest_building(area_builder, builder, BUILDINGS.MINE)
        distance_to_collector = cls._distance_to_nearest_collector(area_builder, builder)
        distance_to_charcoal_burner = cls._distance_to_nearest_building(
            area_builder, builder, BUILDINGS.CHARCOAL_BURNER
        )
        if distance_to_collector is None and (distance_to_charcoal_burner is None or distance_to_iron_mine is None):
            return None

        personality = area_builder.owner.personality_manager.get("SmelteryEvaluator")
        distance_penalty = Entities.buildings[BUILDINGS.SMELTERY].radius * personality.distance_penalty

        alignment = cls._get_alignment(area_builder, builder.position.tuple_iter())
        distance = cls._weighted_distance(
            distance_to_iron_mine,
            [
                (personality.collector_distance_importance, distance_to_collector),
                (personality.charcoal_burner_distance_importance, distance_to_charcoal_burner),
            ],
            distance_penalty,
        )
        value = (
            float(Entities.buildings[BUILDINGS.SMELTERY].radius) / distance
            + alignment * personality.alignment_importance
        )
        return SmelteryEvaluator(area_builder, builder, value)
Beispiel #10
0
	def create(cls, area_builder, x, y, orientation):
		# TODO: create a late initialization phase for this kind of stuff
		if cls.__radius_offsets is None:
			cls.__init_outline()

		area_value = 0
		coastline = area_builder.land_manager.coastline
		personality = area_builder.owner.personality_manager.get('LumberjackEvaluator')
		for dx, dy in cls.__radius_offsets:
			coords = (x + dx, y + dy)
			if coords in area_builder.plan and coords not in coastline:
				purpose = area_builder.plan[coords][0]
				if purpose == BUILDING_PURPOSE.NONE:
					area_value += personality.new_tree
				elif purpose == BUILDING_PURPOSE.TREE:
					area_value += personality.shared_tree
		area_value = min(area_value, personality.max_forest_value) # the lumberjack doesn't actually need all the trees
		if area_value < personality.min_forest_value:
			return None # the area is too bad for a lumberjack

		personality = area_builder.owner.personality_manager.get('LumberjackEvaluator')
		alignment = cls._get_alignment_from_outline(area_builder, cls._get_outline(x, y))
		value = area_value + alignment * personality.alignment_importance
		builder = BasicBuilder.create(BUILDINGS.LUMBERJACK, (x, y), orientation)
		return LumberjackEvaluator(area_builder, builder, value)
Beispiel #11
0
	def create(cls, area_builder, x, y, new_field_purpose):
		building_id = {
			BUILDING_PURPOSE.POTATO_FIELD:    BUILDINGS.POTATO_FIELD,
			BUILDING_PURPOSE.PASTURE:         BUILDINGS.PASTURE,
			BUILDING_PURPOSE.SUGARCANE_FIELD: BUILDINGS.SUGARCANE_FIELD,
			BUILDING_PURPOSE.TOBACCO_FIELD:   BUILDINGS.TOBACCO_FIELD,
			BUILDING_PURPOSE.HERBARY:         BUILDINGS.HERBARY,
		}.get(new_field_purpose)

		personality = area_builder.owner.personality_manager.get('ModifiedFieldEvaluator')
		value = {
			BUILDING_PURPOSE.POTATO_FIELD:    personality.add_potato_field_value,
			BUILDING_PURPOSE.PASTURE:         personality.add_pasture_value,
			BUILDING_PURPOSE.SUGARCANE_FIELD: personality.add_sugarcane_field_value,
			BUILDING_PURPOSE.TOBACCO_FIELD:   personality.add_tobacco_field_value,
			BUILDING_PURPOSE.HERBARY:         personality.add_herbary_field_value,
		}.get(new_field_purpose, 0)

		old_field_purpose = area_builder.plan[(x, y)][0]
		value -= {
			BUILDING_PURPOSE.POTATO_FIELD:    personality.remove_unused_potato_field_penalty,
			BUILDING_PURPOSE.PASTURE:         personality.remove_unused_pasture_penalty,
			BUILDING_PURPOSE.SUGARCANE_FIELD: personality.remove_unused_sugarcane_field_penalty,
			BUILDING_PURPOSE.TOBACCO_FIELD:   personality.remove_unused_tobacco_field_penalty,
			BUILDING_PURPOSE.HERBARY:         personality.remove_unused_herbary_field_penalty,
		}.get(old_field_purpose, 0)

		builder = BasicBuilder.create(building_id, (x, y), 0)
		return ModifiedFieldEvaluator(area_builder, builder, value, old_field_purpose)
    def create(cls, area_builder, x, y, orientation):
        builder = BasicBuilder.create(BUILDINGS.DISTILLERY, (x, y), orientation)

        distance_to_farm = None
        for building in area_builder.settlement.buildings_by_id.get(BUILDINGS.FARM, []):
            distance = builder.position.distance(building.position)
            if distance <= Entities.buildings[BUILDINGS.DISTILLERY].radius:
                sugarcane_producer = False
                for provider in building.get_providers():
                    if isinstance(provider, Entities.buildings[BUILDINGS.SUGARCANE_FIELD]):
                        sugarcane_producer = True
                        break
                if sugarcane_producer:
                    distance_to_farm = (
                        distance if distance_to_farm is None or distance < distance_to_farm else distance_to_farm
                    )

        distance_to_collector = cls._distance_to_nearest_collector(area_builder, builder)
        if distance_to_collector is None:
            return None  # require distilleries to have a collector building in range

        personality = area_builder.owner.personality_manager.get("DistilleryEvaluator")
        distance_penalty = Entities.buildings[BUILDINGS.DISTILLERY].radius * personality.distance_penalty

        alignment = cls._get_alignment(area_builder, builder.position.tuple_iter())
        distance = cls._weighted_distance(
            distance_to_collector, [(personality.farm_distance_importance, distance_to_farm)], distance_penalty
        )
        value = (
            float(Entities.buildings[BUILDINGS.DISTILLERY].radius) / distance
            + alignment * personality.alignment_importance
        )
        return DistilleryEvaluator(area_builder, builder, value)
	def create(cls, area_builder, x, y, orientation):
		builder = BasicBuilder.create(BUILDINGS.BOAT_BUILDER, (x, y), orientation)

		distance_to_collector = cls._distance_to_nearest_collector(area_builder, builder)
		if distance_to_collector is None:
			return None # require boat builders to have a collector building in range

		personality = area_builder.owner.personality_manager.get('BoatBuilderEvaluator')
		alignment = cls._get_alignment(area_builder, builder.position.tuple_iter())
		value = float(Entities.buildings[BUILDINGS.BOAT_BUILDER].radius) / distance_to_collector + alignment * personality.alignment_importance
		return BoatBuilderEvaluator(area_builder, builder, value)
	def create(cls, area_builder, x, y, orientation):
		builder = BasicBuilder.create(BUILDINGS.SIGNAL_FIRE, (x, y), orientation)

		sea_area = 0
		for coords in builder.position.get_radius_coordinates(Entities.buildings[BUILDINGS.SIGNAL_FIRE].radius):
			if coords in area_builder.session.world.water:
				sea_area += 1

		personality = area_builder.owner.personality_manager.get('SignalFireEvaluator')
		alignment = cls._get_alignment(area_builder, builder.position.tuple_iter())
		value = sea_area + alignment * personality.alignment_importance
		return SignalFireEvaluator(area_builder, builder, value)
	def create(cls, area_builder, x, y, orientation):
		builder = BasicBuilder.create(BUILDINGS.STONEMASON, (x, y), orientation)
		distance_to_collector = cls._distance_to_nearest_collector(area_builder, builder)
		if distance_to_collector is None:
			return None

		distance_to_stone_pit = cls._distance_to_nearest_building(area_builder, builder, BUILDINGS.STONE_PIT)
		alignment = cls._get_alignment(area_builder, builder.position.tuple_iter())

		personality = area_builder.owner.personality_manager.get('StonemasonEvaluator')
		distance_penalty = Entities.buildings[BUILDINGS.STONEMASON].radius * personality.distance_penalty

		distance = cls._weighted_distance(distance_to_collector, [(personality.stone_pit_distance_importance, distance_to_stone_pit)], distance_penalty)
		value = float(Entities.buildings[BUILDINGS.STONEMASON].radius) / distance + alignment * personality.alignment_importance
		return StonemasonEvaluator(area_builder, builder, value)
Beispiel #16
0
	def create(cls, area_builder, x, y, orientation):
		builder = BasicBuilder.create(BUILDINGS.BRICKYARD, (x, y), orientation)

		distance_to_clay_pit = cls._distance_to_nearest_building(area_builder, builder, BUILDINGS.CLAY_PIT)
		distance_to_collector = cls._distance_to_nearest_collector(area_builder, builder)
		if distance_to_clay_pit is None and distance_to_collector is None:
			return None

		personality = area_builder.owner.personality_manager.get('BrickyardEvaluator')
		distance_penalty = Entities.buildings[BUILDINGS.BRICKYARD].radius * personality.distance_penalty

		alignment = cls._get_alignment(area_builder, builder.position.tuple_iter())
		distance = cls._weighted_distance(distance_to_clay_pit, [(personality.collector_distance_importance, distance_to_collector)], distance_penalty)
		value = float(Entities.buildings[BUILDINGS.BRICKYARD].radius) / distance + alignment * personality.alignment_importance
		return BrickyardEvaluator(area_builder, builder, value)
	def create(cls, production_builder, x, y, orientation):
		settlement_manager = production_builder.settlement_manager
		village_builder = settlement_manager.village_builder
		builder = BasicBuilder.create(BUILDINGS.FIRE_STATION, (x, y), orientation)

		assigned_residences = village_builder.special_building_assignments[BUILDING_PURPOSE.FIRE_STATION][(x, y)]
		total = len(assigned_residences)
		not_serviced = 0
		for residence_coords in assigned_residences:
			if village_builder.plan[residence_coords][0] == BUILDING_PURPOSE.RESIDENCE:
				not_serviced += 1

		if not_serviced <= 0 or not_serviced < total * settlement_manager.owner.personality_manager.get('AbstractFireStation').fraction_of_assigned_residences_built:
			return None

		return FireStationEvaluator(village_builder, builder, not_serviced)
    def _improve_deposit_coverage(self):
        """Get closer to having a resource deposit in the settlement."""
        if not self.production_builder.have_resources(BUILDINGS.STORAGE):
            return BUILD_RESULT.NEED_RESOURCES

        available_deposits = []
        for tile in self.land_manager.resource_deposits[self._deposit_resource_id]:
            if tile.object.settlement is None:
                available_deposits.append(tile.object)
        if not available_deposits:
            return BUILD_RESULT.IMPOSSIBLE

        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,
        )

        options = []
        for coords in sorted(storage_spots):
            builder = BasicBuilder.create(BUILDINGS.STORAGE, coords, 0)

            min_distance = None
            for building in available_deposits:
                distance = building.position.distance(builder.position)
                if min_distance is None or min_distance > distance:
                    min_distance = distance

            alignment = 0
            for tile in self.production_builder.iter_neighbor_tiles(builder.position):
                if tile is None:
                    continue
                coords = (tile.x, tile.y)
                if (
                    coords not in self.production_builder.plan
                    or self.production_builder.plan[coords][0] != BUILDING_PURPOSE.NONE
                ):
                    alignment += 1

            value = min_distance - alignment * self.personality.alignment_coefficient
            options.append((-value, builder))

        return self.production_builder.build_best_option(options, BUILDING_PURPOSE.STORAGE)
	def create(cls, area_builder, x, y, orientation):
		builder = BasicBuilder.create(BUILDINGS.CHARCOAL_BURNER, (x, y), orientation)

		distance_to_collector = cls._distance_to_nearest_collector(area_builder, builder)
		if distance_to_collector is None:
			return None

		personality = area_builder.owner.personality_manager.get('CharcoalBurnerEvaluator')
		distance_penalty = Entities.buildings[BUILDINGS.CHARCOAL_BURNER].radius * personality.distance_penalty

		distance_to_iron_mine = cls._distance_to_nearest_building(area_builder, builder, BUILDINGS.MINE)
		distance_to_lumberjack = cls._distance_to_nearest_building(area_builder, builder, BUILDINGS.LUMBERJACK)
		alignment = cls._get_alignment(area_builder, builder.position.tuple_iter())

		distance = cls._weighted_distance(distance_to_collector, [(personality.lumberjack_distance_importance, distance_to_lumberjack),
			(personality.iron_mine_distance_importance, distance_to_iron_mine)], distance_penalty)
		value = float(Entities.buildings[BUILDINGS.CHARCOAL_BURNER].radius) / distance + alignment * personality.alignment_importance
		return CharcoalBurnerEvaluator(area_builder, builder, value)
Beispiel #20
0
	def create(cls, area_builder, x, y, orientation):
		builder = BasicBuilder.create(BUILDINGS.TOOLMAKER, (x, y), orientation)
		distance_to_collector = cls._distance_to_nearest_collector(area_builder, builder)
		if distance_to_collector is None:
			return None

		distance_to_smeltery = cls._distance_to_nearest_building(area_builder, builder, BUILDINGS.SMELTERY)
		distance_to_charcoal_burner = cls._distance_to_nearest_building(area_builder, builder, BUILDINGS.CHARCOAL_BURNER)
		distance_to_lumberjack = cls._distance_to_nearest_building(area_builder, builder, BUILDINGS.LUMBERJACK)
		alignment = cls._get_alignment(area_builder, builder.position.tuple_iter())

		personality = area_builder.owner.personality_manager.get('ToolmakerEvaluator')
		distance_penalty = Entities.buildings[BUILDINGS.TOOLMAKER].radius * personality.distance_penalty

		distance = cls._weighted_distance(distance_to_collector, [(personality.smeltery_distance_importance, distance_to_smeltery),
			(personality.charcoal_burner_distance_importance, distance_to_charcoal_burner), (personality.lumberjack_distance_importance, distance_to_lumberjack)],
			distance_penalty)
		value = float(Entities.buildings[BUILDINGS.TOOLMAKER].radius) / distance + alignment * personality.alignment_importance
		return ToolmakerEvaluator(area_builder, builder, value)
Beispiel #21
0
	def create(cls, area_builder, x, y, orientation):
		builder = BasicBuilder.create(BUILDINGS.STONE_PIT, (x, y), orientation)
		distance_to_collector = cls._distance_to_nearest_collector(area_builder, builder, False)
		value = 1.0 / (distance_to_collector + 1)
		return StonePitEvaluator(area_builder, builder, value)
Beispiel #22
0
 def create(cls, area_builder, x, y, orientation):
     builder = BasicBuilder.create(BUILDINGS.CLAY_PIT, (x, y), orientation)
     distance_to_collector = cls._distance_to_nearest_collector(area_builder, builder, False)
     value = 1.0 / (distance_to_collector + 1)
     return ClayPitEvaluator(area_builder, builder, value)
	def create(cls, area_builder, x, y, orientation):
		builder = BasicBuilder.create(BUILDINGS.SALT_PONDS, (x, y), orientation)
		alignment = cls._get_alignment(area_builder, builder.position.tuple_iter())
		return SaltPondsEvaluator(area_builder, builder, alignment)
	def _build_extra_storage(self):
		"""Build an extra storage tent to improve collector coverage."""
		if not self.production_builder.have_resources(BUILDINGS.STORAGE):
			return BUILD_RESULT.NEED_RESOURCES

		reachable = dict.fromkeys(self.land_manager.roads) # {(x, y): [(building worldid, distance), ...], ...}
		for coords, (purpose, _) in self.production_builder.plan.iteritems():
			if purpose == BUILDING_PURPOSE.NONE:
				reachable[coords] = []
		for key in reachable:
			if reachable[key] is None:
				reachable[key] = []

		storage_radius = Entities.buildings[BUILDINGS.STORAGE].radius
		moves = [(-1, 0), (0, -1), (0, 1), (1, 0)]
		for building in self._problematic_buildings:
			distance = dict.fromkeys(reachable)
			queue = deque()
			for coords in self.production_builder.iter_possible_road_coords(building.loading_area, building.position):
				if coords in distance:
					distance[coords] = 0
					queue.append(coords)

			while queue:
				x, y = queue[0]
				queue.popleft()
				for dx, dy in moves:
					coords2 = (x + dx, y + dy)
					if coords2 in distance and distance[coords2] is None:
						distance[coords2] = distance[(x, y)] + 1
						queue.append(coords2)

			for coords, dist in distance.iteritems():
				if dist is not None:
					if building.loading_area.distance(coords) <= storage_radius:
						reachable[coords].append((building.worldid, dist))

		options = []
		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)
		for coords, building_distances in reachable.iteritems():
			if coords not in storage_spots:
				continue
			builder = BasicBuilder.create(BUILDINGS.STORAGE, coords, 0)

			actual_distance = {}
			for coords in builder.position.tuple_iter():
				for building_worldid, distance in reachable[coords]:
					if building_worldid not in actual_distance or actual_distance[building_worldid] > distance:
						actual_distance[building_worldid] = distance
			if not actual_distance:
				continue

			usefulness = min(len(actual_distance), self.personality.max_reasonably_served_buildings)
			for distance in actual_distance.itervalues():
				usefulness += 1.0 / (distance + self.personality.collector_extra_distance)

			alignment = 1
			for tile in self.production_builder.iter_neighbor_tiles(builder.position):
				coords = (tile.x, tile.y)
				if coords not in self.production_builder.plan or self.production_builder.plan[coords][0] != BUILDING_PURPOSE.NONE:
					alignment += 1

			value = usefulness + alignment * self.personality.alignment_coefficient
			options.append((value, builder))

		return self.production_builder.build_best_option(options, BUILDING_PURPOSE.STORAGE)
	def create(cls, area_builder, x, y, orientation):
		builder = BasicBuilder.create(BUILDINGS.SALT_PONDS, (x, y), orientation)
		alignment = cls._get_alignment(area_builder, builder.position.tuple_iter())
		return SaltPondsEvaluator(area_builder, builder, alignment)
Beispiel #26
0
	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
Beispiel #27
0
	def create(cls, area_builder, x, y, orientation):
		builder = BasicBuilder.create(BUILDINGS.MINE, (x, y), orientation)
		return IronMineEvaluator(area_builder, builder, 0)
	def create(cls, area_builder, x, y, orientation):
		builder = BasicBuilder.create(BUILDINGS.MINE, (x, y), orientation)
		return IronMineEvaluator(area_builder, builder, 0)
Beispiel #29
0
    def create(cls, area_builder, farm_x, farm_y, road_dx, road_dy, min_fields,
               field_purpose, field_spots_set, road_spots_set,
               positive_alignment):
        farm_plan = {}

        # place the farm area road
        existing_roads = 0
        for other_offset in xrange(-3, 6):
            coords = None
            if road_dx == 0:
                coords = (farm_x + other_offset, farm_y + road_dy)
            else:
                coords = (farm_x + road_dx, farm_y + other_offset)
            assert coords in road_spots_set

            farm_plan[coords] = BUILDING_PURPOSE.ROAD
            if coords in area_builder.land_manager.roads:
                existing_roads += 1

        # place the fields
        fields = 0
        for (dx, dy) in cls.__field_offsets:
            if fields >= 8:
                break  # unable to place more anyway
            coords = (farm_x + dx, farm_y + dy)
            if coords not in field_spots_set:
                continue

            field_fits = True
            for (fdx, fdy) in cls.__field_pos_offsets:
                coords2 = (coords[0] + fdx, coords[1] + fdy)
                if coords2 in farm_plan:
                    field_fits = False
                    break
            if not field_fits:
                continue  # some part of the area is reserved for something else

            fields += 1
            for (fdx, fdy) in cls.__field_pos_offsets:
                coords2 = (coords[0] + fdx, coords[1] + fdy)
                farm_plan[coords2] = BUILDING_PURPOSE.RESERVED
            farm_plan[coords] = field_purpose
        if fields < min_fields:
            return None  # go for the most fields possible

        # add the farm itself to the plan
        builder = BasicBuilder.create(BUILDINGS.FARM, (farm_x, farm_y), 0)
        for coords in builder.position.tuple_iter():
            farm_plan[coords] = BUILDING_PURPOSE.RESERVED
        farm_plan[(farm_x, farm_y)] = BUILDING_PURPOSE.FARM

        # calculate the alignment value and the rectangle that contains the whole farm
        alignment = 0
        min_x, max_x, min_y, max_y = None, None, None, None
        for x, y in farm_plan:
            min_x = x if min_x is None or min_x > x else min_x
            max_x = x if max_x is None or max_x < x else max_x
            min_y = y if min_y is None or min_y > y else min_y
            max_y = y if max_y is None or max_y < y else max_y

            for dx, dy in cls.__moves:
                coords = (x + dx, y + dy)
                if coords not in farm_plan and coords in positive_alignment:
                    alignment += 1

        # calculate the value of the farm road end points (larger is better)
        personality = area_builder.owner.personality_manager.get(
            'FarmEvaluator')
        immediate_connections = 0
        for other_offset in [-4, 6]:
            if road_dx == 0:
                coords = (farm_x + other_offset, farm_y + road_dy)
            else:
                coords = (farm_x + road_dx, farm_y + other_offset)
            if coords in area_builder.land_manager.roads:
                immediate_connections += personality.immediate_connection_road
            elif coords in area_builder.plan:
                if area_builder.plan[coords][0] == BUILDING_PURPOSE.NONE:
                    immediate_connections += personality.immediate_connection_free

        extra_space = (max_x - min_x + 1) * (max_y - min_y + 1) - 9 * (fields +
                                                                       2)
        value = fields + existing_roads * personality.existing_road_importance + \
         alignment * personality.alignment_importance - extra_space * personality.wasted_space_penalty + \
         immediate_connections * personality.immediate_connection_importance
        return FarmEvaluator(area_builder, builder, value, farm_plan, fields,
                             field_purpose)
Beispiel #30
0
	def create(cls, area_builder, farm_x, farm_y, road_dx, road_dy, min_fields, field_purpose, field_spots_set, road_spots_set, positive_alignment):
		farm_plan = {}

		# place the farm area road
		existing_roads = 0
		for other_offset in xrange(-3, 6):
			coords = None
			if road_dx == 0:
				coords = (farm_x + other_offset, farm_y + road_dy)
			else:
				coords = (farm_x + road_dx, farm_y + other_offset)
			assert coords in road_spots_set

			farm_plan[coords] = BUILDING_PURPOSE.ROAD
			if coords in area_builder.land_manager.roads:
				existing_roads += 1

		# place the fields
		fields = 0
		for (dx, dy) in cls.__field_offsets:
			if fields >= 8:
				break # unable to place more anyway
			coords = (farm_x + dx, farm_y + dy)
			if coords not in field_spots_set:
				continue

			field_fits = True
			for (fdx, fdy) in cls.__field_pos_offsets:
				coords2 = (coords[0] + fdx, coords[1] + fdy)
				if coords2 in farm_plan:
					field_fits = False
					break
			if not field_fits:
				continue # some part of the area is reserved for something else

			fields += 1
			for (fdx, fdy) in cls.__field_pos_offsets:
				coords2 = (coords[0] + fdx, coords[1] + fdy)
				farm_plan[coords2] = BUILDING_PURPOSE.RESERVED
			farm_plan[coords] = field_purpose
		if fields < min_fields:
			return None # go for the most fields possible

		# add the farm itself to the plan
		builder = BasicBuilder.create(BUILDINGS.FARM, (farm_x, farm_y), 0)
		for coords in builder.position.tuple_iter():
			farm_plan[coords] = BUILDING_PURPOSE.RESERVED
		farm_plan[(farm_x, farm_y)] = BUILDING_PURPOSE.FARM

		# calculate the alignment value and the rectangle that contains the whole farm
		alignment = 0
		min_x, max_x, min_y, max_y = None, None, None, None
		for x, y in farm_plan:
			min_x = x if min_x is None or min_x > x else min_x
			max_x = x if max_x is None or max_x < x else max_x
			min_y = y if min_y is None or min_y > y else min_y
			max_y = y if max_y is None or max_y < y else max_y

			for dx, dy in cls.__moves:
				coords = (x + dx, y + dy)
				if coords not in farm_plan and coords in positive_alignment:
					alignment += 1

		# calculate the value of the farm road end points (larger is better)
		personality = area_builder.owner.personality_manager.get('FarmEvaluator')
		immediate_connections = 0
		for other_offset in [-4, 6]:
			if road_dx == 0:
				coords = (farm_x + other_offset, farm_y + road_dy)
			else:
				coords = (farm_x + road_dx, farm_y + other_offset)
			if coords in area_builder.land_manager.roads:
				immediate_connections += personality.immediate_connection_road
			elif coords in area_builder.plan:
				if area_builder.plan[coords][0] == BUILDING_PURPOSE.NONE:
					immediate_connections += personality.immediate_connection_free

		extra_space = (max_x - min_x + 1) * (max_y - min_y + 1) - 9 * (fields + 2)
		value = fields + existing_roads * personality.existing_road_importance + \
			alignment * personality.alignment_importance - extra_space * personality.wasted_space_penalty + \
			immediate_connections * personality.immediate_connection_importance
		return FarmEvaluator(area_builder, builder, value, farm_plan, fields, field_purpose)
	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
Beispiel #32
0
    def _build_extra_storage(self):
        """Build an extra storage tent to improve collector coverage."""
        if not self.production_builder.have_resources(BUILDINGS.STORAGE):
            return BUILD_RESULT.NEED_RESOURCES

        reachable = dict.fromkeys(
            self.land_manager.roads
        )  # {(x, y): [(building worldid, distance), ...], ...}
        for coords, (purpose, _) in self.production_builder.plan.items():
            if purpose == BUILDING_PURPOSE.NONE:
                reachable[coords] = []
        for key in reachable:
            if reachable[key] is None:
                reachable[key] = []

        storage_radius = Entities.buildings[BUILDINGS.STORAGE].radius
        moves = [(-1, 0), (0, -1), (0, 1), (1, 0)]
        for building in self._problematic_buildings:
            distance = dict.fromkeys(reachable)
            queue = deque()
            for coords in self.production_builder.iter_possible_road_coords(
                    building.loading_area, building.position):
                if coords in distance:
                    distance[coords] = 0
                    queue.append(coords)

            while queue:
                x, y = queue.popleft()
                for dx, dy in moves:
                    coords2 = (x + dx, y + dy)
                    if coords2 in distance and distance[coords2] is None:
                        distance[coords2] = distance[(x, y)] + 1
                        queue.append(coords2)

            for coords, dist in distance.items():
                if dist is not None:
                    if building.loading_area.distance(
                            coords) <= storage_radius:
                        reachable[coords].append((building.worldid, dist))

        options = []
        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)
        for coords, building_distances in reachable.items():
            if coords not in storage_spots:
                continue
            builder = BasicBuilder.create(BUILDINGS.STORAGE, coords, 0)

            actual_distance = {}
            for coords in builder.position.tuple_iter():
                for building_worldid, distance in reachable[coords]:
                    if building_worldid not in actual_distance or actual_distance[
                            building_worldid] > distance:
                        actual_distance[building_worldid] = distance
            if not actual_distance:
                continue

            usefulness = min(len(actual_distance),
                             self.personality.max_reasonably_served_buildings)
            for distance in actual_distance.values():
                usefulness += 1.0 / (distance +
                                     self.personality.collector_extra_distance)

            alignment = 1
            for tile in self.production_builder.iter_neighbor_tiles(
                    builder.position):
                coords = (tile.x, tile.y)
                if coords not in self.production_builder.plan or self.production_builder.plan[
                        coords][0] != BUILDING_PURPOSE.NONE:
                    alignment += 1

            value = usefulness + alignment * self.personality.alignment_coefficient
            options.append((value, builder))

        return self.production_builder.build_best_option(
            options, BUILDING_PURPOSE.STORAGE)