Complete.Org: Mailing Lists: Archives: freeciv-dev: October 2003:
[Freeciv-Dev] (PR#6494) generalised barb leader
Home

[Freeciv-Dev] (PR#6494) generalised barb leader

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [Freeciv-Dev] (PR#6494) generalised barb leader
From: "Per I. Mathisen" <per@xxxxxxxxxxx>
Date: Sun, 12 Oct 2003 15:14:06 -0700
Reply-to: rt@xxxxxxxxxxxxxx

I took a plunge into the barb code today, and wrote this generalization of
the barbarian leader function, which now is used also for any gameloss
unit. Converts the function from warmap to pf, too.

It is not very good, however. (Not that it ever was.)

Ideas on how to make it better (without spending too much CPU) are
welcome.

  - Per

Index: ai/advmilitary.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/advmilitary.c,v
retrieving revision 1.154
diff -u -r1.154 advmilitary.c
--- ai/advmilitary.c    23 Sep 2003 18:43:23 -0000      1.154
+++ ai/advmilitary.c    12 Oct 2003 21:42:30 -0000
@@ -140,7 +140,7 @@
     return 0;
   }
 
-  defense = get_defense_power(punit) * punit->hp;
+  defense = get_defense_power(punit, punit->x, punit->y) * punit->hp;
   if (!is_sailing_unit(punit)) {
     defense *= unit_type(punit)->firepower;
     if (is_ground_unit(punit)) {
Index: ai/aiunit.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.c,v
retrieving revision 1.298
diff -u -r1.298 aiunit.c
--- ai/aiunit.c 9 Oct 2003 00:07:33 -0000       1.298
+++ ai/aiunit.c 12 Oct 2003 21:42:32 -0000
@@ -64,8 +64,7 @@
 static void ai_manage_unit(struct player *pplayer, struct unit *punit);
 static void ai_manage_military(struct player *pplayer,struct unit *punit);
 static void ai_manage_caravan(struct player *pplayer, struct unit *punit);
-static void ai_manage_barbarian_leader(struct player *pplayer,
-                                      struct unit *leader);
+static void ai_manage_leader(struct player *pplayer, struct unit *punit);
 
 #define RAMPAGE_ANYTHING                 1
 #define RAMPAGE_HUT_OR_BETTER        99998
@@ -221,7 +220,9 @@
 static bool has_defense(struct city *pcity)
 {
   unit_list_iterate(map_get_tile(pcity->x, pcity->y)->units, punit) {
-    if (is_military_unit(punit) && get_defense_power(punit) != 0 && punit->hp 
!= 0) {
+    if (is_military_unit(punit) 
+        && get_defense_power(punit, punit->x, punit->y) != 0
+        && punit->hp != 0) {
       return TRUE;
     }
   }
@@ -2564,16 +2565,19 @@
     return FALSE;
   }
 
-  if (is_allied_city_tile
-      (map_get_tile(punit->x, punit->y), unit_owner(punit))) return FALSE;
+  if (is_allied_city_tile(map_get_tile(punit->x, punit->y), 
+                          unit_owner(punit))) {
+    punit->fuel = BARBARIAN_LIFE; /* renew life */
+    return FALSE;
+  }
 
   /* check if there is enemy nearby */
   square_iterate(punit->x, punit->y, 3, x, y) {
     if (is_enemy_city_tile(map_get_tile(x, y), unit_owner(punit)) ||
-       is_enemy_unit_tile(map_get_tile(x, y), unit_owner(punit)))
+       is_enemy_unit_tile(map_get_tile(x, y), unit_owner(punit))) {
       return FALSE;
-  }
-  square_iterate_end;
+    }
+  } square_iterate_end;
 
   return TRUE;
 }
@@ -2592,17 +2596,16 @@
 
   CHECK_UNIT(punit);
 
-  /* retire useless barbarian units here, before calling the management
-     function */
-  if( is_barbarian(pplayer) ) {
-    /* Todo: should be configurable */
-    if( unit_can_be_retired(punit) && myrand(100) > 90 ) {
+  /* Retire useless barbarian units here, before calling the management
+   * function */
+  if (is_barbarian(pplayer)) {
+    if (unit_can_be_retired(punit) && myrand(100) > 90) {
       wipe_unit(punit);
       return;
     }
-    if( !is_military_unit(punit)
+    if (!is_military_unit(punit)
        && !unit_has_role(punit->type, L_BARBARIAN_LEADER)) {
-      freelog(LOG_VERBOSE, "Barbarians picked up non-military unit.");
+      UNIT_LOG(LOG_ERROR, punit, "Barbarians control non-military unit");
       return;
     }
   }
@@ -2612,7 +2615,7 @@
   if (!bodyguard && punit->ai.bodyguard > BODYGUARD_NONE) {
     UNIT_LOG(LOGLEVEL_BODYGUARD, punit, "lost bodyguard, asking for new");
     punit->ai.bodyguard = BODYGUARD_WANTED;
-  }  
+  }
 
   if (punit->moves_left <= 0) {
     /* Can do nothing */
@@ -2623,35 +2626,28 @@
 
   if ((unit_flag(punit, F_DIPLOMAT))
       || (unit_flag(punit, F_SPY))) {
-    ai_manage_diplomat(pplayer, punit);
-    return;
+    ai_manage_diplomat(pplayer, punit);  
+  } else if (unit_flag(punit, F_GAMELOSS)) {
+    ai_manage_leader(pplayer, punit);
   } else if (unit_flag(punit, F_SETTLERS)
             ||unit_flag(punit, F_CITIES)) {
     ai_manage_settler(pplayer, punit);
-    return;
   } else if (unit_flag(punit, F_TRADE_ROUTE)
              || unit_flag(punit, F_HELP_WONDER)) {
     ai_manage_caravan(pplayer, punit);
-    return;
   } else if (unit_has_role(punit->type, L_BARBARIAN_LEADER)) {
-    ai_manage_barbarian_leader(pplayer, punit);
-    return;
+    ai_manage_leader(pplayer, punit);
   } else if (get_transporter_capacity(punit) > 0) {
     ai_manage_ferryboat(pplayer, punit);
-    return;
   } else if (is_air_unit(punit)) {
     ai_manage_airunit(pplayer, punit);
-    return;
   } else if (is_heli_unit(punit)) {
     /* TODO: We can try using air-unit code for helicopters, just
      * pretend they have fuel = HP / 3 or something. */
-    return;
   } else if (is_military_unit(punit)) {
     ai_manage_military(pplayer,punit); 
-    return;
   } else {
     (void) ai_manage_explorer(punit); /* what else could this be? -- Syela */
-    return;
   }
 }
 
@@ -2726,132 +2722,135 @@
 }
 
 /*************************************************************************
-Barbarian leader tries to stack with other barbarian units, and if it's
-not possible it runs away. When on coast, it may disappear with 33% chance.
+  Evaluate safety of given tile.
+
+  FIXME: Use something more bright than current enemies_at(), which
+  only looks at adjacent tiles. We should look at bit further to check
+  for longer range attackers. Perhaps use something like settlers'
+  enemies map.
 **************************************************************************/
-static void ai_manage_barbarian_leader(struct player *pplayer, struct unit 
*leader)
+static int evaluate_safety_of_tile(struct player *pplayer, struct unit *punit, 
+                                   int x, int y, bool on_ferry)
 {
-  int con = map_get_continent(leader->x, leader->y);
-  int safest = 0, safest_x = leader->x, safest_y = leader->y;
-  struct unit *closest_unit = NULL;
-  int dist, mindist = 10000;
-
-  CHECK_UNIT(leader);
-
-  if (leader->moves_left == 0 || 
-      (!is_ocean(map_get_terrain(leader->x, leader->y)) &&
-       unit_list_size(&(map_get_tile(leader->x, leader->y)->units)) > 1) ) {
-      handle_unit_activity_request(leader, ACTIVITY_SENTRY);
-      return;
-  }
-  /* the following takes much CPU time and could be avoided */
-  generate_warmap(map_get_city(leader->x, leader->y), leader);
+  int want = 0;
+  struct tile *ptile = map_get_tile(x, y);
+  bool vulnerable = is_stack_vulnerable(x, y);
+  int selfdef;
+  bool enemies = enemies_at(punit, x, y);
 
-  /* duck under own units */
-  unit_list_iterate(pplayer->units, aunit) {
-    if (unit_has_role(aunit->type, L_BARBARIAN_LEADER)
-       || !is_ground_unit(aunit)
-       || map_get_continent(aunit->x, aunit->y) != con)
-      continue;
+  unit_list_iterate(ptile->units, aunit) {
+    int def;
 
-    if (WARMAP_COST(aunit->x, aunit->y) < mindist) {
-      mindist = WARMAP_COST(aunit->x, aunit->y);
-      closest_unit = aunit;
+    if (pplayers_allied(pplayer, unit_owner(aunit))) {
+      return -999;
     }
-  } unit_list_iterate_end;
 
-  if (closest_unit
-      && !same_pos(closest_unit->x, closest_unit->y, leader->x, leader->y)
-      && (map_get_continent(leader->x, leader->y)
-          == map_get_continent(closest_unit->x, closest_unit->y))) {
-    (void) ai_unit_goto(leader, closest_unit->x, closest_unit->y);
-    return; /* sticks better to own units with this -- jk */
-  }
-
-  UNIT_LOG(LOG_DEBUG, leader, "Barbarian leader needs to flee");
-  mindist = 1000000;
-  closest_unit = NULL;
-
-  players_iterate(other_player) {
-    unit_list_iterate(other_player->units, aunit) {
-      if (is_military_unit(aunit)
-         && is_ground_unit(aunit)
-         && map_get_continent(aunit->x, aunit->y) == con) {
-       /* questionable assumption: aunit needs as many moves to reach us as we
-          need to reach it */
-       dist = WARMAP_COST(aunit->x, aunit->y) - unit_move_rate(aunit);
-       if (dist < mindist) {
-         freelog(LOG_DEBUG, "Barbarian leader: closest enemy is %s at %d, %d, 
dist %d",
-                  unit_name(aunit->type), aunit->x, aunit->y, dist);
-         mindist = dist;
-         closest_unit = aunit;
-       }
-      }
-    } unit_list_iterate_end;
-  } players_iterate_end;
+    def = get_defense_power(aunit, aunit->x, aunit->y)
+          * unit_type(aunit)->hp
+          * unit_type(aunit)->firepower;
 
-  /* Disappearance - 33% chance on coast, when older than barbarian life span 
*/
-  if (is_at_coast(leader->x, leader->y) && leader->fuel == 0) {
-    if(myrand(3) == 0) {
-      UNIT_LOG(LOG_DEBUG, leader, "barbarian leader disappearing...");
-      wipe_unit(leader);
-      return;
+    if (aunit == punit) {
+      continue;
+    }
+    if (vulnerable) {
+      /* Only best defender counts */
+      want = MAX(def, want);
+    } else {
+      /* All can defend before we do/die */
+      want += def;
+    }
+  } unit_list_iterate_end;
+  if (!on_ferry) {
+    /* Maybe we can defend ourselves best at this place. Ignore if
+     * on a ferry, since this can cause spurious disembarks. */
+    selfdef = get_defense_power(punit, x, y)
+              * unit_type(punit)->hp * unit_type(punit)->firepower;
+    if (vulnerable) {
+      want = MAX(want, selfdef);
+    } else {
+      want += selfdef;
     }
   }
-
-  if (!closest_unit) {
-    handle_unit_activity_request(leader, ACTIVITY_IDLE);
-    UNIT_LOG(LOG_DEBUG, leader, "Barbarian leader: no enemy.");
-    return;
+  if (enemies) {
+    want += want / 2;
+  }
+  if (ptile->city) {
+    want *= 2; /* we can hide in cities */
   }
+  if (is_at_coast(x, y)) {
+    want /= 2; /* but coast is dangerous */
+  }
+  return want;
+}
 
-  generate_warmap(map_get_city(closest_unit->x, closest_unit->y), 
closest_unit);
+/*************************************************************************
+  A leader tries to stack with other units, and if that is not possible 
+  try to find a safe spot somewhere.
 
-  do {
-    int last_x, last_y;
-    UNIT_LOG(LOG_DEBUG, leader, "Barbarian leader: moves left: %d.",
-             leader->moves_left);
-
-    square_iterate(leader->x, leader->y, 1, x, y) {
-      if (WARMAP_COST(x, y) > safest
-         && could_unit_move_to_tile(leader, x, y) == 1) {
-       safest = WARMAP_COST(x, y);
-       freelog(LOG_DEBUG,
-               "Barbarian leader: safest is %d, %d, safeness %d", x, y,
-               safest);
-       safest_x = x;
-       safest_y = y;
-      }
-    } 
-    square_iterate_end;
-
-    UNIT_LOG(LOG_DEBUG, leader, "Barbarian leader: fleeing to (%d,%d).", 
-             safest_x, safest_y);
-    if (same_pos(leader->x, leader->y, safest_x, safest_y)) {
-      UNIT_LOG(LOG_DEBUG, leader, 
-               "Barbarian leader: reached the safest position.");
-      handle_unit_activity_request(leader, ACTIVITY_IDLE);
-      return;
-    }
+  FIXME: Ensure that we are called last, so that we can stack properly 
+  with others.
+**************************************************************************/
+static void ai_manage_leader(struct player *pplayer, struct unit *punit)
+{
+  struct pf_map *map;
+  struct pf_parameter parameter;
+  int best_want;
+  struct tile *best = map_get_tile(punit->x, punit->y);
+  bool on_ferry = is_ocean(best->terrain);
+  int best_x = punit->x, best_y = punit->y;
+  /* Sanity checks */
+  CHECK_UNIT(punit);
+
+  pft_fill_default_parameter(&parameter);
+  pft_fill_unit_parameter(&parameter, punit);
+  map = pf_create_map(&parameter);
 
-    last_x = leader->x;
-    last_y = leader->y;
-    (void) ai_unit_goto(leader, safest_x, safest_y);
-    if (same_pos(leader->x, leader->y, last_x, last_y)) {
-      /* Deep inside the goto handling code, in 
-        server/unithand.c::handle_unite_move_request(), the server
-        may decide that a unit is better off not moving this turn,
-        because the unit doesn't have quite enough movement points
-        remaining.  Unfortunately for us, this favor that the server
-        code does may lead to an endless loop here in the barbarian
-        leader code:  the BL will try to flee to a new location, execute 
-        the goto, find that it's still at its present (unsafe) location,
-        and repeat.  To break this loop, we test for the condition
-        where the goto doesn't do anything, and break if this is
-        the case. */
+  /* Current position */
+  best_want = evaluate_safety_of_tile(pplayer, punit, punit->x, punit->y, 
+                                      on_ferry);
+
+  /* Find most safe spot to camp */
+  while (pf_next(map)) {
+    struct pf_position pos;
+    int want;
+    struct tile *ptile;
+
+    pf_next_get_position(map, &pos);
+    /* Only two turns horizon */
+    if (pos.total_MC >= punit->moves_left * 2) {
       break;
     }
-  } while (leader->moves_left > 0);
+    ptile = map_get_tile(pos.x, pos.y);
+    if ((ptile->city && ptile->city->owner != punit->owner)
+        || is_ocean(ptile->terrain)) {
+      continue;
+    }
+
+    want = evaluate_safety_of_tile(pplayer, punit, pos.x, pos.y, on_ferry);
+
+    if (want > best_want) {
+      best = ptile;
+      best_x = pos.x;
+      best_y = pos.y;
+    }
+  }
+  /* Go there */
+  if (!same_pos(punit->x, punit->y, best_x, best_y)) {
+    struct pf_path *path = pf_get_path(map, best_x, best_y);
+
+    if (!ai_unit_execute_path(punit, path)) {
+      /* We died?? */
+      freelog(LOG_ERROR, "%s's leader died en route to (%d, %d)",
+              pplayer->name, best_x, best_y);
+      pf_destroy_path(path);
+      return;
+    }
+    pf_destroy_path(path);
+  }
+
+  if (punit->activity != ACTIVITY_FORTIFIED) {
+    handle_unit_activity_request(punit, ACTIVITY_FORTIFYING);
+  }
 }
 
 /*************************************************************************
Index: common/combat.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/combat.c,v
retrieving revision 1.33
diff -u -r1.33 combat.c
--- common/combat.c     17 Sep 2003 02:57:21 -0000      1.33
+++ common/combat.c     12 Oct 2003 21:42:42 -0000
@@ -412,14 +412,15 @@
 }
 
 /**************************************************************************
-  Returns the defense power, modified by terrain and veteran status.
+  Returns the defense power of unit at position, modified by terrain 
+  and veteran status.
 **************************************************************************/
-int get_defense_power(struct unit *punit)
+int get_defense_power(struct unit *punit, int x, int y)
 {
   int db, power = base_get_defense_power(punit);
 
-  db = get_tile_type(map_get_terrain(punit->x, punit->y))->defense_bonus;
-  if (map_has_special(punit->x, punit->y, S_RIVER)) {
+  db = get_tile_type(map_get_terrain(x, y))->defense_bonus;
+  if (map_has_special(x, y, S_RIVER)) {
     db += (db * terrain_control.river_defense_bonus) / 100;
   }
   power = (power * db) / 10;
@@ -542,7 +543,9 @@
 {
   return defence_multiplication(attacker->type, defender->type,
                                defender->x, defender->y,
-                               get_defense_power(defender),
+                               get_defense_power(defender, 
+                                                  defender->x, 
+                                                  defender->y),
                                defender->activity == ACTIVITY_FORTIFIED);
 }
 
Index: common/combat.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/combat.h,v
retrieving revision 1.10
diff -u -r1.10 combat.h
--- common/combat.h     17 Sep 2003 02:57:21 -0000      1.10
+++ common/combat.h     12 Oct 2003 21:42:42 -0000
@@ -49,7 +49,7 @@
 int get_attack_power(struct unit *punit);
 int base_get_attack_power(Unit_Type_id type, bool veteran, int moves_left);
 int base_get_defense_power(struct unit *punit);
-int get_defense_power(struct unit *punit);
+int get_defense_power(struct unit *punit, int x, int y);
 int get_total_defense_power(struct unit *attacker, struct unit *defender);
 int get_virtual_defense_power(Unit_Type_id att_type, Unit_Type_id def_type,
                              int x, int y, bool fortified, bool veteran);

[Prev in Thread] Current Thread [Next in Thread]
  • [Freeciv-Dev] (PR#6494) generalised barb leader, Per I. Mathisen <=