Beispiel #1
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 #2
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 #3
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 #4
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.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 #6
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)
	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 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 #12
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 #16
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 #17
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)
    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)
Beispiel #19
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)
	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 #21
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.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 _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 #25
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 range(-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 #26
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 create(cls, area_builder, x, y, orientation):
		builder = BasicBuilder.create(BUILDINGS.MINE, (x, y), orientation)
		return IronMineEvaluator(area_builder, builder, 0)
Beispiel #28
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 #29
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 #30
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)
Beispiel #31
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)