Index: server/settlers.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/settlers.c,v retrieving revision 1.139 diff -u -r1.139 settlers.c --- server/settlers.c 2002/07/19 14:24:04 1.139 +++ server/settlers.c 2002/08/08 21:13:17 @@ -39,18 +39,13 @@ 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); - +static int city_desirability(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,12 +72,17 @@ } /************************************************************************** -amortize(benefit, delay) returns benefit * ((MORT - 1)/MORT)^delay -(^ = to the power of) - -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 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. + + amortize(benefit, delay) returns benefit * ((MORT - 1)/MORT)^delay + (^ = to the power of) + + 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). **************************************************************************/ int amortize(int benefit, int delay) { @@ -111,7 +111,9 @@ } /************************************************************************** -... + 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,14 +128,16 @@ } /************************************************************************** -... + 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. **************************************************************************/ void remove_city_from_minimap(int x, int y) { freelog(LOG_DEBUG, "Removing (%d, %d) from minimap.", x, y); - square_iterate(x, y, 4, x1, y1) { + square_iterate(x, y, CITY_MAP_SIZE-1, x1, y1) { int dist = sq_map_distance(x, y, x1, y1); - if (dist <= 5) { + if (dist <= CITY_MAP_SIZE) { if (minimap[x1][y1] < 0) minimap[x1][y1]++; else minimap[x1][y1] = 0; } else if (dist <= 20) { @@ -143,14 +147,18 @@ } /************************************************************************** -... + 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. + The minimap also caches city desirability. When the minimap position + is positive, it is a city want. When it is negative, there is one or + more cities keeping a semaphoric lock on this tile. **************************************************************************/ void add_city_to_minimap(int x, int y) { freelog(LOG_DEBUG, "Adding (%d, %d) to minimap.", x, y); - square_iterate(x, y, 4, x1, y1) { + square_iterate(x, y, CITY_MAP_SIZE-1, x1, y1) { int dist = sq_map_distance(x, y, x1, y1); - if (dist <= 5) { + if (dist <= CITY_MAP_SIZE) { if (minimap[x1][y1] < 0) minimap[x1][y1]--; else minimap[x1][y1] = -1; } else if (dist <= 20) { @@ -160,26 +168,34 @@ } /************************************************************************** -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. The minimap ensures that we do not build cities too close + to each other. + + 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 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; - int worst, b2, best = 0, ii, jj, val, cur; - int d = 0; - int a, i0, j0; /* need some temp variables */ - int temp=0, tmp=0; - bool debug = FALSE; - int g = 1; +#define SETTLER_COST 40 +#define SETTLER_LOSS_FACTOR 12 /* totally arbitrary Syelaism */ +#define TEMPLE_COST 40 + int taken[CITY_MAP_SIZE][CITY_MAP_SIZE], food[CITY_MAP_SIZE][CITY_MAP_SIZE]; + int shield[CITY_MAP_SIZE][CITY_MAP_SIZE], trade[CITY_MAP_SIZE][CITY_MAP_SIZE]; + int irrig[CITY_MAP_SIZE][CITY_MAP_SIZE], mine[CITY_MAP_SIZE][CITY_MAP_SIZE]; + int road[CITY_MAP_SIZE][CITY_MAP_SIZE]; + int cfood, n, worst, b2, best = 0, ii, jj, val, cur; + int a, i0, j0, temp=0, tmp=0; /* need some temp variables */ + const bool debug = FALSE; /* turn on extra LOG_DEBUG output */ struct tile *ptile; - int con, con2; - bool har; - int t, sh; + int continent, continent2; + bool harbour; /* is near ocean */ struct tile_type *ptype; struct city *pcity; - int db; + int defense_bonus; if (is_square_threatened(pplayer, x, y)) return 0; @@ -190,23 +206,11 @@ if (pcity && pcity->size >= game.add_to_size_limit) return 0; if (!pcity && minimap[x][y] < 0) return 0; if (!pcity && minimap[x][y] > 0) return minimap[x][y]; - - har = is_terrain_near_tile(x, y, T_OCEAN); - sh = SHIELD_WEIGHTING * MORT; - t = TRADE_WEIGHTING * MORT; /* assuming DEMOCRACY now, much easier! - Syela */ + harbour = is_terrain_near_tile(x, y, T_OCEAN); - con = ptile->continent; + continent = 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)); @@ -216,16 +220,20 @@ memset(road, 0, sizeof(road)); city_map_checked_iterate(x, y, i, j, map_x, map_y) { + const int cshields = SHIELD_WEIGHTING * MORT; + const int ctrade = TRADE_WEIGHTING * MORT; + if ((!pcity && minimap[map_x][map_y] >= 0) || (pcity && get_worker_city(pcity, i, j) == C_TILE_EMPTY)) { ptile = map_get_tile(map_x, map_y); - con2 = ptile->continent; + continent2 = ptile->continent; ptype = get_tile_type(ptile->terrain); food[i][j] = (get_tile_food_base(ptile) - 2) * MORT; if (is_city_center(i, j)) { food[i][j] += 2 * MORT; } - if (ptype->irrigation_result == ptile->terrain && con2 == con) { + if (ptype->irrigation_result == ptile->terrain + && continent2 == continent) { if (tile_has_special(ptile, S_IRRIGATION) || is_city_center(i, j)) { irrig[i][j] = MORT * ptype->irrigation_food_incr; } @@ -233,31 +241,34 @@ ptile->terrain != T_HILLS) irrig[i][j] = MORT * ptype->irrigation_food_incr - 9; /* KLUGE */ /* all of these kluges are hardcoded amortize calls to save much CPU use -- Syela */ - } else if (ptile->terrain == T_OCEAN && har) food[i][j] += MORT; /* harbor */ - shield[i][j] = get_tile_shield_base(ptile) * sh; + } else if (ptile->terrain == T_OCEAN && harbour) food[i][j] += MORT; + shield[i][j] = get_tile_shield_base(ptile) * cshields; if (is_city_center(i, j) && shield[i][j] == 0) { - shield[i][j] = sh; + shield[i][j] = cshields; } - if (ptile->terrain == T_OCEAN && har) shield[i][j] += sh; +/* if (ptile->terrain == T_OCEAN && harbour) shield[i][j] += cshields; */ /* above line is not sufficiently tested. AI was building on shores, but not as far out in the ocean as possible, which limits growth in the very long term (after SEWER). These cities will all eventually have OFFSHORE, and I need to acknowledge that. I probably shouldn't treat it as free, but that's the easiest, and I doubt pathological behavior will result. -- Syela */ - if (tile_has_special(ptile, S_MINE)) - mine[i][j] = sh * ptype->mining_shield_incr; - else if (ptile->terrain == T_HILLS && con2 == con) - mine[i][j] = sh * ptype->mining_shield_incr - 300; /* KLUGE */ - trade[i][j] = get_tile_trade_base(ptile) * t; + if (tile_has_special(ptile, S_MINE)) { + mine[i][j] = cshields * ptype->mining_shield_incr; + } else if (ptile->terrain == T_HILLS && continent2 == continent) { + mine[i][j] = cshields * ptype->mining_shield_incr - 300; /* KLUGE */ + } + trade[i][j] = get_tile_trade_base(ptile) * ctrade; if (ptype->road_trade_incr > 0) { if (tile_has_special(ptile, S_ROAD) || is_city_center(i, j)) { - road[i][j] = t * ptype->road_trade_incr; + road[i][j] = ctrade * ptype->road_trade_incr; } - else if (con2 == con) - road[i][j] = t * ptype->road_trade_incr - 70; /* KLUGE */ /* actualy exactly 70 1/2 */ + else if (continent2 == continent) { + /* KLUGE */ /* actualy exactly 70 1/2 */ + road[i][j] = ctrade * ptype->road_trade_incr - 70; + } } - if (trade[i][j] != 0) trade[i][j] += t; - else if (road[i][j] != 0) road[i][j] += t; + if (trade[i][j] != 0) trade[i][j] += ctrade; + else if (road[i][j] != 0) road[i][j] += ctrade; } } city_map_checked_iterate_end; @@ -276,37 +287,38 @@ } } city_map_iterate_end; if (best == 0) return(0); - 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 */ - freelog(LOG_DEBUG, "Desire to immigrate to %s = %d -> %d", + val = (shield[ii][jj] + mine[ii][jj]) + + (food[ii][jj] + irrig[ii][jj]) * FOOD_WEIGHTING + + (trade[ii][jj] + road[ii][jj]); + val -= amortize(SETTLER_COST * SHIELD_WEIGHTING + (50 - 20) + * FOOD_WEIGHTING, SETTLER_LOSS_FACTOR); + freelog(LOG_DEBUG, "Desire to add ourselves to city %s = %d -> %d", pcity->name, val, (val * 100) / MORT / 70); return(val); } - f = food[2][2] + irrig[2][2]; - if (f == 0) return(0); /* no starving cities, thank you! -- Syela */ - val = f * FOOD_WEIGHTING + /* this needs to be here, strange as it seems */ + cfood = food[2][2] + irrig[2][2]; + if (cfood == 0) return(0); /* no starving cities, thank you! -- Syela */ + val = cfood * FOOD_WEIGHTING + /* this needs to be here, strange as it seems */ (shield[2][2] + mine[2][2]) + (trade[2][2] + road[2][2]); taken[2][2]++; /* val is mort times the real value */ /* treating harbor as free to approximate advantage of building boats. -- Syela */ - db = get_tile_type(map_get_terrain(x, y))->defense_bonus; + defense_bonus = 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; + defense_bonus += (defense_bonus * terrain_control.river_defense_bonus) / 100; + val += (4 * defense_bonus - SETTLER_COST) * SHIELD_WEIGHTING; /* don't build cities in danger!! FIX! -- Syela */ - val += 8 * MORT; /* one science per city */ + val += 8 * MORT; /* one science per city */ /* FIXME: not correct - Per */ - if (debug) freelog(LOG_DEBUG, "City value (%d, %d) = %d, har = %d, f = %d", - x, y, val, har, f); + freelog(LOG_DEBUG, "City value (%d, %d) = %d, harbour = %d, cfood = %d", + x, y, val, harbour, cfood); - for (n = 1; n <= 20 && f > 0; n++) { + /* loop until size 20 virtual city */ + for (n = 1; n <= 20 && cfood > 0; n++) { + int d = 0; /* i have no idea wtf this is - per */ for (a = 1; a > 0; a--) { best = 0; worst = -1; b2 = 0; i0 = 0; j0 = 0; ii = 0; jj = 0; city_map_iterate(i, j) { @@ -332,7 +344,7 @@ cur = amortize((shield[ii][jj] + mine[ii][jj]) + (food[ii][jj] + irrig[ii][jj]) * FOOD_WEIGHTING + /* seems to be needed */ (trade[ii][jj] + road[ii][jj]), d); - f += food[ii][jj] + irrig[ii][jj]; + cfood += food[ii][jj] + irrig[ii][jj]; if (cur > 0) val += cur; taken[ii][jj]++; @@ -351,7 +363,7 @@ if (worst < b2 && worst >= 0) { cur = amortize((shield[i0][j0] + mine[i0][j0]) + (trade[i0][j0] + road[i0][j0]), d); - f -= (food[i0][j0] + irrig[i0][j0]); + cfood -= (food[i0][j0] + irrig[i0][j0]); val -= cur; taken[i0][j0]--; a++; @@ -367,10 +379,11 @@ } } if (best == 0) break; - if (f > 0) d += (game.foodbox * MORT * n + (f*g) - 1) / (f*g); + if (cfood > 0) d += (game.foodbox * MORT * n + cfood - 1) / cfood; 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 +400,20 @@ } return(val); } +#undef TEMPLE_COST +#undef SETTLER_COST +#undef SETTLER_LOSS_FACTOR /************************************************************************** -... + 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; } @@ -404,7 +421,8 @@ return 1 if there is already a unit on this square or one destined for it (via goto) **************************************************************************/ -static bool is_already_assigned(struct unit *myunit, struct player *pplayer, int x, int y) +static bool is_already_assigned(struct unit *myunit, struct player *pplayer, + int x, int y) { if (same_pos(myunit->x, myunit->y, x, y) || same_pos(myunit->goto_dest_x, myunit->goto_dest_y, x, y)) { @@ -421,16 +439,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 +456,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 +473,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 +492,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 +552,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 +570,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 +622,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 +661,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 +681,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,78 +702,42 @@ /* 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 (dxgoto_dest_x = x; punit->goto_dest_y = y; set_unit_activity(punit, ACTIVITY_GOTO); send_unit_info(NULL, punit); - do_unit_goto(punit, GOTO_MOVE_ANY, FALSE); + gotores = do_unit_goto(punit, GOTO_MOVE_ANY, FALSE); + + if (gotores != GR_DIED) { + freelog(LOG_DEBUG, "%s: %s (%d@%d,%d) did settler goto to %s towards " + "(%d,%d) with result %d", pplayer->name, unit_type(punit)->name, + punit->id, punit->x, punit->y, get_activity_text(punit->activity), + x, y, gotores); + } } /************************************************************************** -... + 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. **************************************************************************/ -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 +771,8 @@ } /************************************************************************** -... + Returns TRUE if there are (other) ground units than punit stacked on + punit's tile. **************************************************************************/ struct unit *other_passengers(struct unit *punit) { @@ -860,27 +823,30 @@ } /************************************************************************** -... + Returns how much food a settler will consume out of the city's foodbox + when created. If unit has id zero it is assumed to be a virtual unit + inside a city. + + FIXME: This function should be generalised and then moved into + common/unittype.c - Per **************************************************************************/ -static int unit_food_cost(struct unit *punit) +static int unit_foodbox_cost(struct unit *punit) { - int cost; + int cost = 30; - if (punit->id != 0) { - cost = 30; - } else { - /* It is a virtuel unit, so must start in a city... */ + if (punit->id == 0) { + /* 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; + if (city_got_effect(pcity, B_GRANARY)) { cost /= 2; } } return cost; } /************************************************************************** -... + Calculates a unit's food upkeep (per turn). **************************************************************************/ static int unit_food_upkeep(struct unit *punit) { @@ -911,13 +877,13 @@ struct unit **ferryboat) { struct city *mycity = map_get_city(punit->x, punit->y); - int newv, b, d; + int newv, b, moves; struct player *pplayer = unit_owner(punit); bool nav_known = (get_invention(pplayer, game.rtech.nav) == TECH_KNOWN); 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; @@ -982,23 +948,21 @@ } else { mv_cost = warmap.cost[x][y]; } - d = mv_cost / mv_rate; + moves = mv_cost / mv_rate; /* without this, the computer will go 6-7 tiles from X to build a city at Y */ - d *= 2; + moves *= 2; /* and then build its NEXT city halfway between X and Y. -- Syela */ b = city_desirability(pplayer, x, y) * (ai_fuzzy(pplayer, TRUE) ? 1 : 0); - newv = amortize(b, d); + newv = amortize(b, moves); b = (food_upkeep * FOOD_WEIGHTING) * MORT; if (map_get_continent(x, y) != ucont) b += SHIELD_WEIGHTING * MORT; - newv -= (b - amortize(b, d)); + newv -= (b - amortize(b, moves)); /* deal with danger Real Soon Now! -- Syela */ /* newv is now the value over mort turns */ newv = (newv * 100) / MORT / ((w_virtual ? 80 : 40) + food_cost); - if (best_newv != 0 && map_get_city(x, y)) newv = 0; - /* I added a line to discourage settling in existing cities, but it was inadequate. It may be true that in the short-term building infrastructure on tiles that won't be worked for ages is not as useful as helping other @@ -1007,20 +971,27 @@ either through city growth or de-elvisization, settlers being built and improving those tiles, and then immigrating shortly thereafter. -- Syela */ + if (best_newv != 0 && map_get_city(x, y)) newv = 0; + if (newv>0 && pplayer->ai.expand!=100) { newv = (newv * pplayer->ai.expand) / 100; } +#ifdef REALLY_DEBUG_THIS if (w_virtual) { freelog(LOG_DEBUG, "%s: best_newv = %d, w_virtual = 1, newv = %d", mycity->name, best_newv, newv); } +#endif if (map_get_continent(x, y) != ucont && !nav_known && near >= 8) { +#ifdef REALLY_DEBUG_THIS freelog(LOG_DEBUG, - "%s @(%d, %d) city_des (%d, %d) = %d, newv = %d, d = %d", + "%s (%d, %d) rejected city at (%d, %d) to %d, newv = %d, moves = %d" \ + " (too far for trireme)", (punit->id != 0 ? unit_type(punit)->name : mycity->name), - punit->x, punit->y, x, y, b, newv, d); + punit->x, punit->y, x, y, b, newv, moves); +#endif } else if (newv > best_newv) { best_newv = newv; if (w_virtual) { @@ -1032,6 +1003,10 @@ } } square_iterate_end; + freelog(LOG_DEBUG, + "%s (%d, %d) wants city at (%d, %d) with want %d, distance %d moves", + (punit->id != 0 ? unit_type(punit)->name : mycity->name), + punit->x, punit->y, *gx, *gy, best_newv, moves); return best_newv; } @@ -1055,7 +1030,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; @@ -1063,7 +1038,9 @@ generate_warmap(mycity, punit); city_list_iterate(pplayer->cities, pcity) { - freelog(LOG_DEBUG, "%s", pcity->name); +#ifdef REALLY_DEBUG_THIS + freelog(LOG_DEBUG, "Evaluating improvements for %s...", pcity->name); +#endif /* try to work near the city */ city_map_checked_iterate(pcity->x, pcity->y, i, j, x, y) { if (get_worker_city(pcity, i, j) == C_TILE_UNAVAILABLE) @@ -1154,6 +1131,7 @@ x, y); } +#ifdef REALLY_DEBUG_THIS freelog(LOG_DEBUG, "(%d %d) I=%+-4d O=%+-4d M=%+-4d R=%+-4d RR=%+-4d P=%+-4d N=%+-4d", i, j, @@ -1161,6 +1139,7 @@ pcity->ai.mine[i][j], pcity->ai.road[i][j], pcity->ai.railroad[i][j], pcity->ai.detox[i][j], pcity->ai.derad[i][j]); +#endif } /* end if we are a legal destination */ } city_map_iterate_outwards_end; } @@ -1181,7 +1160,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 +1316,7 @@ } /************************************************************************** -... + Do all tile improvement calculations and cache them for later. **************************************************************************/ void initialize_infrastructure_cache(struct city *pcity) { @@ -1410,6 +1389,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 +1416,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 +1432,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 +1445,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 +1483,6 @@ } /************************************************************************** -... This function is supposed to keep settlers out of enemy territory -- Syela **************************************************************************/ @@ -1579,12 +1581,15 @@ virtualunit.moves_left = unit_type(&virtualunit)->move_rate; virtualunit.hp = unit_type(&virtualunit)->hp; want = evaluate_improvements(&virtualunit, &best_act, &gx, &gy); +/* FIXME: AI does not ship F_SETTLERS around, only F_CITIES - Per */ +#ifdef AI_SMART unit_list_iterate(pplayer->units, qpass) { /* We want a ferryboat with want 199 */ if (qpass->ai.ferryboat == pcity->id) want = -199; - } unit_list_iterate_end; - + } unit_list_iterate_end; +#endif + assert(want >= 0); 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/08/08 21:13:17 @@ -29,7 +29,6 @@ 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. */ void generate_minimap(void); void remove_city_from_minimap(int x, int y); void add_city_to_minimap(int x, int y); Index: server/unittools.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/unittools.c,v retrieving revision 1.178 diff -u -r1.178 unittools.c --- server/unittools.c 2002/06/01 19:25:26 1.178 +++ server/unittools.c 2002/08/08 21:13:17 @@ -1745,6 +1745,17 @@ struct city *phomecity = find_city_by_id(punit->homecity); int punit_x = punit->x, punit_y = punit->y; + /* Since settlers plot in new cities in the minimap before they + are built, so that no two settlers head towards the same city + spot, we need to ensure this reservation is cleared should + the settler die on the way. */ + if (unit_owner(punit)->ai.control + && punit->ai.ai_role == AIUNIT_AUTO_SETTLER) { + if (normalize_map_pos(&punit->goto_dest_x, &punit->goto_dest_y)) { + remove_city_from_minimap(punit->goto_dest_x, punit->goto_dest_y); + } + } + remove_unit_sight_points(punit); if (punit->pgr) { @@ -2388,7 +2399,7 @@ { struct player *pplayer = unit_owner(punit); - if (is_ok_city_spot(punit->x, punit->y)) { + if (city_can_be_built_here(punit->x, punit->y)) { notify_player_ex(pplayer, punit->x, punit->y, E_HUT_CITY, _("Game: You found a friendly city.")); create_city(pplayer, punit->x, punit->y,