Index: server/settlers.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/settlers.c,v retrieving revision 1.138 diff -u -r1.138 settlers.c --- server/settlers.c 2002/04/04 03:51:05 1.138 +++ server/settlers.c 2002/04/13 16:04:09 @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include "city.h" #include "game.h" @@ -39,18 +41,14 @@ static unsigned int territory[MAP_MAX_WIDTH][MAP_MAX_HEIGHT]; /* negative: in_city_radius, 0: unassigned, positive: city_des */ -/*************************************************************************/ - static int city_desirability(struct player *pplayer, int x, int y); - static void auto_settlers_player(struct player *pplayer); - static bool is_already_assigned(struct unit *myunit, struct player *pplayer, int x, int y); - /************************************************************************** -... + Build a city and initialize AI infrastructure cache. FIXME: Should be + moved into ai/advdomestic.c - Per **************************************************************************/ static bool ai_do_build_city(struct player *pplayer, struct unit *punit) { @@ -77,37 +75,19 @@ } /************************************************************************** -amortize(benefit, delay) returns benefit * ((MORT - 1)/MORT)^delay -(^ = to the power of) + Amortize means gradually paying off a cost or debt over time. In freeciv + terms this means we calculate how much less worth something is to us + depending on how long it will take to complete. -Plus, it has tests to prevent the numbers getting too big. It takes -advantage of the fact that (23/24)^12 approximately = 3/5 to chug through -delay in chunks of 12, and then does the remaining multiplications of (23/24). + amortize(benefit, delay) returns benefit * ((MORT - 1)/MORT)^delay + (^ = to the power of) **************************************************************************/ int amortize(int benefit, int delay) { - int num = MORT - 1; - int denom; - int s = 1; + float amortval; assert(delay >= 0); - if (benefit < 0) { s = -1; benefit *= s; } - while (delay > 0 && benefit != 0) { - denom = 1; - while (delay >= 12 && (benefit >> 28) == 0 && (denom >> 27) == 0) { - benefit *= 3; /* this is a kluge but it is 99.9% accurate and saves time */ - denom *= 5; /* as long as MORT remains 24! -- Syela */ - delay -= 12; - } - while ((benefit >> 25) == 0 && delay > 0 && (denom >> 25) == 0) { - benefit *= num; - denom *= MORT; - delay--; - } - if (denom > 1) { /* The "+ (denom/2)" makes the rounding correct */ - benefit = (benefit + (denom/2)) / denom; - } - } - return(benefit * s); + amortval = pow(((float)MORT -1) / MORT, delay); + return (benefit * amortval *(1+FLT_EPSILON)); } /************************************************************************** @@ -160,7 +140,10 @@ } /************************************************************************** -this whole funct assumes G_REP^H^H^HDEMOCRACY -- Syela + Calculates the desire for founding new cities. FIXME: This function + should be moved to ai/advdomestic.c - Per + + this whole funct assumes G_REP^H^H^HDEMOCRACY -- Syela **************************************************************************/ static int city_desirability(struct player *pplayer, int x, int y) { @@ -171,8 +154,7 @@ int d = 0; int a, i0, j0; /* need some temp variables */ int temp=0, tmp=0; - bool debug = FALSE; - int g = 1; + bool debug = FALSE; /* turn on extra LOG_DEBUG output */ struct tile *ptile; int con, con2; bool har; @@ -198,15 +180,6 @@ con = ptile->continent; -/* not worth the computations AFAICT -- Syela - wrapped anyway in case it comes back -- dwp - if (improvement_variant(B_PYRAMIDS)==0) { - city_list_iterate(pplayer->cities, acity) - if (city_got_building(acity, B_PYRAMIDS)) g++; - city_list_iterate_end; - } -*/ - memset(taken, 0, sizeof(taken)); memset(food, 0, sizeof(food)); memset(shield, 0, sizeof(shield)); @@ -279,7 +252,7 @@ val = (shield[ii][jj] + mine[ii][jj]) + (food[ii][jj] + irrig[ii][jj]) * FOOD_WEIGHTING + /* seems to be needed */ (trade[ii][jj] + road[ii][jj]); - val -= amortize(40 * SHIELD_WEIGHTING + (50 - 20 * g) * FOOD_WEIGHTING, 12); + val -= amortize(40 * SHIELD_WEIGHTING + (50 - 20) * FOOD_WEIGHTING, 12); /* 12 is arbitrary; need deterrent to represent loss of a settlers -- Syela */ freelog(LOG_DEBUG, "Desire to immigrate to %s = %d -> %d", @@ -367,9 +340,9 @@ } } if (best == 0) break; - if (f > 0) d += (game.foodbox * MORT * n + (f*g) - 1) / (f*g); + if (f > 0) d += (game.foodbox * MORT * n + f - 1) / f; if (n == 4) { - val -= amortize(40 * SHIELD_WEIGHTING + (50 - 20 * g) * FOOD_WEIGHTING, d); /* lers */ + val -= amortize(40 * SHIELD_WEIGHTING + (50 - 20) * FOOD_WEIGHTING, d); /* lers */ temp = amortize(40 * SHIELD_WEIGHTING, d); /* temple */ tmp = val; } @@ -389,14 +362,15 @@ } /************************************************************************** -... + Manages settlers. FIXME: This function belongs in ai/aiunit.c - Per **************************************************************************/ void ai_manage_settler(struct player *pplayer, struct unit *punit) { punit->ai.control = TRUE; - if (punit->ai.ai_role == AIUNIT_NONE) /* if BUILD_CITY must remain BUILD_CITY */ + /* if BUILD_CITY must remain BUILD_CITY, otherwise turn into autosettler */ + if (punit->ai.ai_role == AIUNIT_NONE) { punit->ai.ai_role = AIUNIT_AUTO_SETTLER; -/* gonna handle city-building in the auto-settler routine -- Syela */ + } return; } @@ -421,16 +395,8 @@ return TEST_BIT(map_get_tile(x, y)->assigned, pplayer->player_no); } -/*************************************************************************/ - -/* all of the benefit and ai_calc routines rewritten by Syela */ -/* to conform with city_tile_value and related calculations elsewhere */ -/* all of these functions are VERY CPU-inefficient and are being cached */ -/* I don't really want to rewrite them and possibly screw them up. */ -/* The cache should keep the CPU increase linear instead of quadratic. -- Syela */ - /************************************************************************** -... + Calculates the value of removing pollution. **************************************************************************/ static int ai_calc_pollution(struct city *pcity, int cx, int cy, int best, int mx, int my) @@ -446,7 +412,7 @@ } /************************************************************************** -... + Calculates the value of removing fallout. **************************************************************************/ static int ai_calc_fallout(struct city *pcity, struct player *pplayer, int cx, int cy, int best, int mx, int my) @@ -463,7 +429,9 @@ } /************************************************************************** -... + Returns TRUE if tile at x, y is useful as a source of irrigation. If + the given player is an AI, it will ignore fog of war. (Do not "fix" this, + since the AI does too little exploration yet to manage without this.) **************************************************************************/ static bool is_wet(struct player *pplayer, int x, int y) { @@ -480,7 +448,8 @@ } /************************************************************************** -... + Returns TRUE if there is an irrigation source adjacent to the given x, y + position. **************************************************************************/ static bool is_wet_or_is_wet_cardinal_around(struct player *pplayer, int x, int y) @@ -539,31 +508,14 @@ } /************************************************************************** -... + Calculates the value of building a mine. **************************************************************************/ static int ai_calc_mine(struct city *pcity, int cx, int cy, int mx, int my) { int m; struct tile *ptile = map_get_tile(mx, my); - -#if 0 - enum tile_terrain_type t = ptile->terrain; - struct tile_type *type = get_tile_type(t); - int s = ptile->special; - if (ptile->terrain != type->mining_result && - type->mining_result != T_LAST) { /* EXPERIMENTAL 980905 -- Syela */ - ptile->terrain = type->mining_result; - map_clear_special(x, y, S_FARMLAND); - map_clear_special(x, y, S_IRRIGATION); - m = city_tile_value(pcity, i, j, 0, 0); - ptile->terrain = t; - ptile->special = s; - return(m); - } else -#endif - - /* Note that this code means we will never try to mine a city into the ocean */ + /* Don't replace existing infrastructure */ if ((ptile->terrain == T_HILLS || ptile->terrain == T_MOUNTAINS) && !tile_has_special(ptile, S_IRRIGATION) && !tile_has_special(ptile, S_MINE)) { map_set_special(mx, my, S_MINE); @@ -574,7 +526,7 @@ } /************************************************************************** -... + Calculates the value of doing a terrain transformation. **************************************************************************/ static int ai_calc_transform(struct city *pcity, int cx, int cy, int mx, int my) @@ -626,6 +578,8 @@ struct tile *ptile; bool is_border = IS_BORDER_MAP_POS(x, y, 2); + assert(spc == S_ROAD || spc == S_RAILROAD); + if (!normalize_map_pos(&x, &y)) return 0; @@ -663,7 +617,7 @@ } /************************************************************************** -... + Calculate the value of building a road. **************************************************************************/ static int ai_calc_road(struct city *pcity, struct player *pplayer, int cx, int cy, int mx, int my) @@ -683,7 +637,7 @@ } /************************************************************************** -... + Calculate the value of building a railroad. **************************************************************************/ static int ai_calc_railroad(struct city *pcity, struct player *pplayer, int cx, int cy, int mx, int my) @@ -770,12 +724,21 @@ } /************************************************************************** -... + Tries to find a boat for our settler. Requires warmap to be initialized + with respect to x, y. cap is the requested capacity on the transport. + Note that it may return a transport with less than cap capacity if this + transport has zero move cost to x, y. + + The "virtual boats" code is not used. It is probably too unreliable, + since the AI switches its production back and forth continously. + + FIXME: This function should be moved to ai/aiunit.c, since only AI + settlers use boats. - Per **************************************************************************/ -int find_boat(struct player *pplayer, int *x, int *y, int cap) -{ /* this function uses the current warmap, whatever it may hold */ -/* unit is no longer an arg! we just trust the map! -- Syela */ - int best = 22, id = 0; /* arbitrary maximum distance, I will admit! */ +Unit_Type_id find_boat(struct player *pplayer, int *x, int *y, int cap) +{ + int best = 22; /* arbitrary maximum distance, I will admit! */ + Unit_Type_id id = 0; unit_list_iterate(pplayer->units, aunit) if (is_ground_units_transport(aunit)) { if (warmap.cost[aunit->x][aunit->y] < best && @@ -809,7 +772,8 @@ } /************************************************************************** -... + Returns TRUE if there are (other) ground units than punit stacked on + punit's tile. **************************************************************************/ struct unit *other_passengers(struct unit *punit) { @@ -860,17 +824,21 @@ } /************************************************************************** -... + Returns how much food a settler will consume out of the city's foodbox + when created. + + FIXME: This function should be moved into common/unittype.c - Per **************************************************************************/ -static int unit_food_cost(struct unit *punit) +static int unit_foodbox_cost(struct unit *punit) { int cost; if (punit->id != 0) { cost = 30; } else { - /* It is a virtuel unit, so must start in a city... */ + /* It is a virtual unit, so must start in a city... */ struct city *pcity = map_get_city(punit->x, punit->y); + assert(pcity); cost = city_granary_size(pcity->size); if (city_got_effect(pcity, B_GRANARY)) cost /= 2; @@ -880,7 +848,7 @@ } /************************************************************************** -... + Calculates a unit's food upkeep (per turn). **************************************************************************/ static int unit_food_upkeep(struct unit *punit) { @@ -917,7 +885,7 @@ int ucont = map_get_continent(punit->x, punit->y); int mv_rate = unit_type(punit)->move_rate; int food_upkeep = unit_food_upkeep(punit); - int food_cost = unit_food_cost(punit); + int food_cost = unit_foodbox_cost(punit); int boatid, bx = 0, by = 0; /* as returned by find_boat */ int best_newv = 0; @@ -1055,7 +1023,7 @@ newv==best_newv; not initialized to zero, so that newv=0 activities are not chosen */ int food_upkeep = unit_food_upkeep(punit); - int food_cost = unit_food_cost(punit); + int food_cost = unit_foodbox_cost(punit); bool can_rr = player_knows_techs_with_flag(pplayer, TF_RAILROAD); int best_newv = 0; @@ -1181,7 +1149,7 @@ } /************************************************************************** -... + Handles GOTO for settlers. Only ever used in auto_settler_findwork below. **************************************************************************/ static bool ai_gothere(struct unit *punit, int gx, int gy, struct unit *ferryboat) { @@ -1337,7 +1305,7 @@ } /************************************************************************** -... + Do all tile improvement calculations and cache them for later. **************************************************************************/ void initialize_infrastructure_cache(struct city *pcity) { @@ -1410,6 +1378,12 @@ } } +/************************************************************************** + Marks tiles as assigned to a settler. If we are on our way to the tile, + it is only assigned with respect to our own calculations, ie other + players' autosettlers may race us to the spot. If we are on the spot, + the it is marked as assigned for all players. +**************************************************************************/ static void assign_settlers_player(struct player *pplayer) { int i = 1<player_no; @@ -1431,6 +1405,10 @@ unit_list_iterate_end; } +/************************************************************************** + Clear previous turn's assignments, then assign autosettlers to uniquely + to tiles. This prevents autosettlers from messing with each others work. +**************************************************************************/ static void assign_settlers(void) { int i; @@ -1443,6 +1421,10 @@ } } +/************************************************************************** + Assign a region of the map as belonging to a certain player for keeping + autosettlers out of enemy territory. +**************************************************************************/ static void assign_region(int x, int y, int player_no, int distance, int s) { square_iterate(x, y, distance, x1, y1) { @@ -1452,7 +1434,18 @@ } /************************************************************************** -FIXME: we currently see even allies as enemies here in this routine + Try to keep autosettlers out of enemy territory. We assign blocks of + territory to the enemy based on the location of his units and their + movement. + + FIXME 1: We currently see even allies as enemies here. + FIXME 2: We totally ignore the possibility of enemies getting to us + by road or rail. Whatever Syela says, this is just so broken. + FIXME 3: Remove omniscience. + + NOTE: Units with extremely high movement in the game will make + effectively make autosettlers run and hide and never come out again. + The cowards. **************************************************************************/ static void assign_territory_player(struct player *pplayer) { @@ -1480,7 +1473,6 @@ } /************************************************************************** -... This function is supposed to keep settlers out of enemy territory -- Syela **************************************************************************/