? ai/aisettler.c ? ai/aisettler.h ? ai/citymap.c ? ai/citymap.h Index: ai/Makefile.am =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/Makefile.am,v retrieving revision 1.13 diff -u -r1.13 Makefile.am --- ai/Makefile.am 2003/03/11 17:59:26 1.13 +++ ai/Makefile.am 2003/04/07 02:00:02 @@ -35,6 +35,8 @@ aihand.h \ ailog.c \ ailog.h \ + aisettler.c \ + aisettler.h \ aitech.c \ aitech.h \ aitools.c \ @@ -42,4 +44,6 @@ aiunit.c \ aiunit.h \ aidiplomat.c \ - aidiplomat.h + aidiplomat.h \ + citymap.c \ + citymap.h Index: ai/advdomestic.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/advdomestic.c,v retrieving revision 1.102 diff -u -r1.102 advdomestic.c --- ai/advdomestic.c 2003/04/04 15:47:45 1.102 +++ ai/advdomestic.c 2003/04/07 02:00:02 @@ -987,7 +987,7 @@ unit_type = best_role_unit(pcity, F_CITIES); if (unit_type != U_LAST - && est_food > utype_food_cost(get_unit_type(unit_type), gov)) { + && est_food >= utype_food_cost(get_unit_type(unit_type), gov)) { /* founder_want calculated in settlers.c, called from ai_manage_city(). */ int want = pcity->ai.founder_want; Index: ai/aidata.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aidata.c,v retrieving revision 1.11 diff -u -r1.11 aidata.c --- ai/aidata.c 2003/04/04 15:47:45 1.11 +++ ai/aidata.c 2003/04/07 02:00:02 @@ -32,6 +32,7 @@ #include "advmilitary.h" #include "aicity.h" +#include "aihand.h" #include "aitools.h" #include "aiunit.h" @@ -210,6 +211,14 @@ ai->unhappy_priority = TRADE_WEIGHTING; /* danger */ ai->angry_priority = TRADE_WEIGHTING * 3; /* grave danger */ ai->pollution_priority = POLLUTION_WEIGHTING; + /* Perfection gives us an idea of how long to search for optimal + * solutions, instead of going for quick and dirty solutions that + * waste valuable time. This should be increased for maps where + * good city placements are hard to find, for example. */ + ai->perfection = TRADE_WEIGHTING; + + /* Goals */ + ai_best_government(pplayer); } /************************************************************************** Index: ai/aidata.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aidata.h,v retrieving revision 1.5 diff -u -r1.5 aidata.h --- ai/aidata.h 2003/01/02 11:59:29 1.5 +++ ai/aidata.h 2003/04/07 02:00:02 @@ -62,6 +62,15 @@ int unhappy_priority; int angry_priority; int pollution_priority; + int perfection; + + /* Goals */ + struct { + struct { + int idx, val, req; + } govt; + int revolution; + } goal; }; void ai_data_turn_init(struct player *pplayer); Index: ai/aihand.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aihand.c,v retrieving revision 1.78 diff -u -r1.78 aihand.c --- ai/aihand.c 2003/04/04 15:47:45 1.78 +++ ai/aihand.c 2003/04/07 02:00:03 @@ -306,20 +306,22 @@ } /************************************************************************** - Change the government form, if it can and there is a good reason. + Find best government to aim for. **************************************************************************/ -static void ai_manage_government(struct player *pplayer) +void ai_best_government(struct player *pplayer) { -#undef ANALYSE +#define ANALYSE struct ai_data *ai = ai_data_get(pplayer); - int best_gov = 0; int best_val = 0; - int really_best_val = 0; - int really_best_req = A_UNSET; int i; int bonus = 0; /* in percentage */ int current_gov = pplayer->government; + ai->goal.govt.idx = pplayer->government; + ai->goal.govt.val = 0; + ai->goal.govt.req = A_UNSET; + ai->goal.revolution = pplayer->government; + if (ai_handicap(pplayer, H_AWAY)) { return; } @@ -370,7 +372,9 @@ #endif } city_list_iterate_end; - /* Bonuses for non-economic abilities */ + /* Bonuses for non-economic abilities. We increase val by + * a very small amount here to choose govt in cases where + * we have cities yet. */ if (government_has_flag(gov, G_BUILD_VETERAN_DIPLOMAT)) { bonus += 3; /* WAG */ } @@ -385,10 +389,12 @@ } if (government_has_flag(gov, G_RAPTURE_CITY_GROWTH)) { bonus += 5; /* WAG */ + val += 1; } if (government_has_flag(gov, G_FANATIC_TROOPS)) { bonus += 3; /* WAG */ } + val += gov->trade_bonus + gov->shield_bonus + gov->food_bonus; val += (val * bonus) / 100; @@ -396,11 +402,12 @@ val = amortize(val, dist); if (val > best_val && can_change_to_government(pplayer, i)) { best_val = val; - best_gov = i; + ai->goal.revolution = i; } - if (val > really_best_val) { - really_best_val = val; - really_best_req = gov->required_tech; + if (val > ai->goal.govt.val) { + ai->goal.govt.idx = i; + ai->goal.govt.val = val; + ai->goal.govt.req = gov->required_tech; } #ifdef ANALYSE freelog(LOG_NORMAL, "%s govt eval %s (dist %d): %d [f%d|sh%d|l%d|g%d|sc%d|h%d|u%d|a%d|p%d]", @@ -409,20 +416,29 @@ ppl_happy, ppl_unhappy, ppl_angry, pollution); #endif } - if (best_gov != current_gov) { - ai_government_change(pplayer, best_gov); /* change */ - } else { - pplayer->government = current_gov; /* reset */ + pplayer->government = current_gov; /* reset */ + ai->goal.govt.val -= best_val; +} + +/************************************************************************** + Change the government form, if it can and there is a good reason. +**************************************************************************/ +static void ai_manage_government(struct player *pplayer) +{ + struct ai_data *ai = ai_data_get(pplayer); + + if (ai->goal.revolution != pplayer->government) { + ai_government_change(pplayer, ai->goal.revolution); /* change */ } /* Crank up tech want */ - if (get_invention(pplayer, really_best_req) == TECH_KNOWN) { + if (get_invention(pplayer, ai->goal.govt.req) == TECH_KNOWN) { return; /* already got it! */ } - pplayer->ai.tech_want[really_best_req] += (really_best_val - best_val); + pplayer->ai.tech_want[ai->goal.govt.req] += ai->goal.govt.val; freelog(LOG_DEBUG, "%s wants %s with want %d", pplayer->name, - advances[really_best_req].name, - pplayer->ai.tech_want[really_best_req]); + advances[ai->goal.govt.req].name, + pplayer->ai.tech_want[ai->goal.govt.req]); } /************************************************************************** Index: ai/aihand.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aihand.h,v retrieving revision 1.6 diff -u -r1.6 aihand.h --- ai/aihand.h 2002/02/19 16:41:14 1.6 +++ ai/aihand.h 2003/04/07 02:00:03 @@ -22,4 +22,6 @@ bool is_unit_choice_type(enum choice_type type); +void ai_best_government(struct player *pplayer); + #endif /* FC__AIHAND_H */ Index: ai/aitools.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aitools.c,v retrieving revision 1.81 diff -u -r1.81 aitools.c --- ai/aitools.c 2003/02/27 22:14:37 1.81 +++ ai/aitools.c 2003/04/07 02:00:03 @@ -45,6 +45,7 @@ #include "aidata.h" #include "ailog.h" #include "aiunit.h" +#include "citymap.h" #include "aitools.h" @@ -197,6 +198,7 @@ if (punit->ai.ai_role == AIUNIT_BUILD_CITY) { assert(is_normal_map_pos(punit->goto_dest_x, punit->goto_dest_y)); remove_city_from_minimap(punit->goto_dest_x, punit->goto_dest_y); + citymap_free_city_spot(punit->goto_dest_x, punit->goto_dest_y, punit->id); } if (charge && (charge->ai.bodyguard == punit->id)) { @@ -226,6 +228,7 @@ if (punit->ai.ai_role == AIUNIT_BUILD_CITY) { assert(is_normal_map_pos(x, y)); add_city_to_minimap(x, y); + citymap_reserve_city_spot(x, y, punit->id); } } Index: common/tech.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/tech.c,v retrieving revision 1.60 diff -u -r1.60 tech.c --- common/tech.c 2003/04/04 15:47:49 1.60 +++ common/tech.c 2003/04/07 02:00:03 @@ -164,7 +164,7 @@ Tech_Type_id i; enum tech_flag_id flag; - assert(get_invention(pplayer, A_NONE) == TECH_KNOWN); +/* assert(get_invention(pplayer, A_NONE) == TECH_KNOWN); */ for (i = 0; i < game.num_tech_types; i++) { if (!tech_exists(i)) { Index: server/citytools.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/citytools.c,v retrieving revision 1.214 diff -u -r1.214 citytools.c --- server/citytools.c 2003/03/07 05:14:40 1.214 +++ server/citytools.c 2003/04/07 02:00:03 @@ -49,6 +49,7 @@ #include "aicity.h" #include "aiunit.h" +#include "citymap.h" #include "citytools.h" @@ -969,29 +970,18 @@ } /************************************************************************** -... + Create virtual skeleton for a city **************************************************************************/ -void create_city(struct player *pplayer, const int x, const int y, - const char *name) +struct city *create_city_virtual(struct player *pplayer, const int x, + const int y, const char *name) { - struct city *pcity; - int i, x_itr, y_itr; - - freelog(LOG_DEBUG, "Creating city %s", name); - gamelog(GAMELOG_FOUNDC, _("%s (%i, %i) founded by the %s"), name, x, y, - get_nation_name_plural(pplayer->nation)); + int i; - if (terrain_control.may_road) { - map_set_special(x, y, S_ROAD); - if (player_knows_techs_with_flag(pplayer, TF_RAILROAD)) - map_set_special(x, y, S_RAILROAD); - } - send_tile_info(NULL, x, y); + struct city *pcity; pcity=fc_malloc(sizeof(struct city)); - pcity->id=get_next_id_number(); - idex_register_city(pcity); + pcity->id=-1; pcity->owner=pplayer->player_no; pcity->x=x; pcity->y=y; @@ -1036,10 +1026,6 @@ improvement_status_init(pcity->improvements, ARRAY_SIZE(pcity->improvements)); - if (!pplayer->capital) { - pplayer->capital = TRUE; - city_add_improvement(pcity,B_PALACE); - } pcity->turn_last_built = game.year; pcity->changed_from_id = 0; pcity->changed_from_is_unit = FALSE; @@ -1064,14 +1050,54 @@ pcity->tax_bonus = 100; pcity->science_bonus = 100; + unit_list_init(&pcity->units_supported); + + return pcity; +} + +/************************************************************************** +... +**************************************************************************/ +void create_city(struct player *pplayer, const int x, const int y, + const char *name) +{ + struct city *pcity; + int x_itr, y_itr; + + freelog(LOG_DEBUG, "Creating city %s", name); + gamelog(GAMELOG_FOUNDC, _("%s (%i, %i) founded by the %s"), name, x, y, + get_nation_name_plural(pplayer->nation)); + + if (terrain_control.may_road) { + map_set_special(x, y, S_ROAD); + if (player_knows_techs_with_flag(pplayer, TF_RAILROAD)) + map_set_special(x, y, S_RAILROAD); + } + send_tile_info(NULL, x, y); + + pcity = create_city_virtual(pplayer, x, y, name); + pcity->id = get_next_id_number(); + idex_register_city(pcity); + + /* Give a little leeway here, since cities might come from + * the most unexpected places, like huts */ + if (citymap_is_reserved(pcity->x, pcity->y)) { + citymap_free_city_spot(pcity->x, pcity->y, + citymap_read(pcity->x, pcity->y)); + } + citymap_reserve_city_spot(pcity->x, pcity->y, pcity->id); + + if (!pplayer->capital) { + pplayer->capital = TRUE; + city_add_improvement(pcity, B_PALACE); + } + /* Before arranging workers to show unknown land */ map_unfog_pseudo_city_area(pplayer, x, y); map_set_city(x, y, pcity); - unit_list_init(&pcity->units_supported); city_list_insert(&pplayer->cities, pcity); - add_city_to_minimap(x, y); /* it is possible to build a city on a tile that is already worked * this will displace the worker on the newly-built city's tile -- Syela */ @@ -1127,6 +1153,28 @@ } /************************************************************************** + Removes the virtual skeleton of a city. Contains lots of duplicate code, + but there is no easy way around that. +**************************************************************************/ +void remove_city_virtual(struct city *pcity) +{ + /* Explicitly remove all improvements, to properly remove any global effects + and to handle the preservation of "destroyed" effects. */ + bool effect_update=FALSE; + + built_impr_iterate(pcity, i) { + effect_update = TRUE; + city_remove_improvement(pcity, i); + } built_impr_iterate_end; + + city_map_checked_iterate(pcity->x, pcity->y, x, y, mx, my) { + set_worker_city(pcity, x, y, C_TILE_EMPTY); + } city_map_checked_iterate_end; + city_list_unlink(&city_owner(pcity)->cities, pcity); + free(pcity); +} + +/************************************************************************** ... **************************************************************************/ void remove_city(struct city *pcity) @@ -1229,7 +1277,7 @@ acceptable. */ /* DO NOT remove city from minimap here. -- Syela */ - + citymap_free_city_spot(pcity->x, pcity->y, pcity->id); game_remove_city(pcity); players_iterate(other_player) { Index: server/citytools.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/citytools.h,v retrieving revision 1.45 diff -u -r1.45 citytools.h --- server/citytools.h 2003/04/04 15:47:49 1.45 +++ server/citytools.h 2003/04/07 02:00:03 @@ -69,6 +69,9 @@ void reality_check_city(struct player *pplayer,int x, int y); void update_dumb_city(struct player *pplayer, struct city *pcity); +struct city *create_city_virtual(struct player *pplayer, const int x, + const int y, const char *name); +void remove_city_virtual(struct city *pcity); void create_city(struct player *pplayer, const int x, const int y, const char *name); void remove_city(struct city *pcity); Index: server/settlers.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/settlers.c,v retrieving revision 1.166 diff -u -r1.166 settlers.c --- server/settlers.c 2003/04/04 15:47:50 1.166 +++ server/settlers.c 2003/04/07 02:00:03 @@ -38,8 +38,10 @@ #include "aicity.h" #include "aidata.h" #include "ailog.h" +#include "aisettler.h" #include "aitools.h" #include "aiunit.h" +#include "citymap.h" #include "settlers.h" @@ -66,6 +68,11 @@ struct packet_unit_request req; struct city *pcity; + handle_unit_activity_request(punit, ACTIVITY_IDLE); + + /* Free city reservations */ + ai_unit_new_role(punit, AIUNIT_NONE, -1, -1); + req.unit_id = punit->id; sz_strlcpy(req.name, city_name_suggestion(pplayer, x, y)); handle_unit_build_city(pplayer, &req); @@ -1261,7 +1268,7 @@ } /************************************************************************** - find some work for the settler + Find some work for the settler **************************************************************************/ static void auto_settler_findwork(struct player *pplayer, struct unit *punit) { @@ -1306,15 +1313,15 @@ return; } - /* If we intent on building a city then reserve the square. */ - if (unit_flag(punit, F_CITIES) && - best_act == ACTIVITY_UNKNOWN /* flag */) { + /* If we intend to build a city then reserve the square. */ + if (unit_flag(punit, F_CITIES) + && best_act == ACTIVITY_UNKNOWN /* flag */ + && !citymap_is_reserved(gx, gy)) { ai_unit_new_role(punit, AIUNIT_BUILD_CITY, gx, gy); } else { ai_unit_new_role(punit, AIUNIT_AUTO_SETTLER, gx, gy); } - /* We've now worked out what to do; go to it! */ if (!ai_gothere(punit, gx, gy, ferryboat)) { /* died */ return; @@ -1325,7 +1332,6 @@ && same_pos(gx, gy, punit->x, punit->y)) { if (best_act == ACTIVITY_UNKNOWN) { remove_city_from_minimap(gx, gy); /* yeah, I know. -- Syela */ - handle_unit_activity_request(punit, ACTIVITY_IDLE); (void) ai_do_build_city(pplayer, punit); return; } @@ -1336,6 +1342,97 @@ } /************************************************************************** + New implementation of server/settlers.c function with same name +**************************************************************************/ +static void auto_settler_findwork2(struct player *pplayer, struct unit *punit) +{ + struct citycache cache; + int best_impr = 0; /* best terrain improvement we can do */ + enum unit_activity best_act = ACTIVITY_IDLE; /* compat. kludge */ + int gx = -1, gy = -1; + + cache.total = 0; + + assert(pplayer && punit); + assert(unit_flag(punit, F_CITIES) || unit_flag(punit, F_SETTLERS)); + + /*** If we are on a city mission: Go where we should ***/ + + if (punit->ai.ai_role == AIUNIT_BUILD_CITY) { + /* Go there */ + if (do_unit_goto(punit, GOTO_MOVE_ANY, FALSE) == GR_DIED) { + return; + } + if (punit->moves_left <= 0) { + return; + } + if (same_pos(punit->x, punit->y, punit->goto_dest_x, punit->goto_dest_y)) { + if (!ai_do_build_city(pplayer, punit)) { + UNIT_LOG(LOG_ERROR, punit, "could not make city"); + ai_unit_new_role(punit, AIUNIT_NONE, -1, -1); + } else { + return; + } + } else { + UNIT_LOG(LOG_ERROR, punit, "could not go to target"); + ai_unit_new_role(punit, AIUNIT_NONE, -1, -1); + return; + } + } + + /*** Try find some work ***/ + + if (unit_flag(punit, F_SETTLERS)) { + best_impr = evaluate_improvements(punit, &best_act, &gx, &gy); +UNIT_LOG(LOG_NORMAL, punit, "impr want %d (%d, %d)", best_impr, gx, gy); + } + + if (unit_flag(punit, F_CITIES)) { + find_best_city_placement(punit, &cache); +UNIT_LOG(LOG_NORMAL, punit, "city want %d", cache.total); + if (cache.total > best_impr) { +UNIT_LOG(LOG_NORMAL, punit, "makes city at (%d, %d)", cache.x, cache.y); + /* Go make a city! */ + ai_unit_new_role(punit, AIUNIT_BUILD_CITY, cache.x, cache.y); + punit->goto_dest_x = cache.x; /* TMP */ + punit->goto_dest_y = cache.y; /* TMP */ + /* Ensure we reserve best tile other than city center */ + citymap_reserve_tile(cache.other_x, cache.other_y, punit->id); + } else { +UNIT_LOG(LOG_NORMAL, punit, "improves terrain"); + /* Terrain improvements follows the old model, and is recalculated + * each turn. */ + ai_unit_new_role(punit, AIUNIT_AUTO_SETTLER, gx, gy); + punit->goto_dest_x = gx; /* TMP */ + punit->goto_dest_y = gy; /* TMP */ + /* Mark the square as taken. */ + if (gx != -1 && gy != -1) { + map_get_tile(gx, gy)->assigned = + map_get_tile(gx, gy)->assigned | 1<player_no; + } else { + UNIT_LOG(LOG_ERROR, punit, "giving up"); + return; /* We cannot do anything */ + } + if (do_unit_goto(punit, GOTO_MOVE_ANY, FALSE) == GR_DIED) { + return; + } + if (punit->moves_left > 0 + && same_pos(gx, gy, punit->x, punit->y)) { + handle_unit_activity_request(punit, best_act); + send_unit_info(NULL, punit); + return; + } + } + } + + /*** Recurse if we want to found a city ***/ + + if (punit->ai.ai_role == AIUNIT_BUILD_CITY) { + auto_settler_findwork2(pplayer, punit); + } +} + +/************************************************************************** Do all tile improvement calculations and cache them for later. **************************************************************************/ void initialize_infrastructure_cache(struct city *pcity) @@ -1400,9 +1497,12 @@ handle_unit_activity_request(punit, ACTIVITY_IDLE); } if (punit->activity == ACTIVITY_IDLE) { - auto_settler_findwork(pplayer, punit); + if (ai_handicap(pplayer, H_EXPERIMENTAL)) { + auto_settler_findwork2(pplayer, punit); + } else { + auto_settler_findwork(pplayer, punit); + } } - freelog(LOG_DEBUG, "Has been processed."); } } unit_list_iterate_end; @@ -1523,7 +1623,6 @@ */ } - /************************************************************************** Recalculate enemies[] table **************************************************************************/ @@ -1579,7 +1678,14 @@ } virtualunit.moves_left = unit_type(&virtualunit)->move_rate; virtualunit.hp = unit_type(&virtualunit)->hp; - want = evaluate_city_building(&virtualunit, &gx, &gy, &ferryboat); + if (ai_handicap(pplayer, H_EXPERIMENTAL)) { + struct citycache cache; + find_best_city_placement(&virtualunit, &cache); + pcity->ai.founder_want = cache.total; + return; + } else { + want = evaluate_city_building(&virtualunit, &gx, &gy, &ferryboat); + } unit_list_iterate(pplayer->units, qpass) { /* We want a ferryboat with want 199 */ if (qpass->ai.ferryboat == pcity->id) Index: server/srv_main.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/srv_main.c,v retrieving revision 1.119 diff -u -r1.119 srv_main.c --- server/srv_main.c 2003/04/04 15:47:50 1.119 +++ server/srv_main.c 2003/04/07 02:00:03 @@ -94,6 +94,7 @@ #include "advmilitary.h" #include "aidata.h" #include "aihand.h" +#include "citymap.h" #include "srv_main.h" @@ -1110,7 +1111,7 @@ struct nation_type* nation; players_iterate(pplayer) { - if (pplayer->nation == MAX_NUM_NATIONS) { + if (pplayer->nation == NO_NATION_SELECTED) { /* still undecided */ continue; } @@ -1738,6 +1739,7 @@ initialize_move_costs(); /* this may be the wrong place to do this */ generate_minimap(); /* for city_desire; saves a lot of calculations */ + citymap_init(); if (!game.is_new_game) { players_iterate(pplayer) { @@ -1747,6 +1749,7 @@ assess_danger_player(pplayer); /* a slowdown, but a necessary one */ } } players_iterate_end; + citymap_generate(); } /* We want to reset the timer as late as possible but before the info is