Complete.Org: Mailing Lists: Archives: freeciv-ai: November 2003:
[freeciv-ai] (PR#4742) AI hunting code v3
Home

[freeciv-ai] (PR#4742) AI hunting code v3

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: Gregory.Berkolaiko@xxxxxxxxxxxx
Subject: [freeciv-ai] (PR#4742) AI hunting code v3
From: "Per I. Mathisen" <per@xxxxxxxxxxx>
Date: Sun, 23 Nov 2003 13:30:36 -0800
Reply-to: rt@xxxxxxxxxxx

<URL: http://rt.freeciv.org/Ticket/Display.html?id=4742 >

This is a more generalized version of the AI seahunter code previously
posted under this ticket. We can now, in theory, also build hunters for
land purposes. This will be practically enabled to replace fstk when that
time comes. For now, its main purpose is still to hunt for hostiles at
sea, and in particular enemy transports.

High-tech AIs with continents of their own should now be much harder. They
will seek out your transports with subs (potentially with missiles on
board, which they will use) and sink them. Each AI city will try to upkeep
one such hunter and maybe a missile for that hunter.

  - Per

/**********************************************************************
 Freeciv - Copyright (C) 2003 - 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.
***********************************************************************/
#ifndef FC__AIHUNT_H
#define FC__AIHUNT_H

#include "shared.h"             /* bool type */

struct ai_choice;
struct city;

void ai_hunter_choice(struct player *pplayer, struct city *pcity,
                      struct ai_choice *choice);
bool ai_hunter_qualify(struct player *pplayer, struct unit *punit);
int ai_hunter_findjob(struct player *pplayer, struct unit *punit);
bool ai_hunter_manage(struct player *pplayer, struct unit *punit);

#endif
/**********************************************************************
 Freeciv - Copyright (C) 2003 - 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 "city.h"
#include "combat.h"
#include "game.h"
#include "map.h"
#include "log.h"
#include "pf_tools.h"
#include "player.h"
#include "unit.h"

#include "citytools.h"
#include "settlers.h"

#include "aidata.h"
#include "ailog.h"
#include "aitools.h"
#include "aiunit.h"

#include "aihunt.h"

/**************************************************************************
  We don't need a hunter in this city if we already have one. Return 
  existing hunter if any.
**************************************************************************/
static struct unit *ai_hunter_find(struct player *pplayer, 
                                   struct city *pcity)
{
  unit_list_iterate(pcity->units_supported, punit) {
    if (ai_hunter_qualify(pplayer, punit)) {
      return punit;
    }
  } unit_list_iterate_end;
  unit_list_iterate(map_get_tile(pcity->x, pcity->y)->units, punit) {
    if (ai_hunter_qualify(pplayer, punit)) {
      return punit;
    }
  } unit_list_iterate_end;

  return NULL;
}

/**************************************************************************
  Guess best hunter unit type.
**************************************************************************/
static Unit_Type_id ai_hunter_guess_best(struct city *pcity,
                                         enum unit_move_type umt)
{
  Unit_Type_id bestid = -1;
  int best = 0;

  unit_type_iterate(i) {
    struct unit_type *ut = get_unit_type(i);
    int desire;

    if (ut->move_type != umt || !can_build_unit(pcity, i)
        || ut->attack_strength < ut->transport_capacity) {
      continue;
    }

    desire = (ut->hp
              * ut->attack_strength
              * ut->firepower
              * ut->move_rate
              + ut->defense_strength) / MAX(UNITTYPE_COSTS(ut), 1);

    if (unit_type_flag(i, F_CARRIER)
        || unit_type_flag(i, F_MISSILE_CARRIER)) {
      desire += desire / 6;
    }
    if (unit_type_flag(i, F_IGTER)) {
      desire += desire / 2;
    }
    if (unit_type_flag(i, F_IGTIRED)) {
      desire += desire / 8;
    }
    if (unit_type_flag(i, F_PARTIAL_INVIS)) {
      desire += desire / 4;
    }
    if (unit_type_flag(i, F_NO_LAND_ATTACK)) {
      desire -= desire / 4; /* less flexibility */
    }
    /* Causes continual unhappiness */
    if (unit_type_flag(i, F_FIELDUNIT)) {
      desire /= 2;
    }

    desire = amortize(desire, ut->build_cost / MAX(pcity->shield_surplus, 1));

    if (desire > best) {
        best = desire;
        bestid = i;
    }
  } unit_type_iterate_end;

  return bestid;
}

/**************************************************************************
  Check if we want to build a missile for our hunter.
**************************************************************************/
static void ai_hunter_missile_want(struct player *pplayer,
                                   struct city *pcity,
                                   struct ai_choice *choice)
{
  int best = -1, best_unit_type = -1;
  bool have_hunter = FALSE;

  unit_list_iterate(map_get_tile(pcity->x, pcity->y)->units, punit) {
    if (ai_hunter_qualify(pplayer, punit)
        && (unit_flag(punit, F_MISSILE_CARRIER)
            || unit_flag(punit, F_CARRIER))) {
      /* There is a potential hunter in our city which we can equip 
       * with a missile. Do it. */
      have_hunter = TRUE;
      break;
    }
  } unit_list_iterate_end;

  if (!have_hunter) {
    return;
  }

  unit_type_iterate(i) {
    struct unit_type *ut = get_unit_type(i);
    int desire;

    if (!BV_ISSET(ut->flags, F_MISSILE) || !can_build_unit(pcity, i)) {
      continue;
    }

    /* FIXME: We need to store some data that can tell us if
     * enemy transports are protected by anti-missile technology. 
     * In this case, want nuclear much more! */
    desire = (ut->hp
              * MIN(ut->attack_strength, 30) /* nuke fix */
              * ut->firepower
              * ut->move_rate) / UNITTYPE_COSTS(ut) + 1;

    /* Causes continual unhappiness */
    if (unit_type_flag(i, F_FIELDUNIT)) {
      desire /= 2;
    }

    desire = amortize(desire, ut->build_cost / MAX(pcity->shield_surplus, 1));

    if (desire > best) {
        best = desire;
        best_unit_type = i;
    }
  } unit_type_iterate_end;

  if (best > choice->want) {
    CITY_LOG(LOGLEVEL_HUNT, pcity, "pri missile w/ want %d", best);
    choice->choice = best_unit_type;
    choice->want = best;
    choice->type = CT_ATTACKER;
  } else if (best != -1) {
    CITY_LOG(LOGLEVEL_HUNT, pcity, "not pri missile w/ want %d"
             "(old want %d)", best, choice->want);
  }
}

/**************************************************************************
  Support function for ai_hunter_choice()
**************************************************************************/
static void eval_hunter_want(struct player *pplayer, struct city *pcity,
                             struct ai_choice *choice, int best_type,
                             bool veteran)
{
  struct unit *virtualunit;
  int want = 0;

  virtualunit = create_unit_virtual(pplayer, pcity, best_type, veteran);
  want = ai_hunter_findjob(pplayer, virtualunit);
  destroy_unit_virtual(virtualunit);
  if (want > choice->want) {
    CITY_LOG(LOGLEVEL_HUNT, pcity, "pri hunter w/ want %d", want);
    choice->choice = best_type;
    choice->want = want;
    choice->type = CT_ATTACKER;
  }
}

/**************************************************************************
  Check if we want to build a hunter.
**************************************************************************/
void ai_hunter_choice(struct player *pplayer, struct city *pcity,
                      struct ai_choice *choice)
{
  int best_land_hunter = ai_hunter_guess_best(pcity, LAND_MOVING);
  int best_sea_hunter = ai_hunter_guess_best(pcity, SEA_MOVING);
  struct unit *hunter = ai_hunter_find(pplayer, pcity);

  if ((best_land_hunter == -1 && best_sea_hunter == -1)
      || is_barbarian(pplayer) || !pplayer->is_alive
      || ai_handicap(pplayer, H_TARGETS)) {
    return; /* None available */
  }
  if (hunter) {
    /* Maybe want missiles to go with a hunter instead? */
    ai_hunter_missile_want(pplayer, pcity, choice);
    return;
  }

  if (best_sea_hunter >= 0) {
    eval_hunter_want(pplayer, pcity, choice, best_sea_hunter, 
                     do_make_unit_veteran(pcity, best_sea_hunter));
  }
  if (best_land_hunter >= 0) {
    eval_hunter_want(pplayer, pcity, choice, best_land_hunter, 
                     do_make_unit_veteran(pcity, best_land_hunter));
  }
}

/**************************************************************************
  Does this unit qualify as a hunter? FIXME: Should also check for
  combat damage? Or should repair code preempt this code? Just saying
  FALSE for damaged units does NOT work.
**************************************************************************/
bool ai_hunter_qualify(struct player *pplayer, struct unit *punit)
{
  struct unit_type *punittype = get_unit_type(punit->type);

  if (is_barbarian(pplayer)
      || !(is_sailing_unit(punit) || is_ground_unit(punit))
      || punittype->move_rate < 2 * SINGLE_MOVE
      || ATTACK_POWER(punit) <= 1
      || punit->owner != pplayer->player_no) {
    return FALSE;
  }
  if (unit_flag(punit, F_PARTIAL_INVIS)) {
    return TRUE;
  }
  /* TODO: insert better algo here */
  return IS_ATTACKER(punit);
}

/**************************************************************************
  Return want for making this (possibly virtual) unit into a hunter. Sets
  punit->ai.target to target's id.
**************************************************************************/
int ai_hunter_findjob(struct player *pplayer, struct unit *punit)
{
  int best_id = -1, best_val = -1;

  assert(!is_barbarian(pplayer));
  assert(pplayer->is_alive);

  players_iterate(aplayer) {
    if (!aplayer->is_alive || !pplayers_at_war(aplayer, pplayer)) {
      continue;
    }
    unit_list_iterate(aplayer->units, target) {
      struct tile *ptile = map_get_tile(target->x, target->y);
      int dist1, dist2, stackthreat = 0, stackcost = 0;
      struct unit *defender;

      if (ptile->city
          || target->ai.hunted & (1 << pplayer->player_no)
          || !is_ocean(ptile->terrain)
          || !is_sailing_unit(target)
          || !goto_is_sane(punit, target->x, target->y, TRUE)) {
        /* Can't hunt this one. */
        continue;
      }
      if (target->ai.cur_pos && target->ai.prev_pos) {
        dist1 = real_map_distance(punit->x, punit->y, target->ai.cur_pos->x, 
                                  target->ai.cur_pos->y);
        dist2 = real_map_distance(punit->x, punit->y, target->ai.prev_pos->x, 
                                  target->ai.prev_pos->y);
      } else {
        dist1 = dist2 = 0;
      }
      UNIT_LOG(LOGLEVEL_HUNT, punit, "considering chasing %s(%d, %d) id %d "
               "dist1 %d dist2 %d", unit_type(target)->name, target->x, 
               target->y, target->id, dist1, dist2);
      /* We can't attack units stationary in cities. */
      if (map_get_city(target->x, target->y) 
          && (dist2 == 0 || dist1 == dist2)) {
        continue;
      }
      /* We can't chase if we aren't faster or on intercept vector */
      if (unit_type(punit)->move_rate < unit_type(target)->move_rate
          && dist1 >= dist2) {
        UNIT_LOG(LOGLEVEL_HUNT, punit, "can't get to %s(%d, %d) was (%d, %d)",
                 unit_type(target)->name, target->x, target->y,
                 target->ai.prev_pos->x, target->ai.prev_pos->y);
        continue;
      }
      unit_list_iterate(ptile->units, sucker) {
        stackthreat += ATTACK_POWER(sucker);
        if (unit_flag(sucker, F_DIPLOMAT)) {
          stackthreat += 500;
        }
        stackcost += unit_type(sucker)->build_cost;
      } unit_list_iterate_end;
      defender = get_defender(punit, target->x, target->y);
      if (stackcost < unit_type(punit)->build_cost
          && unit_win_chance(punit, defender) < 0.6) {
        UNIT_LOG(LOGLEVEL_HUNT, punit, "chickening out from attacking %s"
                 "(%d, %d)", unit_type(defender)->name, defender->x, 
                 defender->y);
        continue;
      }
      stackthreat *= 9; /* WAG */
      stackthreat += stackcost;
      stackthreat /= real_map_distance(punit->x, punit->y, 
                                       target->x, target->y) + 1;
      UNIT_LOG(LOGLEVEL_HUNT, punit, "considering hunting %s's %s(%d, %d) id "
               "id %d with want %d, dist1 %d, dist2 %d", 
               unit_owner(defender)->name, unit_type(defender)->name, 
               defender->x, defender->y, defender->id, stackthreat, dist1, 
               dist2);
      /* Ok, now we FINALLY have a candidate */
      if (stackthreat > best_val) {
        best_val = stackthreat;
        best_id = target->id;
      }
    } unit_list_iterate_end;
  } players_iterate_end;

  punit->ai.target = best_id;
  if (best_val < MORT) {
    /* Safety against silly missions. Unset our role. */
    best_val = -1;
  } else {
    UNIT_LOG(LOGLEVEL_HUNT, punit, "suggest chasing %d with want %d",
             best_id, best_val);
  }
  return best_val;
}

/**************************************************************************
  Try to shoot our target with a missile. Also shoot down anything that
  might attempt to intercept _us_. We assign missiles to a hunter in
  ai_unit_new_role().
**************************************************************************/
static void ai_hunter_try_launch(struct player *pplayer,
                                 struct unit *punit,
                                 struct unit *target)
{
  int target_sanity = target->id;
  struct pf_parameter parameter;
  struct pf_map *map;

  unit_list_iterate(map_get_tile(punit->x, punit->y)->units, missile) {
    struct unit *sucker = NULL;

    if (missile->owner == pplayer->player_no
        && unit_flag(missile, F_MISSILE)) {
      UNIT_LOG(LOGLEVEL_HUNT, missile, "checking for hunt targets");
      pft_fill_default_parameter(&parameter);
      pft_fill_unit_parameter(&parameter, punit);
      map = pf_create_map(&parameter);

      pf_iterator(map, pos) {
        if (pos.total_MC > missile->moves_left / SINGLE_MOVE) {
          break;
        }
        unit_list_iterate(map_get_tile(pos.x, pos.y)->units, victim) {
          struct unit_type *ut = unit_type(victim);

          if (!pplayers_at_war(unit_owner(victim), pplayer)) {
            continue;
          }
          if (victim == target) {
            sucker = victim;
            UNIT_LOG(LOGLEVEL_HUNT, missile, "found primary target %d(%d, %d)"
                     " dist %d", victim->id, victim->x, victim->y, 
                     pos.total_MC);
            break; /* Our target! Get him!!! */
          }
          if (ut->move_rate + victim->moves_left > pos.total_MC
              && ATTACK_POWER(victim) > DEFENCE_POWER(punit)
              && (ut->move_type == SEA_MOVING
                  || ut->move_type == AIR_MOVING)) {
            /* Threat to our carrier. Kill it. */
            sucker = victim;
            UNIT_LOG(LOGLEVEL_HUNT, missile, "found aux target %d(%d, %d)",
                     victim->id, victim->x, victim->y);
            break;
          }
        } unit_list_iterate_end;
        if (sucker) {
          break; /* found something - kill it! */
        }
      } pf_iterator_end;
      pf_destroy_map(map);
      if (sucker) {
        ai_unit_goto(missile, sucker->x, sucker->y);
        sucker = find_unit_by_id(target_sanity); /* Sanity */
        if (sucker && is_tiles_adjacent(sucker->x, sucker->y, missile->x, 
missile->y)) {
          ai_unit_attack(missile, sucker->x, sucker->y);
        }
        target = find_unit_by_id(target_sanity); /* Sanity */
        break; /* try next missile, if any */
      }
    } /* if */
  } unit_list_iterate_end;
}

/**************************************************************************
  Manage a hunter unit. We assume it has AIUNIT_HUNTER role and a valid
  target in punit->ai.hunt.

  Returns FALSE if we could not use unit. If we return TRUE, unit might
  be dead.
**************************************************************************/
bool ai_hunter_manage(struct player *pplayer, struct unit *punit)
{
  struct unit *target = find_unit_by_id(punit->ai.target);
  int sanity_own = punit->id;
  int sanity_target;

  logdebug_suppress_warning; /* had to put it somewhere */

  CHECK_UNIT(punit);
  assert(punit->ai.ai_role == AIUNIT_HUNTER);

  /* Check that target is valid. */
  if (!target
      || !goto_is_sane(punit, target->x, target->y, TRUE)
      || !pplayers_at_war(pplayer, unit_owner(target))) {
    UNIT_LOG(LOGLEVEL_HUNT, punit, "target vanished");
    ai_unit_new_role(punit, AIUNIT_NONE, -1, -1);
    return FALSE;
  }
  UNIT_LOG(LOGLEVEL_HUNT, punit, "hunting %d(%d, %d)", target->id, target->x,
           target->y);
  sanity_target = target->id;

  /* Check if we can nuke it */
  ai_hunter_try_launch(pplayer, punit, target);

  /* Go towards it. */
  if (!ai_unit_goto(punit, target->x, target->y)) {
    return TRUE;
  }

  /* Check if we can nuke it now */
  ai_hunter_try_launch(pplayer, punit, target);

  /* If we are adjacent - RAMMING SPEED! */
  if (is_tiles_adjacent(punit->x, punit->y, target->x, target->y)) {
    ai_unit_attack(punit, target->x, target->y);
  }

  if (!find_unit_by_id(sanity_own)) {
    return TRUE;
  }
  if (!find_unit_by_id(sanity_target)) {
    UNIT_LOG(LOGLEVEL_HUNT, punit, "mission accomplished");
    ai_unit_new_role(punit, AIUNIT_NONE, -1, -1);
  }

  return TRUE;
}
? ai/aihunt.c
? ai/aihunt.h
Index: ai/Makefile.am
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/Makefile.am,v
retrieving revision 1.15
diff -u -r1.15 Makefile.am
--- ai/Makefile.am      2003/09/21 14:02:14     1.15
+++ ai/Makefile.am      2003/11/23 21:21:14
@@ -23,6 +23,8 @@
                aidata.h        \
                aihand.c        \
                aihand.h        \
+               aihunt.c        \
+               aihunt.h        \
                ailog.c         \
                ailog.h         \
                aitech.c        \
Index: ai/advmilitary.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/advmilitary.c,v
retrieving revision 1.158
diff -u -r1.158 advmilitary.c
--- ai/advmilitary.c    2003/11/23 15:49:26     1.158
+++ ai/advmilitary.c    2003/11/23 21:21:17
@@ -34,6 +34,7 @@
 #include "aidata.h"
 #include "aidiplomat.h"
 #include "aihand.h"
+#include "aihunt.h"
 #include "ailog.h"
 #include "aitools.h"
 #include "aiunit.h"
@@ -1320,6 +1321,9 @@
     kill_something_with(pplayer, pcity, virtualunit, choice);
     destroy_unit_virtual(virtualunit);
   }
+
+  /* Consider a hunter */
+  ai_hunter_choice(pplayer, pcity, choice);
 
   /* Consider veteran level enhancing buildings before non-urgent units */
   adjust_ai_unit_choice(pcity, choice);
Index: ai/aidata.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.c,v
retrieving revision 1.21
diff -u -r1.21 aidata.c
--- ai/aidata.c 2003/10/09 00:07:33     1.21
+++ ai/aidata.c 2003/11/23 21:21:17
@@ -303,6 +303,30 @@
   ai->pollution_priority = POLLUTION_WEIGHTING;
 
   ai_best_government(pplayer);
+
+  /*** Interception engine ***/
+
+  /* We are tracking a unit if punit->ai.cur_pos is not NULL. If we
+   * are not tracking, start tracking by setting cur_pos. If we are, 
+   * fill prev_pos with previous cur_pos. This way we get the 
+   * necessary coordinates to calculate a probably trajectory. */
+  players_iterate(aplayer) {
+    if (!aplayer->is_alive || aplayer == pplayer) {
+      continue;
+    }
+    unit_list_iterate(aplayer->units, punit) {
+      if (!punit->ai.cur_pos) {
+        /* Start tracking */
+        punit->ai.cur_pos = &punit->ai.cur_struct;
+        punit->ai.prev_pos = NULL;
+      } else {
+        punit->ai.prev_struct = punit->ai.cur_struct;
+        punit->ai.prev_pos = &punit->ai.prev_struct;
+      }
+      punit->ai.cur_pos->x = punit->x;
+      punit->ai.cur_pos->y = punit->y;
+    } unit_list_iterate_end;
+  } players_iterate_end;
 }
 
 /**************************************************************************
@@ -381,6 +405,7 @@
   } else if (ferry) {
     /* Make sure we delete punit from the list of potential passengers */
     ai_clear_ferry(punit);
+    ferry->ai.passenger = punit->id;
     punit->ai.ferryboat = ferry->id;
   }
 }
Index: ai/ailog.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/ailog.h,v
retrieving revision 1.6
diff -u -r1.6 ailog.h
--- ai/ailog.h  2003/09/28 19:52:17     1.6
+++ ai/ailog.h  2003/11/23 21:21:17
@@ -28,6 +28,7 @@
 #define LOGLEVEL_GOTO LOG_DEBUG
 #define LOGLEVEL_CITY LOG_DEBUG
 #define LOGLEVEL_BUILD LOG_DEBUG
+#define LOGLEVEL_HUNT LOG_DEBUG
 
 void PLAYER_LOG(int level, struct player *pplayer, struct ai_data *ai,
                 const char *msg, ...);
Index: ai/aitools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitools.c,v
retrieving revision 1.95
diff -u -r1.95 aitools.c
--- ai/aitools.c        2003/11/15 12:47:21     1.95
+++ ai/aitools.c        2003/11/23 21:21:18
@@ -422,13 +422,18 @@
 /**************************************************************************
   Ensure unit sanity by telling charge that we won't bodyguard it anymore,
   tell bodyguard it can roam free if our job is done, add and remove city 
-  spot reservation, and set destination.
+  spot reservation, and set destination. If we set a unit to hunter, also
+  reserve its target, and try to load it with cruise missiles or nukes
+  to bring along.
 **************************************************************************/
 void ai_unit_new_role(struct unit *punit, enum ai_unit_task task, int x, int y)
 {
   struct unit *charge = find_unit_by_id(punit->ai.charge);
   struct unit *bodyguard = find_unit_by_id(punit->ai.bodyguard);
 
+  UNIT_LOG(LOG_DEBUG, punit, "changing role from %d to %d",
+           punit->activity, task);
+
   /* Free our ferry.  Most likely it has been done already. */
   if (task == AIUNIT_NONE || task == AIUNIT_DEFEND_HOME) {
     ai_clear_ferry(punit);
@@ -443,6 +448,17 @@
     remove_city_from_minimap(goto_dest_x(punit), goto_dest_y(punit));
   }
 
+  if (punit->ai.ai_role == AIUNIT_HUNTER) {
+    /* Clear victim's hunted bit - we're no longer chasing. */
+    struct unit *target = find_unit_by_id(punit->ai.target);
+
+    if (target) {
+      target->ai.hunted &= ~(1 << unit_owner(punit)->player_no);
+      UNIT_LOG(LOGLEVEL_HUNT, target, "no longer hunted (new role %d, old %d)",
+               task, punit->ai.ai_role);
+    }
+  }
+
   if (charge && (charge->ai.bodyguard == punit->id)) {
     /* ensure we don't let the unit believe we bodyguard it */
     charge->ai.bodyguard = BODYGUARD_NONE;
@@ -467,6 +483,28 @@
   if (punit->ai.ai_role == AIUNIT_BUILD_CITY) {
     assert(is_normal_map_pos(x, y));
     add_city_to_minimap(x, y);
+  }
+  if (punit->ai.ai_role == AIUNIT_HUNTER) {
+    /* Set victim's hunted bit - the hunt is on! */
+    struct unit *target = find_unit_by_id(punit->ai.target);
+
+    assert(target != NULL);
+    target->ai.hunted |= (1 << unit_owner(punit)->player_no);
+    UNIT_LOG(LOGLEVEL_HUNT, target, "is being hunted");
+
+    /* Grab missiles lying around and bring them along */
+    if (unit_flag(punit, F_MISSILE_CARRIER)
+        || unit_flag(punit, F_CARRIER)) {
+      unit_list_iterate(map_get_tile(punit->x, punit->y)->units, missile) {
+        if (/*missile->ai.ai_role == AIUNIT_NONE
+            &&*/ unit_flag(missile, F_MISSILE)) {
+          UNIT_LOG(LOGLEVEL_HUNT, missile, "loaded on hunter");
+          ai_unit_new_role(missile, AIUNIT_ESCORT, target->x, target->y);
+          handle_unit_activity_request(missile, ACTIVITY_SENTRY);
+          missile->transported_by = punit->id;
+        }
+      } unit_list_iterate_end;
+    }
   }
 }
 
Index: ai/aiunit.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.c,v
retrieving revision 1.304
diff -u -r1.304 aiunit.c
--- ai/aiunit.c 2003/11/23 15:49:26     1.304
+++ ai/aiunit.c 2003/11/23 21:21:20
@@ -54,6 +54,7 @@
 #include "aidata.h"
 #include "aidiplomat.h"
 #include "aihand.h"
+#include "aihunt.h"
 #include "ailog.h"
 #include "aitools.h"
 
@@ -1397,6 +1398,19 @@
     return;
   }
 
+  /* Make unit a seahunter? */
+  if (punit->ai.ai_role == AIUNIT_HUNTER) {
+    return; /* Continue mission. */
+  }
+  if (ai_hunter_qualify(pplayer, punit)) {
+    UNIT_LOG(LOGLEVEL_HUNT, punit, "is qualified as hunter");
+    if (ai_hunter_findjob(pplayer, punit) > 0) {
+      UNIT_LOG(LOGLEVEL_HUNT, punit, "set as HUNTER");
+      ai_unit_new_role(punit, AIUNIT_HUNTER, -1, -1);
+      return;
+    }
+  }
+
 /* I'm not 100% sure this is the absolute best place for this... -- Syela */
   generate_warmap(map_get_city(punit->x, punit->y), punit);
 /* I need this in order to call unit_move_turns, here and in look_for_charge */
@@ -2325,7 +2339,7 @@
   }
 
   /* Do we have the passenger-in-charge on board? */
-  if (punit->ai.passenger > 0 
+  if (unit_list_size(&ptile->units) > 0
       && !unit_list_find(&ptile->units, punit->ai.passenger)) {
     UNIT_LOG(LOGLEVEL_FERRY, punit, "lost passenger-in-charge[%d], resetting",
              punit->ai.passenger);
@@ -2334,7 +2348,8 @@
 
   if (punit->ai.passenger <= 0) {
     struct unit *candidate = NULL;
-    
+
+    UNIT_LOG(LOGLEVEL_FERRY, punit, "trying to find a passenger-captain");   
     /* Try to select passanger-in-charge from among our passengers */
     unit_list_iterate(ptile->units, aunit) {
       if (aunit->ai.ferryboat != punit->id 
@@ -2490,7 +2505,8 @@
 }
 
 /**************************************************************************
-decides what to do with a military unit.
+  Decide what to do with a military unit. It will be managed once only.
+  It is up to the caller to try again if it has moves left.
 **************************************************************************/
 static void ai_manage_military(struct player *pplayer, struct unit *punit)
 {
@@ -2498,6 +2514,12 @@
 
   CHECK_UNIT(punit);
 
+  /* "Escorting" aircraft should not do anything. They are activated
+   * by their transport or charge. */
+  if (punit->ai.ai_role == AIUNIT_ESCORT && is_air_unit(punit)) {
+    return;
+  }
+
   if ((punit->activity == ACTIVITY_SENTRY
        || punit->activity == ACTIVITY_FORTIFIED)
       && ai_handicap(pplayer, H_AWAY)) {
@@ -2536,6 +2558,9 @@
   case AIUNIT_RECOVER:
     ai_manage_hitpoint_recovery(punit);
     break;
+  case AIUNIT_HUNTER:
+    ai_hunter_manage(pplayer, punit);
+    break;
   default:
     assert(FALSE);
   }
@@ -2618,8 +2643,6 @@
     return;
   }
 
-  ai_clear_ferry(punit);
-
   if ((unit_flag(punit, F_DIPLOMAT))
       || (unit_flag(punit, F_SPY))) {
     ai_manage_diplomat(pplayer, punit);
@@ -2635,10 +2658,13 @@
   } else if (unit_has_role(punit->type, L_BARBARIAN_LEADER)) {
     ai_manage_barbarian_leader(pplayer, punit);
     return;
-  } else if (get_transporter_capacity(punit) > 0) {
+  } else if (get_transporter_capacity(punit) > 0
+             && !unit_flag(punit, F_MISSILE_CARRIER)
+             && punit->ai.ai_role != AIUNIT_HUNTER) {
     ai_manage_ferryboat(pplayer, punit);
     return;
-  } else if (is_air_unit(punit)) {
+  } else if (is_air_unit(punit)
+             && punit->ai.ai_role != AIUNIT_ESCORT) {
     ai_manage_airunit(pplayer, punit);
     return;
   } else if (is_heli_unit(punit)) {
Index: ai/aiunit.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.h,v
retrieving revision 1.49
diff -u -r1.49 aiunit.h
--- ai/aiunit.h 2003/09/27 11:22:40     1.49
+++ ai/aiunit.h 2003/11/23 21:21:20
@@ -37,6 +37,9 @@
 #define HOSTILE_PLAYER(pplayer, ai, aplayer) \
   (pplayers_at_war(pplayer, aplayer)         \
    || ai->diplomacy.target == aplayer)
+#define UNITTYPE_COSTS(ut)                             \
+  (ut->pop_cost * 3 + ut->happy_cost + ut->shield_cost \
+   + ut->food_cost + ut->gold_cost)
 
 struct player;
 struct city;
Index: common/map.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/map.h,v
retrieving revision 1.164
diff -u -r1.164 map.h
--- common/map.h        2003/11/17 18:22:04     1.164
+++ common/map.h        2003/11/23 21:21:23
@@ -31,10 +31,7 @@
  * <0.
  */
 #define MOVE_COST_FOR_VALID_SEA_STEP   (-3)
-
-struct map_position {
-  int x,y;
-};
+#define MOVE_COST_FOR_VALID_AIR_STEP   (-3)
 
 struct goto_route {
   int first_index; /* first valid tile pos */
Index: common/unit.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/unit.c,v
retrieving revision 1.189
diff -u -r1.189 unit.c
--- common/unit.c       2003/11/23 15:49:26     1.189
+++ common/unit.c       2003/11/23 21:21:24
@@ -1472,6 +1472,10 @@
   if (is_barbarian(pplayer)) {
     punit->fuel = BARBARIAN_LIFE;
   }
+  punit->ai.cur_pos = NULL;
+  punit->ai.prev_pos = NULL;
+  punit->ai.target = 0;
+  punit->ai.hunted = 0;
   punit->ai.control = FALSE;
   punit->ai.ai_role = AIUNIT_NONE;
   punit->ai.ferryboat = 0;
Index: common/unit.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/unit.h,v
retrieving revision 1.103
diff -u -r1.103 unit.h
--- common/unit.h       2003/10/29 08:00:39     1.103
+++ common/unit.h       2003/11/23 21:21:24
@@ -21,6 +21,10 @@
 struct goto_route;
 struct tile;
 
+struct map_position {
+  int x,y;
+};
+
 #define BARBARIAN_LIFE    5
 
 enum unit_activity {
@@ -52,7 +56,7 @@
 enum ai_unit_task { AIUNIT_NONE, AIUNIT_AUTO_SETTLER, AIUNIT_BUILD_CITY,
                     AIUNIT_DEFEND_HOME, AIUNIT_ATTACK, AIUNIT_FORTIFY,
                     AIUNIT_RUNAWAY, AIUNIT_ESCORT, AIUNIT_EXPLORE,
-                    AIUNIT_PILLAGE, AIUNIT_RECOVER };
+                    AIUNIT_PILLAGE, AIUNIT_RECOVER, AIUNIT_HUNTER };
 
 enum goto_move_restriction {
   GOTO_MOVE_ANY,
@@ -103,6 +107,13 @@
   int passenger; /* the unit assigned to this ferryboat */
   int bodyguard; /* the unit bodyguarding us */
   int charge; /* the unit this unit is bodyguarding */
+
+  struct map_position prev_struct;
+  struct map_position cur_struct;
+  struct map_position *prev_pos;
+  struct map_position *cur_pos;
+  int target; /* target we hunt */
+  int hunted; /* if a player is hunting us, set by that player */
 };
 
 struct unit {
Index: common/aicore/pf_tools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/pf_tools.c,v
retrieving revision 1.9
diff -u -r1.9 pf_tools.c
--- common/aicore/pf_tools.c    2003/11/19 15:17:16     1.9
+++ common/aicore/pf_tools.c    2003/11/23 21:21:24
@@ -42,6 +42,15 @@
 }
 
 /*************************************************************
+  SINGLE_MOVE cost function for AIR_MOVING
+*************************************************************/
+static int single_airmove(int x, int y, enum direction8 dir,
+                         int x1, int y1, struct pf_parameter *param)
+{
+  return SINGLE_MOVE; /* simple, eh? */
+}
+
+/*************************************************************
   A cost function for SEA_MOVING.  Does not allow shore 
   bombardment.
 *************************************************************/
@@ -410,6 +419,9 @@
     } else {
       parameter->get_MC = seamove;
     }
+    break;
+  case AIR_MOVING:
+    parameter->get_MC = single_airmove;
     break;
   default:
     die("unknown move_type");

[Prev in Thread] Current Thread [Next in Thread]
  • [freeciv-ai] (PR#4742) AI hunting code v3, Per I. Mathisen <=