Beispiel #1
0
    def __gain_exp_impl(self, exp):
        ###########################################################################
        check_access(self.ALLOW_GAIN_EXP)

        self.__exp += exp

        # Check for level-up
        while (self.__exp >= self.__next_level_cost):
            self.__level += 1

            old_max = self.__max_mana
            self.__max_mana = self.__MANA_POOL_FUNC(self.level())
            self.__mana_regen = self.__max_mana * self.__MANA_REGEN_RATE
            increase = self.__max_mana - old_max
            self.__mana += increase

            self.__exp -= self.__next_level_cost
            self.__next_level_cost = self.__EXP_LEVEL_COST_FUNC(self.level())

        # Check exp invariant
        prequire(self.__exp < self.__next_level_cost, "exp(", self.__exp,
                 ") > ", "next_level_cost(", self.__next_level_cost, ")")

        # Check mana invariant
        prequire(self.__mana <= self.__max_mana, "mana(", self.__mana,
                 ") > max_mana(", self.__max_mana, ")")
Beispiel #2
0
    def __destroy_defense(self, levels):
        ###########################################################################
        check_access(self.ALLOW_DESTROY_DEFENSE)

        prequire(self.defense() >= levels, "Invalid destroy levels: ", levels)

        self.__defense -= levels
Beispiel #3
0
    def __cycle_turn_impl(self):
        ###########################################################################
        check_access(self.ALLOW_CYCLE_TURN)

        cls = self.__class__
        world = engine().world()
        cities_orig = list(world.cities())

        # Manage cities. Note that this may cause additional cities to
        # be created, which is why we needed the list copy above
        for city in cities_orig:
            city.cycle_turn()

        # Adjust tech based on population
        tech_points = cls.__TECH_POINT_FUNC(self.population())
        self.__tech_points += tech_points

        # Check if level-up in tech
        while (self.__tech_points >= self.__next_tech_level_cost):
            self.__tech_level += 1
            self.__tech_points -= self.__next_tech_level_cost  # rollover points
            self.__next_tech_level_cost = \
                cls.__TECH_NEXT_LEVEL_COST_FUNC(self.tech_level())

        # Tech invariants
        prequire(self.__tech_points < self.__next_tech_level_cost,
                 "Expect tech-points(", self.__tech_points, ") < tech-cost(",
                 self.__next_tech_level_cost, ")")
Beispiel #4
0
    def __remove_city_impl_world(self, city):
    ###########################################################################
        check_access(self.ALLOW_REMOVE_CITY)

        self.__cities.remove(city)

        self.tile(city.location()).remove_city()
Beispiel #5
0
    def __cycle_turn_impl(self):
    ###########################################################################
        check_access(self.ALLOW_CYCLE_TURN)

        # Phase 1: Increment time
        self.__time.next()

        # Phase 2: Generate anomalies.
        # TODO: How to handle overlapping anomalies of same category?
        self.__recent_anomalies = []
        for row in self.__rows:
            for tile in row:
                for anomaly_category in AnomalyCategory:
                    anomaly = Anomaly.generate_anomaly(anomaly_category,
                                                       tile.location())
                    if (anomaly is not None):
                        self.__recent_anomalies.append(anomaly)

        # Phase 3 of World turn-cycle: Simulate the inter-turn
        # (long-term) weather Every turn, the weather since the last
        # turn will be randomly simulated. There will be random
        # abnormal areas, with the epicenter of the abnormality having
        # the most extreme deviations from the normal climate and
        # peripheral tiles having smaller deviations from normal.
        # Abnormalilty types are: drought, moist, cold, hot, high/low
        # pressure. Anomalies affect this season.
        for row in self.__rows:
            for tile in row:
                tile.cycle_turn(self.__recent_anomalies,
                                self.__time.season())
Beispiel #6
0
    def __cycle_turn_impl(self):
    ###########################################################################
        check_access(self.ALLOW_CYCLE_TURN)

        # Tension/magma build up more slowly as they reach 100%
        self.__tension += (1 - self.__tension) * self.__tension_buildup
        self.__magma   += (1 - self.__magma)   * self.__magma_buildup

        prequire(self.__tension < 1.0, "Invariant violated: ", self.__tension)
        prequire(self.__magma   < 1.0, "Invariant violated: ", self.__magma)
Beispiel #7
0
    def __cast_impl(self, spell):
        ###########################################################################
        check_access(self.ALLOW_CAST)

        self.__mana -= spell.cost()

        # Maintain mana invariant (m_mana is unsigned, so going below zero will
        # cause it to become enormous).
        prequire(self.__mana <= self.__max_mana, "mana(", self.__mana,
                 ") > max_mana(", self.__max_mana, ")")
Beispiel #8
0
    def __cycle_turn_impl(self):
        ###########################################################################
        check_access(self.ALLOW_CYCLE_TURN)

        self.__mana += self.__mana_regen
        if (self.__mana > self.__max_mana):
            self.__mana = self.__max_mana

        # Maintain mana invariant
        prequire(self.__mana <= self.__max_mana, "m_mana(", self.__mana,
                 ") > m_max_mana(", self.__max_mana, ")")
Beispiel #9
0
    def __kill_impl(self, killed):
        ###########################################################################
        check_access(self.ALLOW_KILL)

        prequire(self.__population >= killed, "Invalid killed: ", killed)

        self.__population -= killed

        while (self.__population <
               self.__next_rank_pop / City._CITY_RANK_UP_MULTIPLIER
               and self.__rank > 1):
            self.__rank -= 1
            self.__next_rank_pop /= City._CITY_RANK_UP_MULTIPLIER
Beispiel #10
0
    def __place_city_impl_world(self, location, name):
    ###########################################################################
        check_access(self.ALLOW_PLACE_CITY)

        if (name is None):
            name = "City %d" % len(self.__cities)

        city = City(name, location)

        self.__cities.append(city)

        tile = self.tile(location)
        tile.place_city(city)
Beispiel #11
0
    def __cycle_turn_impl(self, anomalies, location, season):
        ###########################################################################
        check_access(self.ALLOW_CYCLE_TURN)

        # Gather all modifiers from all anomalies
        precip_modifier = 1.0
        temp_modifier = 0
        pressure_modifier = 0
        for anomaly in anomalies:
            precip_modifier *= anomaly.precip_effect(location)
            temp_modifier += anomaly.temperature_effect(location)
            pressure_modifier += anomaly.pressure_effect(location)

        self.__temperature = self.__climate.temperature(season) + temp_modifier
        self.__pressure = self.NORMAL_PRESSURE + pressure_modifier
        self.__precip = self.__climate.precip(season) * precip_modifier

        self.__dewpoint = self._compute_dewpoint()

        # TODO: Need to compute wind speed changes due to pressure
        self.__wind = self.__climate.wind(season)
Beispiel #12
0
    def __cycle_turn_impl(self):
        ###########################################################################
        # TODO: This method is still too big, break it up more

        check_access(self.ALLOW_CYCLE_TURN)

        prequire(self.__population > 0,
                 "This city has no people and should have been deleted")

        world = engine().world()
        my_row, my_col = self.__location.unpack()

        # Gather resources based on nearby worked tiles. At this time,
        # cities will only be able to harvest adjacent tiles.

        # Evaluate nearby tiles, put in to sorted lists
        # (best-to-worst) for each of the two yield types
        food_tiles, prod_tiles = \
            _compute_nearby_food_and_prod_tiles(self.__location)

        # Choose which tiles to work. We first make sure we have ample
        # food, then we look for production tiles.

        req_food = self.__population / City._POP_THAT_EATS_ONE_FOOD
        food_gathered = City._FOOD_FROM_CITY_CENTER
        prod_gathered = City._PROD_FROM_CITY_CENTER
        num_workers = self.__rank
        num_workers_used_for_food = 0
        for tile in food_tiles:
            if (num_workers == 0): break

            if (food_gathered < req_food):
                tile.work()
                num_workers -= 1
                num_workers_used_for_food += 1
                food_gathered += tile.yield_().food
            else:
                # TODO: AI should continue to pile up food to increase
                # it's growth rate unless it's already hit the max
                # growth rate or if there are good production tiles
                # available or it really needs production.
                break

        # TODO: If city is getting close to being food capped, do
        # not sacrafice good production tiles for marginal food
        # tiles.
        for tile in prod_tiles:
            if (num_workers == 0): break

            if (tile.yield_().prod > City._PROD_FROM_SPECIALIST):
                tile.work()
                num_workers -= 1
                prod_gathered += tile.yield_().prod

        # Remaining workers are specialists that contribute production
        base_specialist_prod = num_workers * City._PROD_FROM_SPECIALIST
        prod_gathered += \
            engine().ai_player().get_adjusted_yield(base_specialist_prod)

        # Accumulate production
        self.__prod_bank += prod_gathered

        # Decide on how to spend production. Options: City
        # fortifications, tile infrastructure, or settler. In the
        # current system, the AI builds general production points and
        # it can use them to insta-buy the things it wants. This is a
        # bit unrealistic and should probably be replaced with a
        # system where the AI chooses to start building something and
        # all production points go to that thing until it is finished
        # (a la civ).

        # First, we need to decide what production item we should be
        # saving up for. Note that the AI will *not* build a cheaper
        # item that is of lower priority if it cannot afford the
        # highest priority item. It will always save-up for the
        # highest priority.

        # We want some of our workers doing something other than just
        # collecting food. If we are having to dedicate our workforce
        # to food, we probably need better food infrastructure. We
        # need to verify that there are nearby food tiles that we can
        # enhance.
        food_tile = None

        pct_workers_on_food = num_workers_used_for_food / self.__rank
        if (pct_workers_on_food > City._TOO_MANY_FOOD_WORKERS
                or food_gathered < req_food):
            for tile in food_tiles:
                if (tile.can_build_infra()):
                    food_tile = tile
                    break

        # We want a healthy level of production from this city if
        # possible. We need to verify that there are nearby production
        # tiles that we can enhance.
        prod_tile = None

        if (prod_gathered < City._PROD_BEFORE_SETTLER):
            for tile in prod_tiles:
                if (tile.can_build_infra()):
                    prod_tile = tile
                    break

        # We want to expand with settlers once a city has become large enough.
        # Check if building a settler is appropriate. New cities must be
        # "adjacent" to the city that created the settler.
        settler_loc = None
        max_distance = 3
        min_distance = 2
        heuristic_of_best_loc_so_far = 0.0
        for row_delta in xrange(-max_distance, max_distance + 1):
            for col_delta in xrange(-max_distance, max_distance + 1):
                loc = Location(my_row + row_delta, my_col + col_delta)

                # Check if this is a valid city loc
                if (world.in_bounds(loc) and world.tile(loc).supports_city()
                        and
                        not _is_too_close_to_any_city(loc, min_distance - 1)):

                    heuristic = _compute_city_loc_heuristic(loc)
                    if (heuristic > heuristic_of_best_loc_so_far):
                        settler_loc = loc
                        heuristic_of_best_loc_so_far = heuristic

        # We want a high level of production from this city if
        # possible. We need to verify that there are nearby production
        # tiles that we can enhance.
        prod_tile_fallback = None
        for tile in prod_tiles:
            if (tile.can_build_infra()):
                prod_tile_fallback = tile
                break

        # Now we actually try to build stuff. The order in which we
        # check the bools defines the priorities of the items.
        if (food_tile):
            self.__build_infra(food_tile)
        elif (prod_tile):
            self.__build_infra(prod_tile)
        elif (settler_loc):
            if (self.__prod_bank >= City._SETTLER_PROD_COST):
                world.place_city(settler_loc)
                self.__prod_bank -= City._SETTLER_PROD_COST
        elif (prod_tile_fallback):
            self.__build_infra(prod_tile_fallback)
        else:
            # No settler expansion is possible, build city
            # defenses. This is the lowest priority item to build.
            cost = self.__defense * City._CITY_DEF_PROD_COST
            if (self.__prod_bank >= cost):
                self.__defense += 1
                self.__prod_bank -= cost

        # Handle population growth

        # Compute multiplier, cannot exceed base by factor of > +-max-growth
        if (food_gathered < req_food):
            self.__famine = True
            food_multiplier = -req_food / food_gathered
            if (food_multiplier < -1 * City._MAX_GROWTH_MODIFIER):
                food_multiplier = -1 * City._MAX_GROWTH_MODIFIER
        else:
            self.__famine = False
            food_multiplier = food_gathered / req_food
            if (food_multiplier > City._MAX_GROWTH_MODIFIER):
                food_multiplier = City._MAX_GROWTH_MODIFIER

        # Grow city
        pop_growth_rate = 1 + (food_multiplier * City._CITY_BASE_GROWTH_RATE)
        self.__population *= pop_growth_rate
        if (self.__population > self.__next_rank_pop):
            self.__rank += 1
            self.__next_rank_pop *= City._CITY_RANK_UP_MULTIPLIER
Beispiel #13
0
    def __learn_impl(self, name):
        ###########################################################################
        check_access(self.ALLOW_LEARN)

        self.__talents.add(name)
Beispiel #14
0
 def __set_wind_impl(self, new_wind):
     ###########################################################################
     check_access(self.ALLOW_SET_WIND)
     self.__wind = new_wind
Beispiel #15
0
 def __set_temperature_impl(self, new_temperature):
     ###########################################################################
     check_access(self.ALLOW_SET_TEMPERATURE)
     self.__temperature = new_temperature