Complete.Org: Mailing Lists: Archives: freeciv-ai: March 2004:
[freeciv-ai] Re: paratroopers
Home

[freeciv-ai] Re: paratroopers

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Cc: freeciv-ai@xxxxxxxxxxx
Subject: [freeciv-ai] Re: paratroopers
From: Jordi Negrevernis i Font <jorneg@xxxxxxxxxxx>
Date: Wed, 31 Mar 2004 23:33:54 +0200


This is the third version. That one does really not crash the server :-)

There is one major problem in this version -> When the code does not find a target for the paratrooper it does not know what to do. Can the new path finding be used with the paratooper module? Or the previous airgoto?

   Comments below.

En/na Per I. Mathisen ha escrit:

On Sun, 21 Mar 2004, Jordi Negrevernis i Font wrote:
+  /* second, we search for enemy units near us,
+     if we don't find any own empty city */
...
+  /* third, we search for undefended enemy cities near us */

Err, shouldn't we rather take enemy cities than enemy units? At least we
should weight the two options against each other.
I didn't do that way because first i want to erase the countryside of enemy units, then go taking enemy empty cities.

+static int find_something_to_paradrop(struct unit *punit, int x, int y)
...
+/* first, we search for undefended cities */
+square_iterate(punit->x, punit->y, range, x, y) {
+  acity = map_get_city(x,y);
+  if ((acity) && (city_owner(acity) == pplayer) &&
+      (unit_list_size(&(map_get_tile(x, y)->units)) == 0)) {
+    /* own city and empty! */
+    if (profit < (acity->trade_prod + acity->shield_prod + acity->food_prod) * 
acity->size) {
+      profit = (acity->trade_prod + acity->shield_prod + acity->food_prod) * 
acity->size;
+    }
+  }
+} square_iterate_end;

I am not sure whether this is worthwhile, and even if it is, we should
definitely check whether the city is in danger before building defenders
for it.
   Done.

Also, why have two different calculations as to profit - one to determine
target and one to build?
I see it more undertandable. The first function to manage the unit has to do more checks that are unnecessary for the profit function used to evaluate the build.

+/* second, we search for enemy units near us,
+   if we don't find any own empty city */
+
+/* third, we search for undefended enemy cities near us */
+
+return profit;

Missing code? :)
   Yes. Done.

+/*******************************************************************
+ * Chooses to build a paratroop if necessary
+ ******************************************************************/
+void ai_choose_paratrooper(struct player *pplayer, struct city *pcity,
+                         struct ai_choice *choice)
+{
+
+/* military_advisor_choose_build does something idiotic,
+ * this function should not be called if there is danger... */
+if (choice->want >= 100 && choice->type != CT_ATTACKER) {
+  return;

assert, not return, IMHO.
   No, it's done that way in airair.c.

I undestand that this only modifies the previous choice if the ai is already building an attacker.

Also, beware of line lenghts. Max 80 chars.
   Ok.

diff -NurEbB -Xfreeciv-cvs-Feb-27/diff_ignore 
freeciv-cvs-Feb-27/ai/advmilitary.c 
freeciv-cvs-Feb-27-paratroop/ai/advmilitary.c
--- freeciv-cvs-Feb-27/ai/advmilitary.c 2004-02-26 07:01:21.000000000 +0100
+++ freeciv-cvs-Feb-27-paratroop/ai/advmilitary.c       2004-03-18 
02:37:15.000000000 +0100
@@ -35,6 +35,7 @@
 #include "aidiplomat.h"
 #include "aihand.h"
 #include "ailog.h"
+#include "aiparatrooper.h"
 #include "aitools.h"
 #include "aiunit.h"

@@ -1306,6 +1307,9 @@
   /* Consider making an airplane */
   (void) ai_choose_attacker_air(pplayer, pcity, choice);

+  /* Consider making a paratrooper */
+  ai_choose_paratrooper(pplayer, pcity, choice);
+
   /* Check if we want a sailing attacker. Have to put sailing first
      before we mung the seamap */
   unit_type = ai_choose_attacker(pcity, SEA_MOVING);
diff -NurEbB -Xfreeciv-cvs-Feb-27/diff_ignore 
freeciv-cvs-Feb-27/ai/aiparatrooper.c 
freeciv-cvs-Feb-27-paratroop/ai/aiparatrooper.c
--- freeciv-cvs-Feb-27/ai/aiparatrooper.c       1970-01-01 01:00:00.000000000 
+0100
+++ freeciv-cvs-Feb-27-paratroop/ai/aiparatrooper.c     2004-03-31 
13:25:01.000000000 +0200
@@ -0,0 +1,383 @@
+/**********************************************************************
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+
+#include "city.h"
+#include "citytools.h"
+#include "log.h"
+#include "pf_tools.h"
+#include "player.h"
+#include "unit.h"
+#include "unittools.h"
+
+#include "ailog.h"
+#include "aiparatrooper.h"
+#include "aiunit.h"
+#include "aitools.h"
+
+#define LOGLEVEL_PARATROOPER LOG_NORMAL
+
+/**********************************************************************
+  Find own city to go there to recover and paradroop.
+
+  TODO: Actually check how safe the city is. This is a difficult
+  decision not easily taken, since we also want to protect unsafe
+  cities, at least most of the time.
+**********************************************************************/
+static struct city *find_nearest_own_city_at(struct player *pplayer,
+                                             int x, int y)
+{ int best = 0, cur = 100;
+  int continent = map_get_continent(x, y);
+  struct city *acity = NULL;
+
+  city_list_iterate(pplayer->cities, pcity) {
+    cur = real_map_distance(pcity->x, pcity->y, x ,y);
+    if (city_got_building(pcity, B_BARRACKS)
+       || city_got_building(pcity, B_BARRACKS2)
+       || city_got_building(pcity, B_BARRACKS3)) {
+      cur /= 3;
+    }
+    if (continent != map_get_continent(pcity->x, pcity->y)) {
+      /* it must be in the same continent */
+      cur = 0;
+    }
+    if (cur < best && (cur > 0) ) {
+      best = cur;
+      acity = pcity;
+    }
+  } city_list_iterate_end;
+
+  return acity;
+}
+
+/**********************************************************************
+ This function does manage the paratrooper units of the AI.
+ 1st.- checks for cities left alone without defenders
+ 2nd.- checks for enemy units in range
+ 3th.- checks for enemy cities left alone
+**********************************************************************/
+void ai_manage_paratrooper(struct player *pplayer, struct unit *punit)
+{
+  struct city *pcity = map_get_city(punit->x, punit->y);
+  struct city *acity = NULL;
+  int sanity = punit->id;
+  int x_dest = 0, y_dest = 0, best = 0;
+
+  CHECK_UNIT(punit);
+
+  /* defend attacking (and be opportunistic too)*/
+  if (!ai_military_rampage(punit, RAMPAGE_ANYTHING,
+                           RAMPAGE_FREE_CITY_OR_BETTER)) {
+    /* dead */
+    return;
+  }
+
+  /* check to recover hit points */
+  if ((punit->hp < unit_type(punit)->hp) && pcity) {
+    UNIT_LOG(LOGLEVEL_PARATROOPER, punit, "recovering hit points.",
+             pplayer->name, unit_type(punit)->name,
+             punit->id, punit->x, punit->y);
+    return;
+  }
+
+  /* nothing to do! */
+  if (punit->moves_left == 0) {
+    return;
+  }
+
+  /* we must defend the city! */
+  if (stay_and_defend(punit)) {
+    UNIT_LOG(LOGLEVEL_PARATROOPER, punit, "defending the city.",
+             pplayer->name, unit_type(punit)->name,
+            punit->id, punit->x, punit->y);
+    return;
+  }
+
+  if (can_unit_paradrop(punit)) {
+    /* unit can paradrop, so use it! */
+    int range = unit_type(punit)->paratroopers_range;
+
+    /* first, we search for undefended cities in danger */
+    square_iterate(punit->x, punit->y, range, x, y) {
+      acity = map_get_city(x,y);
+      if ((acity) && (city_owner(acity) == pplayer) &&
+          (unit_list_size(&(map_get_tile(x, y)->units)) == 0)) {
+        /* own city and empty! */
+        if ((acity->size * acity->ai.urgency) > best) {
+          best = acity->size * acity->ai.urgency;
+          x_dest = acity->x; y_dest = acity->y;
+        }
+      }
+    } square_iterate_end;
+
+    /* second, we search for enemy units near us,
+       if we don't find any own empty city */
+    if (best == 0) {
+      square_iterate(punit->x, punit->y, range, x, y) {
+        struct tile *ptile = map_get_tile(x, y);
+        if (unit_list_size(&(ptile->units)) > 0) {
+          struct unit *pdef = unit_list_get(&ptile->units, 0);
+         if (pplayers_at_war(pplayer, unit_owner(pdef))) {
+           int benefit = stack_cost(pdef);
+            benefit = (benefit * punit->hp) / unit_type(punit)->hp;
+            if (benefit > best) {
+              /* best enemy unit to kill! */
+             best = benefit;
+             x_dest = x; y_dest = y;
+           }
+         }
+        }
+      } square_iterate_end;
+
+      if (best > 0) {
+        /* now we have the location of the enemy unit,
+          we find the best tile around it,
+          to attack him */
+       struct city *acity = find_nearest_own_city_at(unit_owner(punit),
+                                                     x_dest, y_dest);
+       int xd = 0, yd = 0, dist = 0;
+
+       if (acity) {
+         /* we continue if we find a city in that continent */
+         dist = real_map_distance(x_dest, y_dest, acity->x, acity->y);
+         best = 0;
+         square_iterate(x_dest, y_dest, 1, x, y) {
+           int rating = 0;
+           if (is_ocean(map_get_terrain(x, y))) {
+             /*  do not paradrop to ocean! */
+             continue;
+           }
+           if (unit_list_size(&(map_get_tile(x, y)->units)) > 0) {
+             /* do not paradrop on a unit! */
+             continue;
+           }
+            if (could_unit_move_to_tile(punit, x, y)) {
+             switch(map_get_terrain(x, y)) {
+               case T_FOREST:
+               case T_HILLS:
+               case T_JUNGLE:
+                 rating = 2; break;
+               case T_MOUNTAINS:
+                 rating = 4; break;
+               default:
+                 rating = 0; break;
+             };
+             rating += (dist - real_map_distance(x, y, acity->x, acity->y));
+
+             if (rating > best) {
+               /* we assign the best location to attack the unit */
+               best = rating;
+               xd = x; yd = y;
+             }
+           }
+         } square_iterate_end;
+
+          /* we assign the last best values */
+         if (best) {
+           x_dest = xd; y_dest = yd;
+         }
+       } else {
+         best = 0;
+         x_dest = 0; y_dest = 0;
+       }
+      }
+    }
+
+    /* third, we search for undefended enemy cities near us */
+    if (best == 0) {
+      square_iterate(punit->x, punit->y, range, x, y) {
+        acity = map_get_city(x,y);
+        if ((acity) && pplayers_at_war(pplayer, city_owner(acity)) &&
+            (unit_list_size(&(map_get_tile(x, y)->units)) == 0)) {
+          /* enemy empty city! */
+          if (acity->size > best) {
+            best = acity->size;
+            x_dest = acity->x; y_dest = acity->y;
+          }
+        }
+      } square_iterate_end;
+    }
+
+    if (best) {
+      /* we move the unit */
+      set_goto_dest(punit, x_dest, y_dest);
+      UNIT_LOG(LOGLEVEL_PARATROOPER, punit, "paradroping.",
+               pplayer->name, unit_type(punit)->name,
+              punit->id, punit->x, punit->y);
+      if (do_paradrop(punit, x_dest, y_dest)) {
+        /* successfull! */
+        if (!find_unit_by_id(sanity)) {
+          /* the unit did not survive the move */
+          return;
+        }
+        /* and we attack the target */
+        (void)ai_military_rampage(punit, RAMPAGE_ANYTHING, RAMPAGE_ANYTHING);
+      }
+      clear_goto_dest(punit);
+    } else {
+      UNIT_LOG(LOGLEVEL_PARATROOPER, punit, "dind't find objective to paradrop 
to.",
+               pplayer->name, unit_type(punit)->name,
+              punit->id, punit->x, punit->y);
+      /* go to zones in danger */
+      square_iterate(punit->x, punit->y, range, x, y) {
+        struct city *acity = map_get_city(x, y);
+        if (acity && pplayers_allied(city_owner(acity), pplayer)) {
+          if (acity->ai.danger > best) {
+            best = acity->ai.danger;
+            x_dest = acity->x; y_dest = acity->y;
+          }
+        }
+      } square_iterate_end;
+
+      if (best && ((punit->x != x_dest) && (punit->y != y_dest))) {
+        set_goto_dest(punit, x_dest, y_dest);
+        UNIT_LOG(LOGLEVEL_PARATROOPER, punit, "moving to zones in danger.",
+                 pplayer->name, unit_type(punit)->name,
+                punit->id, punit->x, punit->y);
+        (void)do_paradrop(punit, x_dest, y_dest);
+        if (find_unit_by_id(sanity)) {
+          clear_goto_dest(punit);
+        }
+      }
+    }
+  } else {
+    /* we can't paradrop :-(  */
+    struct city *acity = NULL;
+
+    /* we are in a city, so don't try to find another */
+    if (pcity) {
+      UNIT_LOG(LOGLEVEL_PARATROOPER, punit, "waiting in a city for next turn.",
+               pplayer->name, unit_type(punit)->name,
+              punit->id, punit->x, punit->y);
+      return;
+    }
+
+    /* find a city to go to recover and paradrop from */
+    acity = find_nearest_own_city_at(unit_owner(punit), punit->x, punit->y);
+
+    if (acity) {
+      if (!ai_unit_goto(punit, acity->x, acity->y)) {
+        /* die or unsuccessfull move */
+       return;
+      }
+    } else {
+        UNIT_LOG(LOGLEVEL_PARATROOPER, punit, "didn't find city to go and 
recover.",
+                 pplayer->name, unit_type(punit)->name,
+                punit->id, punit->x, punit->y);
+       /* TODO: decide what to do now! */
+    }
+  }
+}
+
+/*******************************************************************
+  Computes the want for an operation
+ ******************************************************************/
+static int find_something_to_paradrop(struct unit *punit, int x, int y)
+{ int profit = 0;
+  int range = unit_type(punit)->paratroopers_range;
+  struct city *acity = NULL;
+  struct player *pplayer = unit_owner(punit);
+
+  /* first, we search for undefended cities */
+  square_iterate(punit->x, punit->y, range, x, y) {
+    acity = map_get_city(x,y);
+    if ((acity) && (city_owner(acity) == pplayer) &&
+        (unit_list_size(&(map_get_tile(x, y)->units)) == 0)) {
+      /* own city and empty! */
+      if ((acity->size *
+           (acity->trade_prod + acity->shield_prod + acity->food_prod) *
+           acity->ai.urgency) > profit) {
+        profit = (acity->size *
+                 (acity->trade_prod + acity->shield_prod + acity->food_prod) *
+                  acity->ai.urgency);
+      }
+    }
+  } square_iterate_end;
+
+  /* second, we search for enemy units near us */
+  square_iterate(punit->x, punit->y, range, x, y) {
+    struct tile *ptile = map_get_tile(x, y);
+    if (unit_list_size(&(ptile->units)) > 0) {
+      struct unit *pdef = unit_list_get(&ptile->units, 0);
+      if (pplayers_at_war(pplayer, unit_owner(pdef))) {
+        int benefit = stack_cost(pdef);
+        benefit = (benefit * punit->hp) / unit_type(punit)->hp;
+        if (benefit > profit) {
+          /* best enemy unit to kill! */
+          profit = benefit;
+        }
+      }
+    }
+  } square_iterate_end;
+
+  /* third, we search for undefended enemy cities near us */
+  square_iterate(punit->x, punit->y, range, x, y) {
+    acity = map_get_city(x,y);
+    if ((acity) && pplayers_at_war(city_owner(acity), pplayer) &&
+        (unit_list_size(&(map_get_tile(x, y)->units)) == 0)) {
+      /* enemy city and empty! */
+      if ((acity->size *
+           (acity->trade_prod + acity->shield_prod + acity->food_prod)) > 
profit) {
+        profit = (acity->size *
+                 (acity->trade_prod + acity->shield_prod + acity->food_prod));
+      }
+    }
+  } square_iterate_end;
+
+  return profit;
+}
+
+/*******************************************************************
+ * Chooses to build a paratroop if necessary
+ ******************************************************************/
+void ai_choose_paratrooper(struct player *pplayer, struct city *pcity,
+                           struct ai_choice *choice)
+{
+
+  /* military_advisor_choose_build does something idiotic,
+   * this function should not be called if there is danger... */
+  if (choice->want >= 100 && choice->type != CT_ATTACKER) {
+    return;
+  }
+
+  unit_type_iterate(u_type) {
+    if (!unit_type_flag(u_type, F_PARATROOPERS)) {
+      continue;
+    }
+
+    if (!can_build_unit(pcity, u_type)) {
+      continue;
+    }
+
+    struct unit *virtual_unit =
+      create_unit_virtual(pplayer, pcity, u_type,
+                          do_make_unit_veteran(pcity, u_type));
+    int profit = find_something_to_paradrop(virtual_unit, pcity->x, pcity->y);
+    if (profit > choice->want){
+      /* Update choice */
+      choice->want = profit;
+      choice->choice = u_type;
+      choice->type = CT_ATTACKER;
+      freelog(LOGLEVEL_PARATROOPER, "%s wants to build %s (want=%d)",
+              pcity->name, get_unit_type(u_type)->name, profit);
+    }
+    destroy_unit_virtual(virtual_unit);
+  } unit_type_iterate_end;
+
+  return;
+}
diff -NurEbB -Xfreeciv-cvs-Feb-27/diff_ignore 
freeciv-cvs-Feb-27/ai/aiparatrooper.h 
freeciv-cvs-Feb-27-paratroop/ai/aiparatrooper.h
--- freeciv-cvs-Feb-27/ai/aiparatrooper.h       1970-01-01 01:00:00.000000000 
+0100
+++ freeciv-cvs-Feb-27-paratroop/ai/aiparatrooper.h     2004-03-18 
02:34:36.000000000 +0100
@@ -0,0 +1,25 @@
+/**********************************************************************
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+#ifndef FC__AIPARATROOPER_H
+#define FC__AIPARATROOPER_H
+
+struct ai_choice;
+struct city;
+struct player;
+struct unit;
+
+void ai_manage_paratrooper(struct player *pplayer, struct unit *punit);
+void ai_choose_paratrooper(struct player *pplayer, struct city *pcity,
+                          struct ai_choice *choice);
+
+#endif /* FC__AIPARATROOPER_H */
diff -NurEbB -Xfreeciv-cvs-Feb-27/diff_ignore freeciv-cvs-Feb-27/ai/aiunit.c 
freeciv-cvs-Feb-27-paratroop/ai/aiunit.c
--- freeciv-cvs-Feb-27/ai/aiunit.c      2004-02-26 07:01:21.000000000 +0100
+++ freeciv-cvs-Feb-27-paratroop/ai/aiunit.c    2004-03-30 14:26:38.000000000 
+0200
@@ -55,6 +55,7 @@
 #include "aidiplomat.h"
 #include "aihand.h"
 #include "ailog.h"
+#include "aiparatrooper.h"
 #include "aitools.h"
 
 #include "aiunit.h"
@@ -67,11 +68,6 @@
 static void ai_manage_barbarian_leader(struct player *pplayer,
                                       struct unit *leader);

-#define RAMPAGE_ANYTHING                 1
-#define RAMPAGE_HUT_OR_BETTER        99998
-#define RAMPAGE_FREE_CITY_OR_BETTER  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);
@@ -665,7 +661,7 @@
 
   FIXME: We should check for fortresses here.
 **************************************************************************/
-static bool stay_and_defend(struct unit *punit)
+bool stay_and_defend(struct unit *punit)
 {
   struct city *pcity = map_get_city(punit->x, punit->y);
   bool has_defense = FALSE;
@@ -1052,7 +1048,7 @@
 
   Returns TRUE if survived the rampage session.
 **************************************************************************/
-static bool ai_military_rampage(struct unit *punit, int thresh_adj, 
+bool ai_military_rampage(struct unit *punit, int thresh_adj,
                                 int thresh_move)
 {
   int count = punit->moves_left + 1; /* break any infinite loops */
@@ -2653,6 +2649,9 @@
   } else if (unit_has_role(punit->type, L_BARBARIAN_LEADER)) {
     ai_manage_barbarian_leader(pplayer, punit);
     return;
+  } else if (unit_flag(punit, F_PARATROOPERS)) {
+    ai_manage_paratrooper(pplayer, punit);
+    return;
   } else if (get_transporter_capacity(punit) > 0) {
     ai_manage_ferryboat(pplayer, punit);
     return;
diff -NurEbB -Xfreeciv-cvs-Feb-27/diff_ignore freeciv-cvs-Feb-27/ai/aiunit.h 
freeciv-cvs-Feb-27-paratroop/ai/aiunit.h
--- freeciv-cvs-Feb-27/ai/aiunit.h      2004-01-12 07:16:15.000000000 +0100
+++ freeciv-cvs-Feb-27-paratroop/ai/aiunit.h    2004-03-30 14:27:31.000000000 
+0200
@@ -46,6 +46,11 @@
 
 extern Unit_Type_id simple_ai_types[U_LAST];
 
+#define RAMPAGE_ANYTHING                 1
+#define RAMPAGE_HUT_OR_BETTER        99998
+#define RAMPAGE_FREE_CITY_OR_BETTER  99999
+bool ai_military_rampage(struct unit *punit, int thresh_adj,
+                         int thresh_move);
 void ai_manage_units(struct player *pplayer); 
 int could_unit_move_to_tile(struct unit *punit, int dest_x, int dest_y);
 int look_for_charge(struct player *pplayer, struct unit *punit,
@@ -64,6 +69,7 @@
                     int *x, int *y);
 
 int build_cost_balanced(Unit_Type_id type);
+bool stay_and_defend(struct unit *punit);
 int unittype_att_rating(Unit_Type_id type, int veteran,
                         int moves_left, int hp);
 int unit_att_rating(struct unit *punit);
diff -NurEbB -Xfreeciv-cvs-Feb-27/diff_ignore freeciv-cvs-Feb-27/ai/Makefile.am 
freeciv-cvs-Feb-27-paratroop/ai/Makefile.am
--- freeciv-cvs-Feb-27/ai/Makefile.am   2003-09-22 07:00:04.000000000 +0200
+++ freeciv-cvs-Feb-27-paratroop/ai/Makefile.am 2004-03-18 01:07:19.000000000 
+0100
@@ -25,6 +25,8 @@
                aihand.h        \
                ailog.c         \
                ailog.h         \
+               aiparatrooper.c \
+               aiparatrooper.h \
                aitech.c        \
                aitech.h        \
                aitools.c       \

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