Complete.Org: Mailing Lists: Archives: freeciv-ai: May 2004:
[freeciv-ai] (PR#8778) New settler code
Home

[freeciv-ai] (PR#8778) New settler code

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [freeciv-ai] (PR#8778) New settler code
From: "Gregory Berkolaiko" <Gregory.Berkolaiko@xxxxxxxxxxxxx>
Date: Fri, 21 May 2004 10:52:57 -0700
Reply-to: rt@xxxxxxxxxxx

<URL: http://rt.freeciv.org/Ticket/Display.html?id=8778 >

> > By the way, why not restrict overseas search only to coastal spots?
> 
> Yes, that is a bright idea.

Done.  The code is much cleaner now but works the same.

> > Give me a couple of days, I will put in provisions for building
> > ferries.

Done.

> > By the way, when we commit it, we kill the old settling code, right?
> 
> We don't need to. The advantage of keeping it around for a few weeks
> is
> that people can compare the two implementations more easily. But then,
> I am not sure if people will do so anyway...

I am pretty sure nobody will.  Just run a few of your tests, put up the
results as the proof and kill the old code.  After you do that, we
should rearrane how contemplate_new_city is called.  It is now called in
a strange place to re-use the warmap, but this is obsolete.

The new code is attached (I think citymap isn't changed but I include it
just in case).  It builds ferries if they are needed.  When
it considers whether to build a boat it uses a fully-fledged virtual
boat so that if we only know triremes, it won't consider going to
far-away islands.

I think it's more ready than not.  I know one buglet (after a ferry is
loaded it waits for the next turn to go), but I can fix it in situ.

The code depends on the find_ferry2 patch (8777).

G.

Attachment: aisettler.h
Description: Binary data

Attachment: citymap.c
Description: Binary data

? ferry_and_settler.txt
? settle4.gz
? settle5.gz
? stuck.gz
? ai/aisettler.c
? ai/aisettler.h
? common/aicore/citymap.c
? common/aicore/citymap.h
Index: ai/Makefile.am
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/Makefile.am,v
retrieving revision 1.17
diff -u -r1.17 Makefile.am
--- ai/Makefile.am      2 May 2004 12:13:51 -0000       1.17
+++ ai/Makefile.am      21 May 2004 17:46:53 -0000
@@ -27,6 +27,8 @@
                aihunt.h        \
                ailog.c         \
                ailog.h         \
+               aisettler.c     \
+               aisettler.h     \
                aitech.c        \
                aitech.h        \
                aitools.c       \
Index: ai/aidata.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.c,v
retrieving revision 1.24
diff -u -r1.24 aidata.c
--- ai/aidata.c 1 May 2004 03:22:11 -0000       1.24
+++ ai/aidata.c 21 May 2004 17:46:56 -0000
@@ -226,6 +226,10 @@
       }
     }
   } unit_list_iterate_end;
+  freelog(LOG_NORMAL, "Boat stats: free %d of total %d.  Passengers: %d",
+         ai->stats.available_boats, ai->stats.boats,
+         ai->stats.passengers);
+
 
   /*** Diplomacy ***/
 
@@ -305,6 +309,9 @@
   ai->angry_priority = TRADE_WEIGHTING * 3; /* grave danger */
   ai->pollution_priority = POLLUTION_WEIGHTING;
 
+  ai->growth_priority = 15; /* WAG */
+
+  /* Goals */
   ai_best_government(pplayer);
 
   /*** Interception engine ***/
@@ -368,6 +375,26 @@
   struct ai_data *ai = &aidata[pplayer->player_no];
   int i;
 
+  /* The values below are WAGs that have been optimized to the
+   * default rules. They are not changed during the game. */
+
+  /* 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. Decrease for maps where good city placements 
+   * are hard to find. Lower means more perfection. */
+  ai->perfection = 3;
+  /* How much to deemphasise the potential for city growth for city
+   * placements. Decrease this value if large cities are important
+   * or planned. Increase if running strict smallpox. */
+  ai->growth_potential_deemphasis = 8;
+  /* Percentage bonus to city locations near an ocean. */
+  ai->naval_emphasis = 20;
+  /* Modifier for defense bonus that is applied to city location want.
+   * Increase this to lower emphasis on defensive positions, increase
+   * it if you want more emphasis on heavily defended cities. */
+  ai->defense_emphasis = 4000;
+
+  /* Government variables */
   ai->govt_reeval = 0;
   ai->government_want = fc_calloc(game.government_count + 1, sizeof(int));
 
@@ -466,3 +493,4 @@
 
   free(ai->government_want);
 }
+
Index: ai/aidata.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.h,v
retrieving revision 1.11
diff -u -r1.11 aidata.h
--- ai/aidata.h 9 Oct 2003 00:07:33 -0000       1.11
+++ ai/aidata.h 21 May 2004 17:46:56 -0000
@@ -91,9 +91,11 @@
   struct {
     int *workers;     /* cities to workers on continent*/
     int *cities;      /* number of cities on continent */
+
     int passengers;   /* number of passengers waiting for boats */
-    int boats;
-    int available_boats;
+    int boats;        /* total number of boats */
+    int available_boats; /* number of boats not booked or used */
+
     int average_production;
     bv_id diplomat_reservations;
   } stats;
@@ -110,6 +112,11 @@
   int unhappy_priority;
   int angry_priority;
   int pollution_priority;
+  int growth_priority;
+  int perfection;
+  int growth_potential_deemphasis;
+  int naval_emphasis;
+  int defense_emphasis;
 
   /* Government data */
   int *government_want;
Index: ai/ailog.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/ailog.c,v
retrieving revision 1.12
diff -u -r1.12 ailog.c
--- ai/ailog.c  2 May 2004 12:33:29 -0000       1.12
+++ ai/ailog.c  21 May 2004 17:46:57 -0000
@@ -120,9 +120,6 @@
   if (punit->debug) {
     minlevel = LOG_NORMAL;
   } else {
-    if (minlevel > fc_log_level) {
-      return;
-    }
     /* Are we a virtual unit evaluated in a debug city?. */
     if (punit->id == 0) {
       struct city *pcity = map_get_city(punit->x, punit->y);
@@ -131,6 +128,9 @@
         minlevel = LOG_NORMAL;
       }
     }
+    if (minlevel > fc_log_level) {
+      return;
+    }
   }
 
   if (is_goto_dest_set(punit)) {
Index: ai/aitools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitools.c,v
retrieving revision 1.104
diff -u -r1.104 aitools.c
--- ai/aitools.c        19 May 2004 21:03:12 -0000      1.104
+++ ai/aitools.c        21 May 2004 17:47:00 -0000
@@ -15,6 +15,7 @@
 #include <config.h>
 #endif
 
+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -48,6 +49,7 @@
 #include "aidata.h"
 #include "ailog.h"
 #include "aiunit.h"
+#include "citymap.h"
 
 #include "aitools.h"
 
@@ -453,7 +455,9 @@
   }
 
   if (punit->ai.ai_role == AIUNIT_BUILD_CITY) {
+    assert(is_normal_map_pos(goto_dest_x(punit), goto_dest_y(punit)));
     remove_city_from_minimap(goto_dest_x(punit), goto_dest_y(punit));
+    citymap_free_city_spot(goto_dest_x(punit), goto_dest_y(punit), punit->id);
   }
 
   if (punit->ai.ai_role == AIUNIT_HUNTER) {
@@ -488,9 +492,11 @@
     ai_unit_new_role(bodyguard, AIUNIT_NONE, -1, -1);
   }
 
-  if (punit->ai.ai_role == AIUNIT_BUILD_CITY) {
+  /* Reserve city spot, _unless_ we want to add ourselves to a city. */
+  if (punit->ai.ai_role == AIUNIT_BUILD_CITY && !map_get_city(x, y)) {
     assert(is_normal_map_pos(x, y));
     add_city_to_minimap(x, y);
+    citymap_reserve_city_spot(x, y, punit->id);
   }
   if (punit->ai.ai_role == AIUNIT_HUNTER) {
     /* Set victim's hunted bit - the hunt is on! */
Index: common/aicore/Makefile.am
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/Makefile.am,v
retrieving revision 1.6
diff -u -r1.6 Makefile.am
--- common/aicore/Makefile.am   2 May 2004 12:13:51 -0000       1.6
+++ common/aicore/Makefile.am   21 May 2004 17:47:02 -0000
@@ -12,4 +12,6 @@
        pf_tools.c              \
        pf_tools.h              \
        cm.c                    \
-       cm.h
+       cm.h                    \
+       citymap.c               \
+       citymap.h
Index: common/aicore/pf_tools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/pf_tools.c,v
retrieving revision 1.14
diff -u -r1.14 pf_tools.c
--- common/aicore/pf_tools.c    19 May 2004 00:49:31 -0000      1.14
+++ common/aicore/pf_tools.c    21 May 2004 17:47:04 -0000
@@ -528,7 +528,6 @@
   parameter->get_TB = NULL;
   parameter->get_EC = NULL;
   BV_CLR_ALL(parameter->unit_flags);
-  parameter->omniscience = TRUE;
 
   parameter->start_x = punit->x;
   parameter->start_y = punit->y;
@@ -538,8 +537,6 @@
   parameter->unit_flags = unit_type(punit)->flags;
 
   parameter->omniscience = !ai_handicap(unit_owner(punit), H_MAP);
-
-
 }
 
 /**********************************************************************
Index: server/settlers.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/settlers.c,v
retrieving revision 1.181
diff -u -r1.181 settlers.c
--- server/settlers.c   19 May 2004 14:40:15 -0000      1.181
+++ server/settlers.c   21 May 2004 17:47:17 -0000
@@ -39,8 +39,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"
 
@@ -54,9 +56,6 @@
 BV_DEFINE(enemy_mask, MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS);
 static enemy_mask enemies[MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS];
 
-static void auto_settler_findwork(struct player *pplayer, 
-                                  struct unit *punit);
-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);
@@ -69,6 +68,11 @@
   int x = punit->x, y = punit->y;
   struct city *pcity;
 
+  handle_unit_activity_request(punit, ACTIVITY_IDLE);
+
+  /* Free city reservations */
+  ai_unit_new_role(punit, AIUNIT_NONE, -1, -1);
+
   handle_unit_build_city(pplayer, punit->id,
                         city_name_suggestion(pplayer, x, y));
   pcity = map_get_city(x, y);
@@ -1207,7 +1211,7 @@
 }
 
 /**************************************************************************
-  find some work for the settler
+  Find some work for the settler
 **************************************************************************/
 static void auto_settler_findwork(struct player *pplayer, struct unit *punit)
 {
@@ -1252,9 +1256,9 @@
     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 */) {
     ai_unit_new_role(punit, AIUNIT_BUILD_CITY, gx, gy);
   } else {
     ai_unit_new_role(punit, AIUNIT_AUTO_SETTLER, gx, gy);
@@ -1271,7 +1275,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;
     }
@@ -1282,6 +1285,117 @@
 }
 
 /**************************************************************************
+  New implementation of server/settlers.c function with same name
+**************************************************************************/
+#define LOG_SETTLER LOG_NORMAL
+static void auto_settler_findwork2(struct player *pplayer, struct unit *punit)
+{
+  struct cityresult result;
+  int best_impr = 0;            /* best terrain improvement we can do */
+  enum unit_activity best_act = ACTIVITY_IDLE; /* compat. kludge */
+  int gx = -1, gy = -1;
+
+  CHECK_UNIT(punit);
+
+  result.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) {
+    int x = goto_dest_x(punit), y = goto_dest_y(punit), sanity = punit->id;
+
+    /* Check that missions is still possible */
+    if (!city_can_be_built_here(x, y, punit)) {
+      UNIT_LOG(LOG_SETTLER, punit, "city founding mission failed");
+      ai_unit_new_role(punit, AIUNIT_NONE, -1, -1);
+      return; /* avoid recursion at all cost */
+    } else {
+      /* Go there */
+      if ((!ai_gothere(pplayer, punit, x, y) && !find_unit_by_id(sanity))
+          || punit->moves_left <= 0) {
+        return;
+      }
+      if (same_pos(punit->x, punit->y, x, y)) {
+        if (!ai_do_build_city(pplayer, punit)) {
+          UNIT_LOG(LOG_ERROR, punit, "could not make city on %s",
+                   map_get_tile_info_text(punit->x, punit->y));
+          ai_unit_new_role(punit, AIUNIT_NONE, -1, -1);
+        } else {
+          return; /* We came, we saw, we built... */
+        }
+      } else {
+        UNIT_LOG(LOG_SETTLER, punit, "could not go to target");
+        /* ai_unit_new_role(punit, AIUNIT_NONE, -1, -1); */
+        return;
+      }
+    }
+  }
+
+  CHECK_UNIT(punit);
+
+  /*** Try find some work ***/
+
+  if (unit_flag(punit, F_SETTLERS)) {
+    best_impr = evaluate_improvements(punit, &best_act, &gx, &gy);
+  }
+
+  if (unit_flag(punit, F_CITIES)) {
+    find_best_city_placement(punit, &result, TRUE, FALSE);
+    UNIT_LOG(LOG_SETTLER, punit, "city want %d (impr want %d)", result.total,
+             best_impr);
+    if (result.total > best_impr) {
+      if (map_get_city(result.x, result.y)) {
+        UNIT_LOG(LOG_SETTLER, punit, "immigrates to %s (%d, %d)", 
+                 map_get_city(result.x, result.y), result.x, result.y);
+      } else {
+        UNIT_LOG(LOG_SETTLER, punit, "makes city at (%d, %d)", 
+                 result.x, result.y);
+      }
+      /* Go make a city! */
+      ai_unit_new_role(punit, AIUNIT_BUILD_CITY, result.x, result.y);
+      set_goto_dest(punit, result.x, result.y); /* TMP */
+    } else if (best_impr > 0) {
+      UNIT_LOG(LOG_SETTLER, punit, "improves terrain");
+      /* Terrain improvements follows the old model, and is recalculated
+       * each turn. */
+      ai_unit_new_role(punit, AIUNIT_AUTO_SETTLER, gx, gy);
+      /* Mark the square as taken. */
+      if (gx != -1 && gy != -1) {
+        map_get_tile(gx, gy)->assigned =
+          map_get_tile(gx, gy)->assigned | 1<<pplayer->player_no;
+      } else {
+        UNIT_LOG(LOG_NORMAL, punit, "giving up trying to improve terrain");
+        return; /* We cannot do anything */
+      }
+      set_goto_dest(punit, gx, gy); /* TMP */
+      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;
+      }
+    } else {
+      UNIT_LOG(LOG_SETTLER, punit, "cannot find work");
+      ai_unit_new_role(punit, AIUNIT_NONE, -1, -1);
+      return;
+    }
+  }
+
+  /*** Recurse if we want to found a city ***/
+
+  if (punit->ai.ai_role == AIUNIT_BUILD_CITY) {
+    auto_settler_findwork2(pplayer, punit);
+  }
+}
+#undef LOG_SETTLER
+
+/**************************************************************************
   Do all tile improvement calculations and cache them for later.
 **************************************************************************/
 void initialize_infrastructure_cache(struct city *pcity)
@@ -1316,15 +1430,20 @@
 }
 
 /************************************************************************** 
-  run through all the players settlers and let those on ai.control work 
-  automagically
+  Run through all the players settlers and let those on ai.control work 
+  automagically.
 **************************************************************************/
-static void auto_settlers_player(struct player *pplayer) 
+void auto_settlers_player(struct player *pplayer) 
 {
   static struct timer *t = NULL;      /* alloc once, never free */
 
   t = renew_timer_start(t, TIMER_CPU, TIMER_DEBUG);
 
+  if (pplayer->ai.control && ai_handicap(pplayer, H_EXPERIMENTAL)) {
+    /* Set up our city map. */
+    citymap_turn_init(pplayer);
+  }
+
   city_list_iterate(pplayer->cities, pcity)
     initialize_infrastructure_cache(pcity); /* saves oodles of time -- Syela */
   city_list_iterate_end;
@@ -1352,9 +1471,12 @@
         handle_unit_activity_request(punit, ACTIVITY_IDLE);
       }
       if (punit->activity == ACTIVITY_IDLE) {
-       auto_settler_findwork(pplayer, punit);
+        if (pplayer->ai.control && 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;
@@ -1473,7 +1595,6 @@
    */
 }  
 
-
 /**************************************************************************
   Recalculate enemies[] table
 **************************************************************************/
@@ -1489,9 +1610,9 @@
 }
 
 /**************************************************************************
-  Do the auto_settler stuff for all the players. 
+  Initialize autosettler code.
 **************************************************************************/
-void auto_settlers(void)
+void auto_settlers_init(void)
 {
   assign_settlers();
   assign_territory();
@@ -1502,8 +1623,8 @@
 }
 
 /**************************************************************************
-used to use old crappy formulas for settler want, but now using actual
-want!
+  Return want for city settler. Note that we rely here on the fact that
+  ai_settler_init() has been run while doing autosettlers.
 **************************************************************************/
 void contemplate_new_city(struct city *pcity)
 {
@@ -1523,24 +1644,38 @@
   virtualunit = create_unit_virtual(pplayer, pcity, unit_type, 0);
   virtualunit->x = pcity->x;
   virtualunit->y = pcity->y;
-  want = evaluate_city_building(virtualunit, &gx, &gy, &ferryboat);
-  free(virtualunit);
-
-  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;
 
-  /* Did we count on using an existing boat.  If yes we need to keep it
-   * in the city. */
-  pcity->ai.founder_boat = (ferryboat != NULL);
+  assert(pplayer->ai.control);
 
-  if (gx == -1) {
-    pcity->ai.founder_want = -want;
+  if (pplayer->ai.control && ai_handicap(pplayer, H_EXPERIMENTAL)) {
+    struct cityresult result;
+    bool is_coastal = is_at_coast(pcity->x, pcity->y);
+
+    find_best_city_placement(virtualunit, &result, is_coastal, is_coastal);
+
+    CITY_LOG(LOG_NORMAL, pcity, "wants (%d much) to establish new city at"
+            " (%d, %d) and will %s to get there", result.total, 
+            result.x, result.y, 
+            (result.virt_boat ? "build a boat" : 
+             (result.overseas ? "use a boat" : "walk")));
+
+    pcity->ai.founder_want = (result.virt_boat ? 
+                             -result.total : result.total);
+    pcity->ai.founder_boat = result.overseas;
   } else {
-    pcity->ai.founder_want = want; /* boat */
+    want = evaluate_city_building(virtualunit, &gx, &gy, &ferryboat);
+
+    /* Did we count on using an existing boat.  If yes we need to keep it
+     * in the city. */
+    pcity->ai.founder_boat = (ferryboat != NULL);
+    
+    if (gx == -1) {
+      pcity->ai.founder_want = -want;
+    } else {
+      pcity->ai.founder_want = want; /* boat */
+    }
   }
+  free(virtualunit);
 }
 
 /**************************************************************************
Index: server/settlers.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/settlers.h,v
retrieving revision 1.23
diff -u -r1.23 settlers.h
--- server/settlers.h   23 Sep 2003 15:59:05 -0000      1.23
+++ server/settlers.h   21 May 2004 17:47:18 -0000
@@ -19,7 +19,8 @@
 struct unit;
 struct city;
 
-void auto_settlers(void);
+void auto_settlers_init(void);
+void auto_settlers_player(struct player *pplayer);
 int find_boat(struct player *pplayer, int *x, int *y, int cap);
 
 #define MORT 24
Index: server/srv_main.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/srv_main.c,v
retrieving revision 1.160
diff -u -r1.160 srv_main.c
--- server/srv_main.c   2 May 2004 14:47:12 -0000       1.160
+++ server/srv_main.c   21 May 2004 17:47:24 -0000
@@ -97,6 +97,8 @@
 #include "advmilitary.h"
 #include "aidata.h"
 #include "aihand.h"
+#include "aisettler.h"
+#include "citymap.h"
 
 #include "srv_main.h"
 
@@ -488,8 +490,13 @@
   nocity_send = TRUE;
 
   /* AI end of turn activities */
+  auto_settlers_init();
   players_iterate(pplayer) {
     if (pplayer->ai.control) {
+      ai_settler_init(pplayer);
+    }
+    auto_settlers_player(pplayer);
+    if (pplayer->ai.control) {
       ai_do_last_activities(pplayer);
     }
   } players_iterate_end;
@@ -1403,8 +1410,6 @@
     summon_barbarians(); /* wild guess really, no idea where to put it, but
                             I want to give them chance to move their units */
     /* Moved this to after the human turn for efficiency -- Syela */
-    freelog(LOG_DEBUG, "Autosettlers");
-    auto_settlers();
     freelog(LOG_DEBUG, "Auto-Attack phase");
     auto_attack();
     freelog(LOG_DEBUG, "Endturn");

Attachment: aisettler.c
Description: Binary data

Attachment: citymap.h
Description: Binary data


[Prev in Thread] Current Thread [Next in Thread]