Complete.Org: Mailing Lists: Archives: freeciv-ai: October 2005:
[freeciv-ai] (PR#14366) Teach AI to use paratroopers
Home

[freeciv-ai] (PR#14366) Teach AI to use paratroopers

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [freeciv-ai] (PR#14366) Teach AI to use paratroopers
From: "Mateusz Stefek" <mstefek@xxxxxxxxx>
Date: Tue, 18 Oct 2005 01:35:31 -0700
Reply-to: bugs@xxxxxxxxxxx

<URL: http://bugs.freeciv.org/Ticket/Display.html?id=14366 >

This patch teaches AI how to use paratroopers. It's based on Jordi
Negrevernis i Font's patch which was sent to the freeciv-ai list in July.

I updated it, changed a logic a bit and made AI cheat less.
I playtested it and AI played reasonable.
--
mateusz
Index: ai/aiunit.h
===================================================================
--- ai/aiunit.h (wersja 11148)
+++ ai/aiunit.h (kopia robocza)
@@ -54,6 +54,12 @@
 
 extern struct unit_type *simple_ai_types[U_LAST];
 
+#define RAMPAGE_ANYTHING                 1
+#define RAMPAGE_HUT_OR_BETTER        99998
+#define RAMPAGE_FREE_CITY_OR_BETTER  99999
+#define BODYGUARD_RAMPAGE_THRESHOLD (SHIELD_WEIGHTING * 4)
+bool ai_military_rampage(struct unit *punit, int thresh_adj,
+                         int thresh_move);
 void ai_manage_units(struct player *pplayer); 
 void ai_manage_unit(struct player *pplayer, struct unit *punit);
 void ai_manage_military(struct player *pplayer,struct unit *punit);
Index: ai/advmilitary.c
===================================================================
--- ai/advmilitary.c    (wersja 11148)
+++ ai/advmilitary.c    (kopia robocza)
@@ -38,6 +38,7 @@
 #include "aihand.h"
 #include "aihunt.h"
 #include "ailog.h"
+#include "aiparatrooper.h"
 #include "aitech.h"
 #include "aitools.h"
 #include "aiunit.h"
@@ -1427,6 +1428,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 */
   punittype = ai_choose_attacker(pcity, SEA_MOVING);
Index: ai/Makefile.am
===================================================================
--- ai/Makefile.am      (wersja 11148)
+++ ai/Makefile.am      (kopia robocza)
@@ -33,6 +33,8 @@
                aihunt.h        \
                ailog.c         \
                ailog.h         \
+               aiparatrooper.c \
+               aiparatrooper.h \
                aisettler.c     \
                aisettler.h     \
                aitech.c        \
Index: ai/aiunit.c
===================================================================
--- ai/aiunit.c (wersja 11148)
+++ ai/aiunit.c (kopia robocza)
@@ -1,4 +1,4 @@
-/********************************************************************** 
+/**********************************************************************
  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
@@ -59,6 +59,7 @@
 #include "aihand.h"
 #include "aihunt.h"
 #include "ailog.h"
+#include "aiparatrooper.h"
 #include "aitools.h"
 
 #include "aiunit.h"
@@ -71,12 +72,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
-#define BODYGUARD_RAMPAGE_THRESHOLD (SHIELD_WEIGHTING * 4)
-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_defend(struct player *pplayer,struct unit *punit);
 static void ai_military_attack(struct player *pplayer,struct unit *punit);
@@ -154,7 +149,7 @@
     if (!transported) {
       return;
     }
-    UNIT_LOG(LOG_DEBUG, transported, "airlifted to defend %s", 
+    UNIT_LOG(LOG_DEBUG, transported, "airlifted to defend %s",
              most_needed->name);
     do_airline(transported, most_needed);
   } while (TRUE);
@@ -599,7 +594,7 @@
 /*************************************************************************
   Look for worthy targets within a one-turn horizon.
 *************************************************************************/
-static struct pf_path *find_rampage_target(struct unit *punit, 
+static struct pf_path *find_rampage_target(struct unit *punit,
                                            int thresh_adj, int thresh_move)
 {
   struct pf_map *tgt_map;
@@ -666,7 +661,7 @@
 }
 
 /*************************************************************************
-  Find and kill anything reachable within this turn and worth more than 
+  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 
@@ -679,7 +674,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 */
@@ -839,7 +834,7 @@
 
   Requires an initialized warmap!
 **************************************************************************/
-int look_for_charge(struct player *pplayer, struct unit *punit, 
+int look_for_charge(struct player *pplayer, struct unit *punit,
                     struct unit **aunit, struct city **acity)
 {
   int dist, def, best = 0;
@@ -2127,6 +2122,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
              && !unit_flag(punit, F_MISSILE_CARRIER)
              && punit->ai.ai_role != AIUNIT_HUNTER) {
/**********************************************************************
 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 */
/**********************************************************************
 Freeciv - Copyright (C) 2005 The Freeciv Team
   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 "game.h"
#include "log.h"
#include "maphand.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_DEBUG

/*****************************************************************************
  Find best tile the paratrooper should jump to.
*****************************************************************************/
static struct tile* find_best_tile_to_paradrop_to(struct unit *punit)
{
  int best = 0;
  int val;
  struct tile* best_tile = NULL;
  int range = unit_type(punit)->paratroopers_range;
  struct city* acity;
  struct player* pplayer = unit_owner(punit);

  /* First, we search for undefended cities in danger */
  square_iterate(punit->tile, range, ptile) {
    if (!map_is_known(ptile, pplayer)) {
      continue;
    }
  
    acity = tile_get_city(ptile);
    if (acity && city_owner(acity) == unit_owner(punit)
        && unit_list_size(ptile->units) == 0) {
      val = acity->size * acity->ai.urgency;
      if (val > best) {
        best = val;
        best_tile = ptile;
      }
    }
  } square_iterate_end;
  
  if (best_tile != NULL) {
    acity = tile_get_city(best_tile);
    UNIT_LOG(LOGLEVEL_PARATROOPER, punit, 
             "Choose to jump in order to protect allied city %s (%d %d). "
             "Benefit: %d",
             acity->name, best_tile->x, best_tile->y, best);
    return best_tile;
  }

  /* Second, we search for undefended enemy cities */
  square_iterate(punit->tile, range, ptile) {
    acity = tile_get_city(ptile);
    if (acity && pplayers_at_war(unit_owner(punit), city_owner(acity)) &&
        (unit_list_size(ptile->units) == 0)) {
      if (!map_is_known_and_seen(ptile, pplayer)
          && ai_handicap(pplayer, H_FOG)) {
        continue;
      }
      /* Prefer big cities on other continents */
      val = acity->size
            + (tile_get_continent(punit->tile) != tile_get_continent(ptile));
      if (val > best) {
        best = val;
        best_tile = ptile;
      }
    }
  } square_iterate_end;
  
  if (best_tile != NULL) {
    acity = tile_get_city(best_tile);
    UNIT_LOG(LOGLEVEL_PARATROOPER, punit, 
             "Choose to jump into enemy city %s (%d %d). Benefit: %d",
             acity->name, best_tile->x, best_tile->y, best);
    return best_tile;
  }

  /* Jump to kill adjacent units */
  square_iterate(punit->tile, range, ptile) {
    if (is_ocean(ptile->terrain)) {
      continue;
    }
    if (!map_is_known(ptile, pplayer)) {
      continue;
    }
    acity = tile_get_city(ptile);
    if (acity && !pplayers_allied(city_owner(acity), pplayer)) {
      continue;
    }
    if (!acity && unit_list_size(ptile->units) > 0) {
      continue;
    }
    /* Iterate over adjacent tile to find good victim */
    adjc_iterate(ptile, target) {
      if (unit_list_size(target->units) == 0
          || !can_unit_attack_tile(punit, target)
          || is_ocean(target->terrain)
          || (!map_is_known_and_seen(target, pplayer)
              && ai_handicap(pplayer, H_FOG))) {
        continue;
      }
      val = 0;
      if (is_stack_vulnerable(target)) {
        unit_list_iterate(target->units, victim) {
          val += victim->hp * 10;
        } unit_list_iterate_end;
      } else {
        val += get_defender(punit, target)->hp * 10;
      }
      val *= unit_win_chance(punit, get_defender(punit, target));
      val += ptile->terrain->defense_bonus;
      val -= punit->hp * 10;
      
      if (val > best) {
        best = val;
        best_tile = ptile;
      }
    } adjc_iterate_end;
  } square_iterate_end;

  if (best_tile != NULL) {
    UNIT_LOG(LOG_DEBUG, punit,
             "Choose to jump at %d %d to attack adjacent units. Benefit: %d",
              best_tile->x, best_tile->y, best);
  }
  
  return best_tile;
}
                                          

/**********************************************************************
 This function does manage the paratrooper units of the AI.
**********************************************************************/
void ai_manage_paratrooper(struct player *pplayer, struct unit *punit)
{
  struct city *pcity = tile_get_city(punit->tile);
  struct tile *ptile_dest = NULL;

  int sanity = punit->id;

  /* 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 / 2) && pcity) {
    UNIT_LOG(LOGLEVEL_PARATROOPER, punit, "Recovering hit points.");
    return;
  }

  /* nothing to do! */
  if (punit->moves_left == 0) {
    return;
  }
  
  if (pcity && unit_list_size(punit->tile->units) == 1) {
    UNIT_LOG(LOGLEVEL_PARATROOPER, punit, "Defending the city.");
    return;
  }

  if (can_unit_paradrop(punit)) {
    ptile_dest = find_best_tile_to_paradrop_to(punit);

    if (ptile_dest) {
      if (do_paradrop(punit, ptile_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);
      }
    }
  } 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.");
      return;
    }

    /* find a city to go to recover and paradrop from */
    acity = find_nearest_safe_city(punit);

    if (acity) {
      UNIT_LOG(LOGLEVEL_PARATROOPER, punit, "Going to %s", acity->name);
      if (!ai_unit_goto(punit, acity->tile)) {
        /* die or unsuccessfull move */
        return;
      }
    } else {
      UNIT_LOG(LOGLEVEL_PARATROOPER, punit,
               "didn't find city to go and recover.");
      /* TODO: decide what to do now! */
    }
  }
}

/*******************************************************************
  Evaluate value of the unit.
  Idea: one paratrooper can scare/protect all cities in his range
******************************************************************/
static int calculate_want_for_paratrooper(struct unit *punit,
                                          struct tile *ptile_city)
{

  int range = unit_type(punit)->paratroopers_range;
  int profit = 0;
  struct unit_type* u_type = unit_type(punit);
  struct player* pplayer = unit_owner(punit);

  profit += u_type->defense_strength
          + u_type->move_rate 
          + u_type->attack_strength;
  
  square_iterate(ptile_city, range, ptile) {
    int multiplier;
    
    if (!map_is_known(ptile, pplayer)) {
      continue;
    }
    
    struct city* pcity = tile_get_city(ptile);
    
    if (!pcity) {
      continue;
    }

    /* We prefer jumping to other continents. On the same continent we 
     * can fight traditionally */    
    if (tile_get_continent(ptile_city) != tile_get_continent(ptile)) {
      if (get_continent_size(tile_get_continent(ptile)) < 3) {
        /* Tiny island are hard to conquer with traditional units */
        multiplier = 10;
      } else {
        multiplier = 5;
      }
    } else {
      multiplier = 1;
    }
    
    /* There are lots of units, the city will be safe against paratroopers. */
    if (unit_list_size(ptile->units) > 2) {
      continue;
    }
    
    /* Prefer long jumps.
     * If a city is near we can take/protect it with normal units */
    if (pplayers_allied(pplayer, city_owner(pcity))) {
      profit += pcity->size
                * multiplier * real_map_distance(ptile_city, ptile) / 2;
    } else {

      profit += pcity->size * multiplier * real_map_distance(ptile_city, ptile);
    }
  } square_iterate_end;

  return profit;
}

/*******************************************************************
  Chooses to build a paratroopers if necessary
*******************************************************************/
void ai_choose_paratrooper(struct player *pplayer, struct city *pcity,
                           struct ai_choice *choice)
{
  int profit;
  Tech_type_id tech_req;
  Tech_type_id requirements[A_LAST];
  int num_requirements = 0;
  int i;

  /* 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;
    }

    /* assign tech for paratroopers */
    tech_req = u_type->tech_requirement;
    if (tech_req != A_NONE && tech_req != A_UNSET) {
      for (i = 0; i < num_requirements; i++) {
        if (requirements[i] == tech_req) {
          break;
        }
      }
      if (i == num_requirements) {
        requirements[i++] = tech_req;
      }
    }

    /* we only update choice struct if we can build it! */
    if (!can_build_unit(pcity, u_type)) {
      continue;
    }

    /* it's worth building that unit? */
    struct unit *virtual_unit = create_unit_virtual(pplayer, pcity, u_type,
                                                    do_make_unit_veteran
                                                    (pcity, u_type));
    profit = calculate_want_for_paratrooper(virtual_unit, pcity->tile);
    destroy_unit_virtual(virtual_unit);

    /* update choise struct if it's worth */
    if (profit > choice->want) {
      /* Update choice */
      choice->want = profit;
      choice->choice = u_type->index;
      choice->type = CT_ATTACKER;
      freelog(LOGLEVEL_PARATROOPER, "%s wants to build %s (want=%d)",
              pcity->name, u_type->name, profit);
    }
  } unit_type_iterate_end;

  /* we raise want if the required tech is not known */
  for (i = 0; i < num_requirements; i++) {
    tech_req = requirements[i];
    pplayer->ai.tech_want[tech_req] += 2;
    freelog(LOGLEVEL_PARATROOPER, "Raising tech want in city %s for %s "
              "stimulating %s with %d (%d) and req",
              pcity->name, pplayer->name, get_tech_name(pplayer,
                                                        tech_req), 2,
              pplayer->ai.tech_want[tech_req]);

    /* now, we raise want for prerequisites */
    tech_type_iterate(k) {
      if (is_tech_a_req_for_goal(pplayer, k, tech_req)) {
        pplayer->ai.tech_want[k] += 1;
      }
    } tech_type_iterate_end;
  }
  return;
}

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