(PR#9908) Make AI understand channels

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

> It seems that the transitivity is not taken into account (i.e. A->B  and
>  B->C  =>  A->C).

Fixed.

> Also you can make it a single array by associating to each ocean the
> smallest of the ocean numbers which are connected to it.  But I am not
> sure if it's worth the trouble.

Couldn't do in any reasonable way.

Also fixed: 
* in is_goto_sane if we are a boat and happen to be in a city, we still
understand that it is ok to go to an ocean
* in ai_channel we receive the result of map_get_continent which is
negative if it's an ocean, so we treat it correspondingly.  if it isn't
negative, we return FALSE.
channel2.diff (text/plain, 7.4 KB)
Index: ai/aidata.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.c,v
retrieving revision 1.46
diff -u -r1.46 aidata.c
--- ai/aidata.c	23 Oct 2004 19:01:01 -0000	1.46
+++ ai/aidata.c	11 Nov 2004 06:09:46 -0000
@@ -134,7 +134,8 @@
 void ai_data_turn_init(struct player *pplayer) 
 {
   struct ai_data *ai = &aidata[pplayer->player_no];
-  int i, nuke_units = num_role_units(F_NUCLEAR);
+  int i, j, k;
+  int nuke_units = num_role_units(F_NUCLEAR);
   bool danger_of_nukes = FALSE;
   int ally_strength = -1;
   struct player *ally_strongest = NULL;
@@ -225,6 +226,52 @@
   /* Increase from fear to terror if opponent actually has nukes */
   if (danger_of_nukes) ai->threats.nuclear++; /* sum of both fears */
 
+  /*** Channels ***/
+
+  /* Ways to cross from one ocean to another through a city. */
+  ai->channels = fc_calloc((ai->num_oceans + 1) * (ai->num_oceans + 1), sizeof(int));
+  players_iterate(aplayer) {
+    if (pplayers_allied(pplayer, aplayer)) {
+      city_list_iterate(aplayer->cities, pcity) {
+        adjc_iterate(pcity->tile, tile1) {
+          if (is_ocean(tile1->terrain)) {
+            adjc_iterate(pcity->tile, tile2) {
+              if (is_ocean(tile2->terrain) 
+                  && map_get_continent(tile1) != map_get_continent(tile2)) {
+                ai->channels[(-tile1->continent) * ai->num_oceans
+                             + (-tile2->continent)] = TRUE;
+                ai->channels[(-tile2->continent) * ai->num_oceans
+                             + (-tile1->continent)] = TRUE;
+              }
+            } adjc_iterate_end;
+          }
+        } adjc_iterate_end;
+      } city_list_iterate_end;
+    }
+  } players_iterate_end;
+
+  /* If we can go i -> j and j -> k, we can also go i -> k. */
+  for(i = 1; i <= ai->num_oceans; i++) {
+    for(j = 1; j <= ai->num_oceans; j++) {
+      if (ai->channels[i * ai->num_oceans + j]) {
+        for(k = 1; k <= ai->num_oceans; k++) {
+          ai->channels[i * ai->num_oceans + k] |= 
+            ai->channels[j * ai->num_oceans + k];
+        }
+      }
+    }
+  }
+#if 0
+  freelog(LOG_NORMAL, "Player %s:", pplayer->name);
+  for(i = 1; i <= ai->num_oceans; i++) {
+    for(j = 1; j <= ai->num_oceans; j++) {
+      if (ai->channels[i * ai->num_oceans + j]) {
+        freelog(LOG_NORMAL, "oceans %d and %d are connected", i, j);
+      }
+    }
+  }
+#endif
+
   /*** Exploration ***/
 
   ai->explore.land_done = TRUE;
@@ -426,6 +473,9 @@
 
   free(ai->stats.cities);
   ai->stats.cities = NULL;
+
+  free(ai->channels);
+  ai->channels = NULL;
 }
 
 /**************************************************************************
@@ -458,7 +508,7 @@
 				    * sizeof(*ai->government_want)));
   memset(ai->government_want, 0,
 	 (game.government_count + 1) * sizeof(*ai->government_want));
-
+  ai->channels = NULL;
   ai->diplomacy.target = NULL;
   ai->diplomacy.strategy = WIN_OPEN;
   ai->diplomacy.timer = 0;
@@ -481,3 +531,17 @@
     ai->diplomacy.player_intel[i].warned_about_space = 0;
   }
 }
+
+/**************************************************************************
+  Is there a channel going from ocean c1 to ocean c2?
+  Returns FALSE if either is not an ocean.
+**************************************************************************/
+bool ai_channel(struct player *pplayer, Continent_id c1, Continent_id c2)
+{
+  struct ai_data *ai = ai_data_get(pplayer);
+
+  if (c1 >= 0 || c2 >= 0) {
+    return FALSE;
+  }
+  return (c1 == c2 || ai->channels[(-c1) * ai->num_oceans + (-c2)]);
+}
Index: ai/aidata.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.h,v
retrieving revision 1.19
diff -u -r1.19 aidata.h
--- ai/aidata.h	23 Oct 2004 19:01:01 -0000	1.19
+++ ai/aidata.h	11 Nov 2004 06:09:46 -0000
@@ -97,6 +97,9 @@
     bool sea_done;    /* nothing more to explore at sea */
   } explore;
 
+  /* Keep track of available ocean channels */
+  bool *channels;
+
   /* This struct is used for statistical unit building, eg to ensure
    * that we don't build too few or too many units of a given type. */
   struct {
@@ -149,4 +152,6 @@
 
 struct ai_data *ai_data_get(struct player *pplayer);
 
+bool ai_channel(struct player *pplayer, Continent_id c1, Continent_id c2);
+
 #endif
Index: server/gotohand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/gotohand.c,v
retrieving revision 1.187
diff -u -r1.187 gotohand.c
--- server/gotohand.c	31 Oct 2004 22:32:33 -0000	1.187
+++ server/gotohand.c	11 Nov 2004 06:09:46 -0000
@@ -33,6 +33,7 @@
 #include "unithand.h"
 #include "unittools.h"
 
+#include "aidata.h"
 #include "aitools.h"
 
 #include "gotohand.h"
@@ -1217,6 +1218,9 @@
 bool goto_is_sane(struct unit *punit, struct tile *ptile, bool omni)
 {  
   struct player *pplayer = unit_owner(punit);
+  struct city *pcity = map_get_city(ptile);
+  Continent_id my_cont = map_get_continent(punit->tile);
+  Continent_id target_cont = map_get_continent(ptile);
 
   if (same_pos(punit->tile, ptile)) {
     return TRUE;
@@ -1235,36 +1239,57 @@
        * and with a boat */
       if (ground_unit_transporter_capacity(ptile, pplayer) > 0) {
         adjc_iterate(ptile, tmp_tile) {
-          if (map_get_continent(tmp_tile) == map_get_continent(punit->tile))
+          if (map_get_continent(tmp_tile) == my_cont) {
             /* The target is adjacent to our continent! */
             return TRUE;
+          }
         } adjc_iterate_end;
       }
     } else {
       /* Going to a land tile: better be our continent */
-      if (map_get_continent(punit->tile) == map_get_continent(ptile)) {
+      if (my_cont == target_cont) {
         return TRUE;
       } else {
         /* Well, it's not our continent, but maybe we are on a boat
          * adjacent to the target continent? */
 	adjc_iterate(punit->tile, tmp_tile) {
-	  if (map_get_continent(tmp_tile) == map_get_continent(ptile)) {
+	  if (map_get_continent(tmp_tile) == target_cont) {
 	    return TRUE;
           }
 	} adjc_iterate_end;
       }
-    }
-      
+    }      
     return FALSE;
 
   case SEA_MOVING:
-    if (is_ocean(map_get_terrain(ptile))
-        || is_ocean_near_tile(ptile)) {
-      /* The target is sea or is accessible from sea 
-       * (allow for bombardment and visiting ports) */
-      return TRUE;
+    if (!is_ocean(map_get_terrain(punit->tile))) {
+      /* Oops, we are not in the open waters.  What ocean do we have 
+       * access to?  We can assume we are in a city and any oceans adjacent
+       * are connected. */
+      adjc_iterate(punit->tile, tmp_tile) {
+        if (is_ocean(map_get_terrain(tmp_tile))) {
+          my_cont = map_get_continent(tmp_tile);
+          break;
+        }
+      } adjc_iterate_end;
     }
-    return FALSE;
+
+    if (is_ocean(map_get_terrain(ptile))) {
+      if (ai_channel(pplayer, target_cont, my_cont)) {
+        return TRUE; /* Ocean -> Ocean travel ok. */
+      }
+    } else if ((pcity && pplayers_allied(city_owner(pcity), pplayer))
+               || !unit_flag(punit, F_NO_LAND_ATTACK)) {
+      /* Not ocean, but allied city or can bombard, checking if there is 
+       * good ocean adjacent */
+      adjc_iterate(ptile, tmp_tile) {
+        if (is_ocean(map_get_terrain(tmp_tile)) 
+            && ai_channel(pplayer, my_cont, map_get_continent(tmp_tile))) {
+          return TRUE;
+        }
+      } adjc_iterate_end;
+    }
+    return FALSE; /* Not ok. */
 
   default:
     return TRUE;
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.