(PR#8992) Patch: Building ferries

"Gregory Berkolaiko" <[email protected]>
Newsgroups gmane.games.freeciv.ai
Message-ID <[email protected]>
<URL: http://rt.freeciv.org/Ticket/Display.html?id=8992 >

I am forwarding the last patch I made in this series.  It is pretty old
(8 Aug) but it has some improvements over previous versions and I tested
it to prove that indeed we build less boats.

It still doesn't use ocean numbers (or Per's channels patch 9908).  It
contains some code that should be deleted rather than commented out.  It
is not likely to apply to CVS.

But in all other aspects it's ready for inclusion ;)
central_boats3.diff (application/octet-stream, 10.2 KB)
? utility/ftwl
Index: ai/advdomestic.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/advdomestic.c,v
retrieving revision 1.114
diff -u -r1.114 advdomestic.c
--- ai/advdomestic.c	6 Aug 2004 16:46:24 -0000	1.114
+++ ai/advdomestic.c	8 Aug 2004 16:55:20 -0000
@@ -1064,7 +1064,7 @@
       choice->type = CT_NONMIL;
       choice->choice = unit_type; /* default */
       choice->need_boat = TRUE;
-      ai_choose_role_unit(pplayer, pcity, choice, L_FERRYBOAT, -want);
+      // ai_choose_role_unit(pplayer, pcity, choice, L_FERRYBOAT, -want);
     }
   }
 
Index: ai/advmilitary.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/advmilitary.c,v
retrieving revision 1.169
diff -u -r1.169 advmilitary.c
--- ai/advmilitary.c	25 Jun 2004 23:43:00 -0000	1.169
+++ ai/advmilitary.c	8 Aug 2004 16:55:20 -0000
@@ -1075,11 +1075,14 @@
   if (best_choice.want > choice->want) {
     /* We want attacker more that what we have selected before */
     copy_if_better_choice(&best_choice, choice);
+    /* 
     if (go_by_boat && !ferryboat) {
       ai_choose_role_unit(pplayer, pcity, choice, L_FERRYBOAT, choice->want);
     }
-    freelog(LOG_DEBUG, "%s has chosen attacker, %s, want=%d",
-            pcity->name, unit_types[choice->choice].name, choice->want);
+    */
+    CITY_LOG(LOG_DEBUG, pcity, "has chosen attacker, %s, want=%d%s",
+	     unit_types[choice->choice].name, choice->want, 
+	     (best_choice.need_boat ? "(need boat)" : ""));
   } 
 }
 
Index: ai/aicity.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aicity.c,v
retrieving revision 1.159
diff -u -r1.159 aicity.c
--- ai/aicity.c	20 Jul 2004 10:05:43 -0000	1.159
+++ ai/aicity.c	8 Aug 2004 16:55:21 -0000
@@ -44,6 +44,7 @@
 #include "advdomestic.h"
 #include "advmilitary.h"
 #include "aidata.h"
+#include "aiferry.h"
 #include "aihand.h"
 #include "ailog.h"
 #include "aitools.h"
@@ -597,6 +598,8 @@
     ai_city_choose_build(pplayer, pcity);
   } city_list_iterate_end;
 
+  aiferry_choose_build_global(pplayer);
+
   ai_spend_gold(pplayer);
 }
 
Index: ai/aiferry.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiferry.c,v
retrieving revision 1.3
diff -u -r1.3 aiferry.c
--- ai/aiferry.c	6 Aug 2004 14:46:28 -0000	1.3
+++ ai/aiferry.c	8 Aug 2004 16:55:22 -0000
@@ -27,6 +27,7 @@
 
 #include "aidata.h"
 #include "aiexplorer.h"
+#include "aihand.h"
 #include "ailog.h"
 #include "aitools.h"
 #include "aiunit.h"
@@ -44,7 +45,7 @@
  * The below is used only by passengers in ai.ferryboat field 
  */ 
 #define FERRY_WANTED      -1      /* Needs a boat */
-#define FERRY_NONE         0      /* Has no boa and doesn't need one */
+#define FERRY_NONE         0      /* Has no boat and doesn't need one */
 
 
 /* =================== group log levels, debug options ================= */
@@ -58,7 +59,6 @@
 /* Extra consistency checks */
 #define DEBUG_FERRY_STATS 0
 
-
 /* ========= managing statistics and boat/passanger assignments ======== */
 
 /**************************************************************************
@@ -73,7 +73,7 @@
   ai->stats.available_boats = 0;
  
   unit_list_iterate(pplayer->units, punit) {
-    if (is_sailing_unit(punit) && is_ground_units_transport(punit)) {
+    if (unit_has_role(punit->type, L_FERRYBOAT)) {
       ai->stats.boats++;
       if (punit->ai.passenger == FERRY_AVAILABLE) {
 	ai->stats.available_boats++;
@@ -95,8 +95,8 @@
   struct ai_data *ai = ai_data_get(pplayer);
   int n = 1;
 
-  freelog(LOG_NORMAL, "Boat stats for %s[%d]", 
-	  pplayer->name, pplayer->player_no);
+  freelog(LOG_NORMAL, "Boat stats for %s[%d] in turn %d", 
+	  pplayer->name, pplayer->player_no, game.turn);
   freelog(LOG_NORMAL, "Registered: %d free out of total %d",
 	  ai->stats.available_boats, ai->stats.boats);
   unit_list_iterate(pplayer->units, punit) {
@@ -851,3 +851,190 @@
  
   return;
 }
+
+/**************************************************************************
+  A cost function for a sea unit which allows going one step into the land.
+  
+  Things to remember: we should prevent going from land to anywhere, unless 
+  we are leaving a city, in which case we can move into the ocean but not 
+  into the land.
+**************************************************************************/
+static int overlap_move(int x, int y, enum direction8 dir,
+			    int x1, int y1, struct pf_parameter *param)
+{
+  if (is_ocean(map_get_terrain(x, y))) {
+    return SINGLE_MOVE;
+  } else if (is_allied_city_tile(map_get_tile(x, y), param->owner)
+	     && is_ocean(map_get_terrain(x1, y1))) {
+    return SINGLE_MOVE;
+  }
+
+  return PF_IMPOSSIBLE_MC;
+}
+
+/* Least Common Denominator of 2,3,..,8 (a very divisible number) */
+#define DENOM 840
+/****************************************************************************
+  A global ferry build selector.  Estimates our want for the boats and finds
+  cities who will build them.
+
+  FIXME: Gross hack, uses pcity->ai.founder_want field for its local 
+  calculations.
+****************************************************************************/
+void aiferry_choose_build_global(struct player *pplayer)
+{
+  struct city *pcity;
+  int inf_loop_guard = city_list_size(&pplayer->cities);
+  /* Path-finding stuff */
+  struct pf_map *pfmap;
+  struct pf_parameter parameter;
+
+  /* Initialize */
+  city_list_iterate(pplayer->cities, acity) {
+    acity->ai.founder_want = 0;
+  } city_list_iterate_end;
+  parameter.turn_mode = TM_NONE;
+  parameter.get_MC = overlap_move;
+  parameter.get_TB = NULL;
+  parameter.get_EC = NULL;
+  parameter.get_moveEC = NULL;
+  parameter.get_zoc = NULL;
+  parameter.is_pos_dangerous = NULL;
+  BV_CLR_ALL(parameter.unit_flags);
+  parameter.owner = pplayer;
+  parameter.omniscience = !ai_handicap(pplayer, H_MAP);
+  /* These don't matter */
+  parameter.moves_left_initially = 0;
+  parameter.move_rate = 3;
+
+  do {
+    int future_psngrs = 0, psngrs = 0;
+    int boats = 0, avail_boats = 0;
+    int boats_to_be_built;
+
+    /* Find unprocessed city */
+    pcity = NULL;
+    city_list_iterate(pplayer->cities, acity) {
+      if (acity->ai.founder_want == 0 
+	  && is_ocean_near_tile(acity->x, acity->y)) {
+	pcity = acity;
+	break;
+      }
+    } city_list_iterate_end;
+
+    if (!pcity) {
+      break;
+    }
+
+    /* Part 1: estimate the demand for the boats */
+
+    parameter.start_x = pcity->x;
+    parameter.start_y = pcity->y;
+    pfmap = pf_create_map(&parameter);
+
+    /* We want to consider the place we are currently in too, hence the 
+     * do-while loop */
+    do {
+      struct pf_position pos;
+      struct city *acity;
+
+      pf_next_get_position(pfmap, &pos);
+      acity = map_get_city(pos.x, pos.y);
+
+      if (acity && acity->owner == pplayer->player_no) {
+	assert(acity->ai.founder_want == 0);
+	acity->ai.founder_want = 1;
+
+	if (acity->ai.choice.need_boat) {
+	  int turns_to_build = 
+	    city_turns_to_build(acity, acity->ai.choice.choice, 
+				is_unit_choice_type(acity->ai.choice.type),
+				TRUE);
+
+	  future_psngrs += DENOM / turns_to_build;
+	}
+      }
+
+      unit_list_iterate(map_get_tile(pos.x, pos.y)->units, aunit) {
+	if (aunit->owner != pplayer->player_no) {
+	  continue;
+	}
+	if (unit_has_role(aunit->type, L_FERRYBOAT)) {
+	  boats++;
+	  if (aunit->ai.passenger == FERRY_AVAILABLE) {
+	    avail_boats++;
+	  }
+	  if (aunit->ai.passenger == 0) {
+	    freelog(LOG_ERROR, "Passenger field set to 0");
+	    avail_boats++;
+	  }
+	} else if (aunit->ai.ferryboat == FERRY_WANTED) {
+	  psngrs++;
+	}
+      } unit_list_iterate_end;      
+    } while (pf_next(pfmap));
+
+    pf_destroy_map(pfmap);
+
+    freelog(LOG_NORMAL, "Sea area accessible from %s has %d boats (%d free)"
+	    " and %d units waiting for boats",
+	    pcity->name, boats, avail_boats, psngrs);
+    freelog(LOG_NORMAL, "Future passenger score is %d.", future_psngrs);
+
+    /* Part 2: Decide on how many boats we want built */
+
+    /* WAG rules: idle boats count twice, future passangers 
+     * count as half */ 
+    psngrs = psngrs * DENOM + MAX(0, future_psngrs - boats * DENOM);
+    boats = (boats + avail_boats) * DENOM;
+    boats_to_be_built = (psngrs > boats ? 1 : 0);
+
+    freelog(LOG_NORMAL, "Boat-want is %d, so %d boats will be built", 
+	    psngrs - boats, boats_to_be_built);
+
+    /* Part 3: Decide where to build it */
+    
+    while (boats_to_be_built > 0) {
+      Unit_Type_id boattype = best_role_unit_for_player(pplayer, L_FERRYBOAT);
+      int best = FC_INFINITY;
+      struct city *bestcity = NULL;
+
+      if (boattype == U_LAST) {
+	/* Stimulate tech A LOT!!! */
+	break;
+      }
+
+      city_list_iterate(pplayer->cities, acity) {
+	if (acity->ai.founder_want == 1 
+	    && acity->ai.choice.need_boat
+	    && city_turns_to_build(acity, boattype, TRUE, TRUE) < best) {
+	  assert(is_ocean_near_tile(pcity->x, pcity->y));
+	  best = city_turns_to_build(acity, boattype, TRUE, TRUE);
+	  bestcity = acity;
+	}
+      } city_list_iterate_end;
+
+      if (!bestcity) {
+	break;
+      }
+
+      /* FIXME: separate a function ai_set_build from 
+       * aicity.c:ai_city_choose_build and use it here */
+      CITY_LOG(LOG_NORMAL, bestcity, "will build a boat");
+      bestcity->currently_building = boattype;
+      pcity->is_building_unit = TRUE;
+      boats_to_be_built--;
+    }
+
+    /* Part 4: Cleanup */
+    city_list_iterate(pplayer->cities, acity) {
+      if (acity->ai.founder_want == 1) { 
+	  acity->ai.founder_want = 2;
+      }
+    } city_list_iterate_end;
+
+    inf_loop_guard--;
+  } while(inf_loop_guard > 0);
+}
+
+#undef DENOM
Index: ai/aiferry.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiferry.h,v
retrieving revision 1.1
diff -u -r1.1 aiferry.h
--- ai/aiferry.h	20 Jun 2004 07:41:14 -0000	1.1
+++ ai/aiferry.h	8 Aug 2004 16:55:22 -0000
@@ -48,4 +48,10 @@
  */
 void ai_manage_ferryboat(struct player *pplayer, struct unit *punit);
 
+/* 
+ * A function for a centralized (i.e. per-civilization rather than per-city
+ * estimation of need to build more ferries and where to build them
+ */
+void aiferry_choose_build_global(struct player *pplayer);
+
 #endif /* FC__AIFERRY_H */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.