Complete.Org: Mailing Lists: Archives: freeciv-ai: August 2003:
[freeciv-ai] Re: (PR#4026) Advanced rampage.
Home

[freeciv-ai] Re: (PR#4026) Advanced rampage.

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: per@xxxxxxxxxxx
Subject: [freeciv-ai] Re: (PR#4026) Advanced rampage.
From: "Gregory Berkolaiko" <Gregory.Berkolaiko@xxxxxxxxxxxx>
Date: Tue, 12 Aug 2003 08:43:59 -0700
Reply-to: rt@xxxxxxxxxxxxxx

An updated and revised version of the patch is attached.

On Sat, 10 May 2003, Per I. Mathisen wrote:

> I don't like this. First, those who have commented that we should also
> check for fortress are right. Just because fortress is not commonly used
> in default rules, does not give you a license to ignore it. (If it is THAT
> useless, it should rather be removed from the game.)

We now check for fortress.

> Second, the benefit of taking a city is ridiculously small. The loss to
> the other player might be enormous. Our benefit of taking the city with

There are now no more bonuses for attacking (defended) cities.  This
should be done in an organized manner by some other code.  We still value
snatching empty cities above anything else.

> Modify thres_adj by occupychance. For example:
>   assert(thres_adj <= thres_move);
>   thres_adj += ((tresh_move - tresh_adj) * game.occupychance / 100);
> 
> This teaches the AI about the dangers inherent in occupychance. Not that
> anyone uses it, but as above, if it is in the rules, we should support it.

Done.

> +/**********************************************************************
> +  Sea attack is the same as overlap (consider bombardment) but we don't
> +  want to pass through enemy tiles.
> +**********************************************************************/
> +static int sea_attack_move(int x, int y, enum direction8 dir,
> +                         int x1, int y1, struct pf_parameter *param)
> +{
> +  struct tile *src_tile = map_get_tile(x, y);
> +
> +  if (src_tile->terrain == T_OCEAN) {
> 
> Use is_ocean(src_tile->terrain), applies everywhere.

I think there should be a different patch doing all this, since there are 
many such occurences in pf_tools.c

> +    if (is_non_allied_unit_tile(src_tile, param->owner)) {
> +      return PF_IMPOSSIBLE_MC;
> +    }
> 
> You need to check F_NO_LAND_ATTACK here. Which means that you have to pass
> another argument, I guess.

The possibility of attack is correctly tested in ai_rampage_want by
    if (!can_player_attack_tile(pplayer, x, y)
        || !can_unit_attack_unit_at_tile(punit, pdef, x, y)) {
      return 0;
    }
We should not ask PF to do all possible checks for us.

> (Don't we have code in common/ that does this kind of thing already?)
> 
> +    /* Sea-to-Land.  TODO: Marines support */
> 
> Please don't leave such important TODOs unimplemented.

Implemented.

> +    /* Land-to-Land */
> +    if (is_non_allied_unit_tile(src_tile, param->owner)) {
> +      /* Cannot pass through defended tiles */
> +      move_cost = PF_IMPOSSIBLE_MC;
> +    } else if(is_non_allied_unit_tile(tgt_tile, param->owner)) {
> +
> +      /* TODO: Intelligent checking */
> +      /* Attack! */
> 
> What would 'intelligent checking' mean here?

Comment removed.  Intelligent checking is done in ai_rampage_want.

You can test the code with the attached game.  Just set Pille to AI and 
watch the Armour on the rampage.  It looks good :)

I feel that the patch is finished and should be committed soon to avoid
bitrot.  Before committing please remove the testing printout on line 1192
of patched ai/aiunit.c


G.

Attachment: armour-on-ship.gz
Description: armour-on-ship.gz

Index: ai/aiunit.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.c,v
retrieving revision 1.282
diff -u -r1.282 aiunit.c
--- ai/aiunit.c 2003/08/08 22:11:41     1.282
+++ ai/aiunit.c 2003/08/12 15:15:08
@@ -44,6 +44,9 @@
 #include "unithand.h"
 #include "unittools.h"
 
+#include "path_finding.h"
+#include "pf_tools.h"
+
 #include "advmilitary.h"
 #include "aiair.h"
 #include "aicity.h"
@@ -62,10 +65,14 @@
 static void ai_manage_barbarian_leader(struct player *pplayer,
                                       struct unit *leader);
 
+#define RAMPAGE_BETTER_THAN_PILLAGE  2
+#define RAMPAGE_HUT_OR_BETTER        99998
+#define RAMPAGE_ONLY_FREE_CITY       99999
+static bool ai_military_rampage(struct unit *punit, int thresh_adj, 
+                                int thresh_move);
 static void ai_military_findjob(struct player *pplayer,struct unit *punit);
 static void ai_military_gohome(struct player *pplayer,struct unit *punit);
 static void ai_military_attack(struct player *pplayer,struct unit *punit);
-static bool ai_military_rampage(struct unit *punit, int threshold);
 
 static int unit_move_turns(struct unit *punit, int x, int y);
 static bool unit_role_defender(Unit_Type_id type);
@@ -1020,182 +1027,225 @@
 }
 
 /*************************************************************************
-This function looks at tiles adjacent to the unit in order to find something
-to kill or explore.  It prefers tiles in the following order:
-
-Returns value of the victim which has been chosen:
-
-99999    means empty (undefended) city
-99998    means hut
-2..99997 means value of enemy unit weaker than our unit
-1        means barbarians wanting to pillage
-0        means nothing found or error
--2*MORT*TRADE_WEIGHTING
-         means nothing found and punit causing unhappiness
+  This function appraises the location (x, y) for a quick hit-n-run operation.
 
-If value <= 0 is returned, (dest_x, dest_y) is set to actual punit's position.
+  Returns value as follows:
+    -RAMPAGE_ONLY_FREE_CITY    
+             means undefended enemy city
+    -RAMPAGE_HUT_OR_BETTER    
+             means hut
+    RAMPAGE_BETTER_THAN_PILLAGE ... RAMPAGE_HUT_OR_BETTER - 1  
+             is value of enemy unit weaker than our unit
+    -(RAMPAGE_BETTER_THAN_PILLAGE - 1)        
+             means barbarians wanting to pillage
+    0        means nothing found or error
+  Here the minus indicates that you need to enter the target tile (as 
+  opposed to attacking it, which leaves you where you are).
 **************************************************************************/
-static int ai_military_findvictim(struct unit *punit, int *dest_x, int *dest_y)
+static int ai_rampage_want(struct unit *punit, int x, int y)
 {
   struct player *pplayer = unit_owner(punit);
-  int attack_power = unit_att_rating_now(punit);
-  int x = punit->x, y = punit->y;
-  int best = 0;
-  int stack_size = unit_list_size(&(map_get_tile(punit->x, punit->y)->units));
+  struct unit *pdef = get_defender(punit, x, y);
 
   CHECK_UNIT(punit);
+  
+  if (pdef) {
+    
+    if (!can_player_attack_tile(pplayer, x, y)
+        || !can_unit_attack_unit_at_tile(punit, pdef, x, y)) {
+      return 0;
+    }
+    
+    {
+      /* See description of kill_desire() about these variables. */
+      int att_rating = unit_att_rating_now(punit);
+      int vuln = unit_def_rating_sq(punit, pdef);
+      int attack = reinforcements_value(punit, pdef->x, pdef->y) + att_rating;
+      int benefit = stack_cost(pdef);
+      int loss = unit_type(punit)->build_cost;
+      
+      attack *= attack;
+      
+      /* If the victim is in the city/fortress, we correct the benefit
+       * with our health because there could be reprisal attacks.  We
+       * shouldn't send already injured units to useless suicide.
+       * Note that we do not specially encourage attacks against
+       * cities: rampage is a hit-n-run operation. */
+      if (!is_stack_vulnerable(x, y) 
+          && unit_list_size(&(map_get_tile(x, y)->units)) > 1) {
+        benefit = (benefit * punit->hp) / unit_type(punit)->hp;
+      }
+      
+      /* If we have non-zero attack rating... */
+      if (att_rating > 0 && is_my_turn(punit, pdef)) {
+        /* No need to amortize, our operation takes one turn. */
+        return kill_desire(benefit, attack, loss, vuln, 1);
+      }
+    }
+    
+  } else {
+    struct city *pcity = map_get_city(x, y);
+    
+    /* No defender... */
+    
+    /* ...and free foreign city waiting for us. Who would resist! */
+    if (pcity && pplayers_at_war(pplayer, city_owner(pcity))
+        && COULD_OCCUPY(punit)) {
+      
+      return -RAMPAGE_ONLY_FREE_CITY;
+    }
+    
+    /* ...or tiny pleasant hut here! */
+    if (map_has_special(x, y, S_HUT) && !is_barbarian(pplayer)) {
+      
+      return -RAMPAGE_HUT_OR_BETTER;
+    }
+    
+    /* If we have nothing to do, we can at least pillage something, hmm? */
+    if (is_land_barbarian(pplayer)
+        && get_tile_infrastructure_set(map_get_tile(x, y)) != S_NO_SPECIAL) {
+      return -(RAMPAGE_BETTER_THAN_PILLAGE - 1);
+    }
+  }
+  
+  return 0;
+}
 
-  *dest_y = y;
-  *dest_x = x;
+/*************************************************************************
+  Look for worthy targets within a one-turn horizon.
+*************************************************************************/
+static struct pf_path *find_rampage_target(struct unit *punit, 
+                                           int thresh_adj, int thresh_move)
+{
+  struct pf_map *tgt_map;
+  struct pf_path *path = NULL;
+  struct pf_parameter parameter;
+  /* Coordinates of the best target (initialize to silence compiler) */
+  int x = punit->x, y = punit->y;
+  /* Want of the best target */
+  int max_want = 0;
+  struct player *pplayer = unit_owner(punit);
  
-  /* Ferryboats with passengers do not attack. */
-  if (punit->ai.passenger > 0) {
-    return 0;
-  }
+  pft_fill_default_parameter(&parameter);  
+  pft_fill_unit_attack_param(&parameter, punit);
+  
+  tgt_map = pf_create_map(&parameter);
+  while (pf_next(tgt_map)) {
+    struct pf_position pos;
+    int want;
+    bool move_needed;
+    int thresh;
  
-  if (punit->unhappiness > 0) {
-    /* When we're causing unhappiness, we'll set best even lower, 
-     * so that we will take even targets which we would ignore otherwise 
-     * (in other words -- we're going to commit suicide). */
-    best = - 2 * MORT * TRADE_WEIGHTING;
+    pf_next_get_position(tgt_map, &pos);
+    
+    if (pos.total_MC > punit->moves_left) {
+      /* This is too far */
+      break;
+    }
+
+    if (ai_handicap(pplayer, H_TARGETS) 
+        && !map_is_known_and_seen(pos.x, pos.y, pplayer)) {
+      /* The target is under fog of war */
+      continue;
+    }
+    
+    want = ai_rampage_want(punit, pos.x, pos.y);
+
+    /* Negative want means move needed even though the tiles are adjacent */
+    move_needed = (!is_tiles_adjacent(punit->x, punit->y, pos.x, pos.y)
+                   || want < 0);
+    /* Select the relevant threshold */
+    thresh = (move_needed ? thresh_move : thresh_adj);
+    want = (want < 0 ? -want : want);
+
+    if (want > max_want && want > thresh) {
+      /* The new want exceeds both the previous maximum 
+       * and the relevant threshold, so it's worth recording */
+      max_want = want;
+      x = pos.x;
+      y = pos.y;
+    }
   }
 
-  adjc_iterate(x, y, x1, y1) {
-    /* Macro to set the tile with our target as the best (with value new_best) 
*/
-#define SET_BEST(new_best) \
-    do { best = (new_best); *dest_x = x1; *dest_y = y1; } while (FALSE)
+  if (max_want > 0) {
+    /* We found something */
+    path = pf_get_path(tgt_map, x, y);
+    assert(path);
+  }
 
-    struct unit *pdef = get_defender(punit, x1, y1);
-    
-    if (pdef) {
-      struct unit *patt = get_attacker(punit, x1, y1);
+  pf_destroy_map(tgt_map);
+  
+  return path;
+}
 
-      if (!can_unit_attack_tile(punit, x1, y1)) {
-        continue;
-      }
+/*************************************************************************
+  This is a function to execute paths returned by the path-finding engine.
+  It is analogous to goto_route_execute, only much simpler.
 
-      /* If we are in the city, let's deeply consider defending it - however,
-       * horsemen in city refused to attack phalanx just outside that was
-       * bodyguarding catapult; thus, we get the best attacker on the tile (x1,
-       * y1) as well, for the case when there are multiple different units on
-       * the tile (x1, y1). Thus we force punit to attack a stack of units if
-       * they're endangering punit seriously, even if they aren't that weak. */
-      /* FIXME: The get_total_defense_power(pdef, punit) should probably use
-       * patt rather than pdef. There also ought to be a better metric for
-       * determining this. */
-      if (patt
-          && map_get_city(x, y) 
-          && get_total_defense_power(pdef, punit) *
-             get_total_defense_power(punit, pdef) >=
-             get_total_attack_power(patt, punit) *
-             get_total_attack_power(punit, pdef)
-          && stack_size < 2
-          && get_total_attack_power(patt, punit) > 0) {
-        freelog(LOG_DEBUG, "%s's %s defending %s from %s's %s at (%d, %d)",
-                pplayer->name, unit_type(punit)->name, map_get_city(x, 
y)->name,
-                unit_owner(pdef)->name, unit_type(pdef)->name, punit->x, 
punit->y);
-      } else {
-        /* See description of kill_desire() about this variables. */
-        int vuln = unit_def_rating_sq(punit, pdef);
-        int attack = reinforcements_value(punit, pdef->x, pdef->y)
-                     + attack_power;
-        int benefit = unit_type(pdef)->build_cost;
-        int loss = unit_type(punit)->build_cost;
-        
-        attack *= attack;
-        
-        /* If the victim is in the city, we increase the benefit and correct
-         * it with our health because there may be more units in the city
-         * stacked, and we won't destroy them all at once, so in the next
-         * turn they may attack us. So we shouldn't send already injured
-         * units to useless suicide. */
-        if (map_get_city(x1, y1)) {
-          benefit += unit_value(get_role_unit(F_CITIES, 0));
-          benefit = (benefit * punit->hp) / unit_type(punit)->hp;
-        }
-        
-        /* If we have non-zero attack rating... */
-        if (attack > 0 && is_my_turn(punit, pdef)) {
-          int desire;
-
-          /* FIXME? Why we don't use stack_size as victim_count? --pasky */
-
-          desire = kill_desire(benefit, attack, loss, vuln, 1);
-          
-          /* No need to amortize! We're doing it in one-turn horizon. */
-          
-          if (desire > best && ai_fuzzy(pplayer, TRUE)) {
-            freelog(LOG_DEBUG, "Better than %d is %d (%s)",
-                          best, desire, unit_type(pdef)->name);
-            SET_BEST(desire);
-          } else {
-            freelog(LOG_DEBUG, "NOT better than %d is %d (%s)",
-                    best, desire, unit_type(pdef)->name);
-          }
-        }
-      }
-      
-    } else {
-      struct city *pcity = map_get_city(x1, y1);
-      
-      /* No defender... */
-     
-      /* ...and free foreign city waiting for us. Who would resist! */
-      if (pcity && pplayers_at_war(pplayer, city_owner(pcity))
-          && could_unit_move_to_tile(punit, x1, y1) != 0
-          && COULD_OCCUPY(punit)
-          && !is_ocean(map_get_terrain(punit->x, punit->y))) {
-        SET_BEST(99999);
-        continue;
-      }
+  Weuse ai_unit_attack which means "move if target unoccupied, attack 
+  otherwise" and also brings our bodyguard along.
+*************************************************************************/
+static bool ai_unit_execute_path(struct unit *punit, struct pf_path *path)
+{
+  int i;
 
-      /* ...or tiny pleasant hut here! */
-      if (map_has_special(x1, y1, S_HUT) && best < 99999
-          && could_unit_move_to_tile(punit, x1, y1) != 0
-          && !is_barbarian(unit_owner(punit))
-          && punit->ai.ai_role != AIUNIT_ESCORT
-          && punit->ai.charge == BODYGUARD_NONE /* Above line doesn't seem to 
work. :( */
-          && punit->ai.ai_role != AIUNIT_DEFEND_HOME) {
-        SET_BEST(99998);
-        continue;
-      }
-      
-      /* If we have nothing to do, we can at least pillage something, hmm? */
-      if (is_land_barbarian(pplayer) && best == 0
-          && get_tile_infrastructure_set(map_get_tile(x1, y1)) != S_NO_SPECIAL
-          && could_unit_move_to_tile(punit, x1, y1) != 0) {
-        SET_BEST(1);
-        continue;
-      }
+  /* We start with i = 1 for i = 0 is our present position */
+  for (i = 1; i < path->length; i++) {
+    int x = path->positions[i].x, y = path->positions[i].y;
+
+    if (!ai_unit_attack(punit, x, y)) {
+      /* Died... */
+      return FALSE;
     }
-#undef SET_BEST
-  } adjc_iterate_end;
+    
+    if (!same_pos(punit->x, punit->y, x, y)) {
+      /* Stopped (or maybe fought) */
+      return TRUE;
+    }
+  }
 
-  return best;
+  return TRUE;
 }
 
 /*************************************************************************
-  Find and kill anything adjacent to us that we don't like with a 
-  given threshold until we have run out of juicy targets or movement. 
-  Wraps ai_military_findvictim().
+  Find and kill anything reachable within this turn and worth more than 
+  the relevant of the given thresholds until we have run out of juicy 
+  targets or movement.  The first threshold is for attacking which will 
+  leave us where we stand (attacking adjacent units), the second is for 
+  attacking distant (but within reach) targets.
+
+  For example, if unit is a bodyguard on duty, it should call
+    ai_military_rampage(punit, 100, RAMPAGE_ONLY_FREE_CITY)
+  meaning "we will move _only_ to pick up a free city but we are happy to
+  attack adjacent squares as long as they are worthy of it".
+
   Returns TRUE if survived the rampage session.
 **************************************************************************/
-static bool ai_military_rampage(struct unit *punit, int threshold)
+static bool ai_military_rampage(struct unit *punit, int thresh_adj, 
+                                int thresh_move)
 {
-  int x, y;
   int count = punit->moves_left + 1; /* break any infinite loops */
-
+  struct pf_path *path = NULL;
+  
   CHECK_UNIT(punit);
 
-  while (punit->moves_left > 0 && count-- > 0
-         && ai_military_findvictim(punit, &x, &y) >= threshold) {
-    if (!ai_unit_attack(punit, x, y)) {
+  assert(thresh_adj <= thresh_move);
+  /* This teaches the AI about the dangers inherent in occupychance. */
+  thresh_adj += ((thresh_move - thresh_adj) * game.occupychance / 100);
+
+  while (count-- > 0 && punit->moves_left > 0
+         && (path = find_rampage_target(punit, thresh_adj, thresh_move))) {
+    if (!ai_unit_execute_path(punit, path)) {
       /* Died */
-      return FALSE;
+      count = -1;
     }
+    pf_destroy_path(path);
+    path = NULL;
   }
-  assert(count >= 0);
-  return TRUE;
+
+  assert(!path);
+
+  return (count >= 0);
 }
 
 /*************************************************************************
@@ -1240,8 +1292,11 @@
       ai_unit_new_role(punit, AIUNIT_NONE, -1, -1);
     }
   } else {
-    /* I had these guys set to just fortify, which is so dumb. -- Syela */
-    (void) ai_military_rampage(punit, 40 * SHIELD_WEIGHTING);
+    /* I had these guys set to just fortify, which is so dumb. -- Syela
+     * Instead we can attack adjacent units and maybe even pick up some free 
+     * cities! */
+    (void) ai_military_rampage(punit, 40 * SHIELD_WEIGHTING, 
+                               RAMPAGE_ONLY_FREE_CITY);
   }
 }
 
@@ -1766,7 +1821,9 @@
       freelog(LOG_DEBUG, "INHOUSE. GOTO AI_NONE(%d)", punit->id);
       ai_unit_new_role(punit, AIUNIT_NONE, -1, -1);
       /* aggro defense goes here -- Syela */
-      (void) ai_military_rampage(punit, 2); /* 2 is better than pillage */
+      /* Attack just about anything */
+      (void) ai_military_rampage(punit, RAMPAGE_BETTER_THAN_PILLAGE, 
+                                 RAMPAGE_BETTER_THAN_PILLAGE);
     } else {
       UNIT_LOG(LOG_DEBUG, punit, "GOHOME");
       (void) ai_unit_goto(punit, pcity->x, pcity->y);
@@ -2335,7 +2392,7 @@
 
 /*************************************************************************
   This does the attack until we have used up all our movement, unless we
-  should safeguard a city. First we rampage on adjacent tiles, then we go
+  should safeguard a city.  First we rampage nearby, then we go
   looking for trouble elsewhere. If there is nothing to kill, sailing units 
   go home, others explore while barbs go berserk.
 **************************************************************************/
@@ -2350,8 +2407,9 @@
 
   /* Main attack loop */
   do {
-    /* First find easy adjacent enemies; 2 is better than pillage */
-    if (!ai_military_rampage(punit, 2)) {
+    /* First find easy nearby enemies, anything better than pillage goes */
+    if (!ai_military_rampage(punit, RAMPAGE_BETTER_THAN_PILLAGE, 
+                             RAMPAGE_BETTER_THAN_PILLAGE)) {
       return; /* we died */
     }
 
@@ -2694,8 +2752,9 @@
 
   if (pcity) {
     /* rest in city until the hitpoints are recovered, but attempt
-       to protect city from attack */
-    if (ai_military_rampage(punit, 2)) {
+       to protect city from attack (and be opportunistic too)*/
+    if (ai_military_rampage(punit, RAMPAGE_BETTER_THAN_PILLAGE, 
+                            RAMPAGE_ONLY_FREE_CITY)) {
       freelog(LOG_DEBUG, "%s's %s(%d) at (%d, %d) recovering hit points.",
               pplayer->name, unit_type(punit)->name, punit->id, punit->x,
               punit->y);
@@ -2704,8 +2763,9 @@
     }
   } else {
     /* goto to nearest city to recover hit points */
-    /* just before, check to see if we can occupy at enemy city undefended */
-    if (!ai_military_rampage(punit, 99999)) { 
+    /* just before, check to see if we can occupy an undefended enemy city */
+    if (!ai_military_rampage(punit, RAMPAGE_ONLY_FREE_CITY, 
+                             RAMPAGE_ONLY_FREE_CITY)) { 
       return; /* oops, we died */
     }
 
Index: common/aicore/pf_tools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/pf_tools.c,v
retrieving revision 1.5
diff -u -r1.5 pf_tools.c
--- common/aicore/pf_tools.c    2003/07/21 19:01:01     1.5
+++ common/aicore/pf_tools.c    2003/08/12 15:15:08
@@ -77,9 +77,30 @@
   return PF_IMPOSSIBLE_MC;
 }
 
+/**********************************************************************
+  Sea attack is the same as overlap (consider bombardment) but we don't
+  want to pass through enemy tiles.
+**********************************************************************/
+static int sea_attack_move(int x, int y, enum direction8 dir,
+                           int x1, int y1, struct pf_parameter *param)
+{
+  struct tile *src_tile = map_get_tile(x, y);
+
+  if (src_tile->terrain == T_OCEAN) {
+    if (is_non_allied_unit_tile(src_tile, param->owner)) {
+      return PF_IMPOSSIBLE_MC;
+    }
+    return SINGLE_MOVE;
+  } else if (is_allied_city_tile(map_get_tile(x, y), param->owner)
+            && map_get_terrain(x1, y1) == T_OCEAN) {
+    return SINGLE_MOVE;
+  }
+
+  return PF_IMPOSSIBLE_MC;
+}
+
 /************************************************************ 
   LAND_MOVE cost function for a unit 
-  Must put owner into *data.
 ************************************************************/
 static int normal_move_unit(int x, int y, enum direction8 dir,
                            int x1, int y1, struct pf_parameter *param)
@@ -111,6 +132,58 @@
   return move_cost;
 }
 
+/******************************************************************* 
+  LAND_MOVE cost function for a unit, but taking into account
+  possibilities of attacking.
+*******************************************************************/
+static int land_attack_move(int x, int y, enum direction8 dir,
+                           int x1, int y1, struct pf_parameter *param)
+{
+  struct tile *src_tile = map_get_tile(x, y);
+  struct tile *tgt_tile = map_get_tile(x1, y1);
+  int move_cost;
+
+  if (tgt_tile->terrain == T_OCEAN) {
+
+    /* Any-to-Sea */
+    if (ground_unit_transporter_capacity(x1, y1, param->owner) > 0) {
+      move_cost = SINGLE_MOVE;
+    } else {
+      move_cost = PF_IMPOSSIBLE_MC;
+    }
+  } else if (src_tile->terrain == T_OCEAN) {
+
+    /* Sea-to-Land. */
+    if (!is_non_allied_unit_tile(tgt_tile, param->owner)
+        && !is_non_allied_city_tile(tgt_tile, param->owner)) {
+      move_cost 
+        = get_tile_type(tgt_tile->terrain)->movement_cost * SINGLE_MOVE;
+    } else if (BV_ISSET(param->unit_flags, F_MARINES)) {
+      /* Can attack!! */
+      move_cost = SINGLE_MOVE;
+    } else {
+      move_cost = PF_IMPOSSIBLE_MC;
+    }
+  } else {
+
+    /* Land-to-Land */
+    if (is_non_allied_unit_tile(src_tile, param->owner)) {
+      /* Cannot pass through defended tiles */
+      move_cost = PF_IMPOSSIBLE_MC;
+    } else if (is_non_allied_unit_tile(tgt_tile, param->owner)) {
+
+      /* Attack! */
+      move_cost = SINGLE_MOVE;
+    } else {
+      /* Normal move */
+      move_cost = src_tile->move_cost[dir];
+    }
+  }
+
+  return move_cost;
+}
+
+
 /************************************************************ 
   A cost function for a land unit, which allows going into
   the ocean (with moves costing SINGLE_MOVE).  It is 
@@ -357,6 +430,7 @@
     break;
   case SEA_MOVING:
     parameter->get_MC = sea_overlap_move;
+    break;
   default:
     die("Unsupported move_type");
   }
@@ -369,6 +443,45 @@
   } else {
     parameter->is_pos_dangerous = NULL;
   }
+}
+
+/**********************************************************************
+  Consider attacking and non-attacking possibilities properly
+**********************************************************************/
+void pft_fill_unit_attack_param(struct pf_parameter *parameter,
+                                struct unit *punit)
+{
+  parameter->turn_mode = TM_CAPPED;
+  parameter->get_TB = NULL;
+  parameter->get_EC = NULL;
+
+  parameter->start_x = punit->x;
+  parameter->start_y = punit->y;
+  parameter->moves_left_initially = punit->moves_left;
+  parameter->move_rate = unit_move_rate(punit);
+  parameter->owner = unit_owner(punit);
+  parameter->omniscience = !ai_handicap(unit_owner(punit), H_MAP);
+
+  switch (unit_type(punit)->move_type) {
+  case LAND_MOVING:
+    parameter->get_MC = land_attack_move;
+    break;
+  case SEA_MOVING:
+    parameter->get_MC = sea_attack_move;
+    break;
+  default:
+    die("Unsupported move_type");
+  }
+
+  if (unit_type(punit)->move_type == LAND_MOVING 
+      && !unit_flag(punit, F_IGZOC)) {
+    parameter->get_zoc = is_my_zoc;
+  } else {
+    parameter->get_zoc = NULL;
+  }
+
+  /* It is too complicated to work with danger here */
+  parameter->is_pos_dangerous = NULL;
 }
 
 /**********************************************************************
Index: common/aicore/pf_tools.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/pf_tools.h,v
retrieving revision 1.2
diff -u -r1.2 pf_tools.h
--- common/aicore/pf_tools.h    2003/05/15 12:38:11     1.2
+++ common/aicore/pf_tools.h    2003/08/12 15:15:08
@@ -22,6 +22,8 @@
                             struct unit *punit);
 void pft_fill_unit_overlap_param(struct pf_parameter *parameter,
                                 struct unit *punit);
+void pft_fill_unit_attack_param(struct pf_parameter *parameter,
+                                struct unit *punit);
 
 /*
  * Below iterator is mostly for use by AI, iterates through all positions

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