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, ")")
def __destroy_defense(self, levels): ########################################################################### check_access(self.ALLOW_DESTROY_DEFENSE) prequire(self.defense() >= levels, "Invalid destroy levels: ", levels) self.__defense -= levels
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, ")")
def __remove_city_impl_world(self, city): ########################################################################### check_access(self.ALLOW_REMOVE_CITY) self.__cities.remove(city) self.tile(city.location()).remove_city()
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())
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)
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, ")")
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, ")")
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
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)
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)
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
def __learn_impl(self, name): ########################################################################### check_access(self.ALLOW_LEARN) self.__talents.add(name)
def __set_wind_impl(self, new_wind): ########################################################################### check_access(self.ALLOW_SET_WIND) self.__wind = new_wind
def __set_temperature_impl(self, new_temperature): ########################################################################### check_access(self.ALLOW_SET_TEMPERATURE) self.__temperature = new_temperature