Index: server/unittools.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/unittools.c,v retrieving revision 1.174 diff -u -r1.174 unittools.c --- server/unittools.c 2002/04/06 10:52:19 1.174 +++ server/unittools.c 2002/04/13 21:51:23 @@ -2386,8 +2386,7 @@ static void hut_get_city(struct unit *punit) { struct player *pplayer = unit_owner(punit); - - if (is_ok_city_spot(punit->x, punit->y)) { + if (city_desirability(unit_owner(punit), punit->x, punit->y) > 0) { notify_player_ex(pplayer, punit->x, punit->y, E_HUT_CITY, _("Game: You found a friendly city.")); create_city(pplayer, punit->x, punit->y, 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 21:51:24 @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include "city.h" #include "game.h" @@ -39,18 +41,12 @@ 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. **************************************************************************/ static bool ai_do_build_city(struct player *pplayer, struct unit *punit) { @@ -77,41 +73,25 @@ } /************************************************************************** -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)); } /************************************************************************** -... + The minimap marks areas that are occupied by other cities, so we + don't settle in their area. This also include cities we plan on + settling. **************************************************************************/ void generate_minimap(void) { @@ -126,7 +106,13 @@ } /************************************************************************** -... + Remove a city from the minimap. We only do this when we become uncertain + if our settler will really settle there. That happens a lot, + unfortunately. + + FIXME: It does not remove a planned city from the minimap if the + settler planned to make this city dies! The result is that the AI + never builds a city at this location. - Per **************************************************************************/ void remove_city_from_minimap(int x, int y) { @@ -143,7 +129,8 @@ } /************************************************************************** -... + Adds a city to the minimap. This is done when a new city is being + founded, and when the AI assigns a settler to build a city somewhere. **************************************************************************/ void add_city_to_minimap(int x, int y) { @@ -160,10 +147,20 @@ } /************************************************************************** -this whole funct assumes G_REP^H^H^HDEMOCRACY -- Syela + Calculates the desire for founding a new city at (x, y). This + information is cached in the minimap at (x, y) for the remainder of + the game. + + FIXME: We need to use a virtual unit here. Some defines added for + now.- Per + + this whole funct assumes G_REP^H^H^HDEMOCRACY -- Syela **************************************************************************/ -static int city_desirability(struct player *pplayer, int x, int y) +int city_desirability(struct player *pplayer, int x, int y) { +#define SETTLER_COST 40 +#define SETTLER_LOSS_CHANCE 12 /* totally arbitrary Syelaism */ +#define TEMPLE_COST 40 int taken[5][5], food[5][5], shield[5][5], trade[5][5]; int irrig[5][5], mine[5][5], road[5][5]; int f, n; @@ -171,8 +168,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 +194,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,9 +266,9 @@ 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); - /* 12 is arbitrary; need deterrent to represent loss - of a settlers -- Syela */ + /* FIXME: Syela got to be kidding about this one - Per */ + val -= amortize(SETTLER_COST * SHIELD_WEIGHTING + (50 - 20) * + FOOD_WEIGHTING, SETTLER_LOSS_CHANCE); freelog(LOG_DEBUG, "Desire to immigrate to %s = %d -> %d", pcity->name, val, (val * 100) / MORT / 70); return(val); @@ -299,7 +286,7 @@ db = get_tile_type(map_get_terrain(x, y))->defense_bonus; if (map_has_special(x, y, S_RIVER)) db += (db * terrain_control.river_defense_bonus) / 100; - val += (4 * db - 40) * SHIELD_WEIGHTING; + val += (4 * db - SETTLER_COST) * SHIELD_WEIGHTING; /* don't build cities in danger!! FIX! -- Syela */ val += 8 * MORT; /* one science per city */ @@ -367,10 +354,11 @@ } } 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 */ - temp = amortize(40 * SHIELD_WEIGHTING, d); /* temple */ + val -= amortize(SETTLER_COST * SHIELD_WEIGHTING + (50 - 20) * + FOOD_WEIGHTING, d); /* settlers */ + temp = amortize(TEMPLE_COST * SHIELD_WEIGHTING, d); /* temple */ tmp = val; } } @@ -387,16 +375,20 @@ } return(val); } +#undef TEMPLE_COST +#undef SETTLER_COST +#undef SETTLER_LOSS_CHANCE /************************************************************************** -... + Manages settlers. **************************************************************************/ 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 +413,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 +430,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 +447,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 +466,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 +526,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 +544,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 +596,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 +635,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 +655,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) @@ -704,57 +676,6 @@ /* bonuses for adjacent railroad tiles */ } -/************************************************************************* - return how good this square is for a new city. -**************************************************************************/ -bool is_ok_city_spot(int x, int y) -{ - int dx, dy; - - switch (map_get_terrain(x,y)) { - case T_OCEAN: - case T_UNKNOWN: - case T_MOUNTAINS: - case T_FOREST: - case T_HILLS: - case T_ARCTIC: - case T_JUNGLE: - case T_SWAMP: - case T_TUNDRA: - case T_LAST: - return FALSE; - case T_DESERT: - if (!map_has_special(x, y, S_SPECIAL_1) - && !map_has_special(x, y, S_SPECIAL_2)) - return FALSE; - case T_GRASSLAND: - case T_PLAINS: - case T_RIVER: - break; - default: - break; - } - - players_iterate(pplayer) { - city_list_iterate(pplayer->cities, pcity) { - if (map_distance(x, y, pcity->x, pcity->y)<=8) { - map_distance_vector(&dx, &dy, pcity->x, pcity->y, x, y); - dx = abs(dx), dy = abs(dy); - /* these are heuristics... */ - if (dx<=5 && dy<5) - return FALSE; - if (dx<5 && dy<=5) - return FALSE; - /* this is the law... */ - if (dxunits, aunit) if (is_ground_units_transport(aunit)) { if (warmap.cost[aunit->x][aunit->y] < best && @@ -809,7 +736,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 +788,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 +812,7 @@ } /************************************************************************** -... + Calculates a unit's food upkeep (per turn). **************************************************************************/ static int unit_food_upkeep(struct unit *punit) { @@ -917,7 +849,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 +987,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 +1113,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 +1269,7 @@ } /************************************************************************** -... + Do all tile improvement calculations and cache them for later. **************************************************************************/ void initialize_infrastructure_cache(struct city *pcity) { @@ -1410,6 +1342,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 +1369,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 +1385,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 +1398,17 @@ } /************************************************************************** -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. + + NOTE: Having units with extremely high movement in the game will + effectively make autosettlers run and hide and never come out again. + The cowards. **************************************************************************/ static void assign_territory_player(struct player *pplayer) { @@ -1480,7 +1436,6 @@ } /************************************************************************** -... This function is supposed to keep settlers out of enemy territory -- Syela **************************************************************************/ @@ -1576,7 +1531,6 @@ if (qpass->ai.ferryboat == pcity->id) want = -199; } unit_list_iterate_end; - pcity->ai.settler_want = want; } Index: server/settlers.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/settlers.h,v retrieving revision 1.20 diff -u -r1.20 settlers.h --- server/settlers.h 2002/03/05 10:56:38 1.20 +++ server/settlers.h 2002/04/13 21:51:24 @@ -29,7 +29,7 @@ int amortize(int benefit, int delay); void ai_manage_settler(struct player *pplayer, struct unit *punit); -bool is_ok_city_spot(int x, int y); /* laughable, really. */ +int city_desirability(struct player *pplayer, int x, int y); void generate_minimap(void); void remove_city_from_minimap(int x, int y); void add_city_to_minimap(int x, int y);