Complete.Org: Mailing Lists: Archives: freeciv-ai: January 2003:
[freeciv-ai] Patch Army
Home

[freeciv-ai] Patch Army

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: freeciv ai <freeciv-ai@xxxxxxxxxxx>
Subject: [freeciv-ai] Patch Army
From: Jordi Negrevernis i Font <jorneg@xxxxxxxxxxx>
Date: Thu, 09 Jan 2003 22:29:35 +0100


Well, i've been developing a patch for giving to the ai the capacity to make coordinate mass attacks against a city. It does handle a total of 8 attacking units, 4 defensive units and 4 naval units.

   The patch is still full of bugs but it works and does not crash.

   But, i have 3 questions:

   - How do i know if there is a city in the attack direction?
- Which are the wants for the units? While debugging i get want > 101 many times, before i evaluate army_advisor_choice!! I knew they were between 0 and 100!! - i need to calculate the mean of a couple of units. But i get a wrong result! Which is the mean of (77, 15) and (5,12) if the coordinate of the map are (80,50) ???!!!!

   If that thing is likely to go to CVS i can continue to develop it.

diff -Nur -b -Xfreeciv-cvs-Oct-25/diff_ignore freeciv-cvs-Nov-16/.cvsignore 
freeciv-cvs-army/.cvsignore
diff -Nur -b -Xfreeciv-cvs-Oct-25/diff_ignore freeciv-cvs-Nov-16/ai/Makefile.am 
freeciv-cvs-army/ai/Makefile.am
--- freeciv-cvs-Nov-16/ai/Makefile.am   Fri Nov  1 18:40:45 2002
+++ freeciv-cvs-army/ai/Makefile.am     Thu Jan  2 17:11:10 2003
@@ -27,6 +27,8 @@
                advtrade.h      \
                aiair.c         \
                aiair.h         \
+               aiarmy.c        \
+               aiarmy.h        \
                aicity.c        \
                aicity.h        \
                aidata.c        \
diff -Nur -b -Xfreeciv-cvs-Oct-25/diff_ignore 
freeciv-cvs-Nov-16/ai/advmilitary.c freeciv-cvs-army/ai/advmilitary.c
--- freeciv-cvs-Nov-16/ai/advmilitary.c Thu Nov 14 10:14:49 2002
+++ freeciv-cvs-army/ai/advmilitary.c   Thu Jan  9 14:33:07 2003
@@ -59,7 +59,7 @@
   desirability without regard to cost, unless costs are equal. This is
   very wrong. FIXME, use amortize on time to build.
 **************************************************************************/
-static Unit_Type_id ai_choose_attacker(struct city *pcity,
+Unit_Type_id ai_choose_attacker(struct city *pcity,
                                        enum unit_move_type which)
 {
   Unit_Type_id bestid = -1;
@@ -800,7 +800,7 @@
   (best_value, best_choice) is pre-filled with our current choice, we only
   consider units of the same move_type as best_choice
 **************************************************************************/
-static void process_attacker_want(struct player *pplayer, struct city *pcity,
+void process_attacker_want(struct player *pplayer, struct city *pcity,
                                   int value, Unit_Type_id victim_unit_type,
                                   bool veteran, int x, int y, bool unhap,
                                   int *best_value, int *best_choice,
diff -Nur -b -Xfreeciv-cvs-Oct-25/diff_ignore 
freeciv-cvs-Nov-16/ai/advmilitary.h freeciv-cvs-army/ai/advmilitary.h
--- freeciv-cvs-Nov-16/ai/advmilitary.h Sat Aug 31 04:09:38 2002
+++ freeciv-cvs-army/ai/advmilitary.h   Thu Jan  9 14:33:02 2003
@@ -32,5 +32,15 @@
 int assess_defense(struct city *pcity);
 int ai_unit_defence_desirability(Unit_Type_id i);
 int ai_unit_attack_desirability(Unit_Type_id i);
+
+void process_attacker_want(struct player *pplayer, struct city *pcity,
+                                  int value, Unit_Type_id victim_unit_type,
+                                  bool veteran, int x, int y, bool unhap,
+                                  int *best_value, int *best_choice,
+                                  int boatx, int boaty, int boatspeed,
+                                  int needferry);
+
+Unit_Type_id ai_choose_attacker(struct city *pcity,
+                                       enum unit_move_type which);

 #endif  /* FC__ADVMILITARY_H */
diff -Nur -b -Xfreeciv-cvs-Oct-25/diff_ignore freeciv-cvs-Nov-16/ai/aiarmy.c 
freeciv-cvs-army/ai/aiarmy.c
--- freeciv-cvs-Nov-16/ai/aiarmy.c      Thu Jan  1 01:00:00 1970
+++ freeciv-cvs-army/ai/aiarmy.c        Thu Jan  9 21:14:44 2003
@@ -0,0 +1,1383 @@
+/**********************************************************************
+ 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 <stdio.h>
+#include <string.h>
+
+#include "city.h"
+#include "gotohand.h"
+#include "log.h"
+#include "map.h"
+#include "maphand.h"
+#include "mem.h"
+#include "player.h"
+#include "support.h"
+#include "unit.h"
+#include "unittype.h"
+
+#include "advmilitary.h"
+#include "aiarmy.h"
+#include "aidata.h"
+#include "aihand.h"
+#include "aitools.h"
+#include "aiunit.h"
+
+#define LOG_ARMY LOG_NORMAL
+
+#define POPULATION_WEIGHT 150
+
+/**********************************************************************
+  TODO:
+  better bodyguard code
+  request building of units in the continent of the army
+    use 'process_attacker_want' and 'kill_something_with' to raise up the want 
for
+      new technology
+  disband army if units cannot attack the city
+  use musketer and riflemen as attacking units
+  modify function to find objectives
+  PROBLEM: how to take the mean of a sum of units?
+***********************************************************************/
+
+/**********************************************************************
+  Find safe city to atack another on the same continent.
+  An allied player's city is just as good as one of our own.
+***********************************************************************/
+static struct city *find_nearest_friendly_city(struct unit *punit, int dest_x, 
int dest_y)
+{
+  struct player *pplayer = unit_owner(punit);
+  int best = 12 * THRESHOLD + 1, meet, dest, dist;
+  struct city *presult = NULL;
+
+  generate_warmap(map_get_city(punit->x, punit->y), punit);
+  players_iterate(aplayer) {
+    if (pplayers_allied(pplayer,aplayer)) {
+      city_list_iterate(aplayer->cities, pcity) {
+        if ( map_get_continent(punit->x, punit->y) == 
map_get_continent(pcity->x, pcity->y) ) {
+          meet = warmap.cost[pcity->x][pcity->y];
+          dest = warmap.cost[dest_x][dest_y];
+          dist = dest - meet;
+          if (dist < best && dist > 0) {
+            presult = pcity;
+            best = dist;
+          }
+        }
+      } city_list_iterate_end;
+    }
+  } players_iterate_end;
+  if (best > 12 * THRESHOLD) return FALSE;
+  freelog(LOG_DEBUG, "Friendly city nearest to (%d,%d) is %s@(%d,%d) [%d]",
+               dest_x, dest_y,
+               presult->name,
+               presult->x, presult->y, best);
+  return presult;
+}
+
+/**********************************************************************
+  Function that initializes the armies.
+***********************************************************************/
+void ai_init_armies(struct player *pplayer)
+{ int ndx, i;
+  for (ndx=0; ndx < MAX_ARMIES_PER_PLAYER; ndx++) {
+    if ( pplayer->ai.army[ndx] == NULL ) {
+      pplayer->ai.army[ndx] = (struct ai_army *)fc_malloc(sizeof(struct 
ai_army));
+      for (i=0; i < MAX_ATTACK_PER_ARMY; i++) 
pplayer->ai.army[ndx]->attack_unit[i] = 0;
+      for (i=0; i < MAX_DEFENSE_PER_ARMY; i++) 
pplayer->ai.army[ndx]->defense_unit[i] = 0;
+      for (i=0; i < MAX_NAVAL_PER_ARMY; i++) 
pplayer->ai.army[ndx]->naval_unit[i] = 0;
+      for (i=0; i < MAX_TRANSPORT_PER_ARMY; i++) 
pplayer->ai.army[ndx]->transport[i] = 0;
+      pplayer->ai.army[ndx]->active = FALSE;
+      pplayer->ai.army[ndx]->accept_attack = TRUE;
+      pplayer->ai.army[ndx]->accept_defense = TRUE;
+      pplayer->ai.army[ndx]->accept_naval = FALSE;
+      pplayer->ai.army[ndx]->accept_transport = FALSE;
+      pplayer->ai.army[ndx]->objective_id = 0;
+      pplayer->ai.army[ndx]->continent = 0;
+      pplayer->ai.army[ndx]->army_x = 0; pplayer->ai.army[ndx]->army_y = 0;
+      pplayer->ai.army[ndx]->meeting_id = 0;
+      pplayer->ai.army[ndx]->obj_x = 0; pplayer->ai.army[ndx]->obj_y = 0;
+      pplayer->ai.army[ndx]->state = ARMY_STATE_IDLE;
+    }
+  }
+}
+
+/**********************************************************************
+  Function for compute the accept switches of an army.
+  Returns the number of units of an army.
+***********************************************************************/
+int recalculate_accept_units(struct ai_army *parmy)
+{ int ndx;
+  int num_attack = 0;
+  int num_defense = 0;
+  int num_naval = 0;
+  int num_transport = 0;
+
+  for (ndx = 0; ndx < MAX_ATTACK_PER_ARMY; ndx++)
+    if ( parmy->attack_unit[ndx] != 0 ) num_attack++;
+
+  for (ndx = 0; ndx < MAX_DEFENSE_PER_ARMY; ndx++)
+    if ( parmy->defense_unit[ndx] != 0 ) num_defense++;
+
+  for (ndx = 0; ndx < MAX_NAVAL_PER_ARMY; ndx++)
+    if ( parmy->naval_unit[ndx] != 0 ) num_naval++;
+
+  for (ndx = 0; ndx < MAX_TRANSPORT_PER_ARMY; ndx++)
+    if ( parmy->transport[ndx] != 0 ) num_transport++;
+
+  /* recalcutating accept modes of units and state */
+  if ( num_attack == MAX_ATTACK_PER_ARMY )
+    parmy->accept_attack = FALSE;
+
+  if ( (num_naval == MAX_DEFENSE_PER_ARMY)  && ( num_defense >= num_attack ) )
+    parmy->accept_naval = FALSE;
+
+  if ( (num_naval == MAX_NAVAL_PER_ARMY) || (num_attack == 0) )
+    parmy->accept_naval = FALSE;
+
+  if ( num_transport == MAX_TRANSPORT_PER_ARMY )
+    parmy->accept_transport = FALSE;
+
+  if ((num_attack + num_defense + num_naval + num_transport) > 1)
+    if ( parmy->state == ARMY_STATE_IDLE )
+      parmy->state = ARMY_STATE_FORMING;
+
+  return (num_attack + num_defense + num_naval + num_transport);
+
+}
+
+/**********************************************************************
+  Function for disbanding all units of an army.
+***********************************************************************/
+void disband_army(struct ai_army *parmy)
+{ struct unit *punit;
+  int ndx;
+
+  for (ndx = 0; ndx < MAX_ATTACK_PER_ARMY; ndx++)
+    if ( parmy->attack_unit[ndx] != 0 ) {
+      parmy->attack_unit[ndx] = 0;
+      punit = find_unit_by_id(parmy->attack_unit[ndx]);
+      if (!punit)
+        continue;
+      set_unit_activity(punit, ACTIVITY_IDLE);
+      punit->ai.ai_role = AIUNIT_NONE;
+    }
+
+  for (ndx = 0; ndx < MAX_DEFENSE_PER_ARMY; ndx++)
+    if ( parmy->defense_unit[ndx] != 0 ) {
+      parmy->defense_unit[ndx] = 0;
+      punit = find_unit_by_id(parmy->defense_unit[ndx]);
+      if (!punit)
+        continue;
+      set_unit_activity(punit, ACTIVITY_IDLE);
+      punit->ai.ai_role = AIUNIT_NONE;
+    }
+
+  for (ndx = 0; ndx < MAX_NAVAL_PER_ARMY; ndx++)
+    if ( parmy->naval_unit[ndx] != 0 ) {
+      parmy->naval_unit[ndx] = 0;
+      punit = find_unit_by_id(parmy->naval_unit[ndx]);
+      if (!punit)
+        continue;
+      set_unit_activity(punit, ACTIVITY_IDLE);
+      punit->ai.ai_role = AIUNIT_NONE;
+    }
+
+  for (ndx = 0; ndx < MAX_TRANSPORT_PER_ARMY; ndx++)
+    if ( parmy->transport[ndx] != 0 ) {
+      parmy->transport[ndx] = 0;
+      punit = find_unit_by_id(parmy->transport[ndx]);
+      if (!punit)
+        continue;
+      set_unit_activity(punit, ACTIVITY_IDLE);
+      punit->ai.ai_role = AIUNIT_NONE;
+    }
+
+  /* recalcutating accept modes of units and state */
+  parmy->accept_attack = TRUE;
+  parmy->accept_defense = TRUE;
+  parmy->accept_naval = FALSE;
+  parmy->accept_transport = FALSE;
+  parmy->state = ARMY_STATE_FORMING;
+
+}
+
+/**********************************************************************
+  Function for assigning continents to armies.
+  its assigns the seven continents with more cities to player's armies.
+***********************************************************************/
+void assign_army_continent(struct player *pplayer)
+{ struct {
+    int ncont;
+    int count;
+  } continent[50], x;   /* arbitrary value */
+  struct pcity *pcity;
+  int numcont, n, i, ndx;
+
+  /* init */
+  for (n = 0; n < 50; n++) {
+    continent[n].ncont = n;
+    continent[n].count = 0;
+  }
+
+  /* sum num cities on continent */
+  city_list_iterate(pplayer->cities, pcity) {
+    numcont = map_get_continent(pcity->x, pcity->y);
+    if (numcont >= 50) {
+      /* bad luck */
+    } else
+    { continent[numcont].count++;
+    }
+  } city_list_iterate_end;
+
+  /* order by num cities */
+  for (n=0; n<50; n++)
+    for (i=0; i<50; i++)
+      if (continent[i].count < continent[n].count) {
+        /* copy */
+        x.ncont = continent[n].ncont;
+        x.count = continent[n].count;
+        /* traslate 1 */
+        continent[n].ncont = continent[i].ncont;
+        continent[n].count = continent[i].count;
+        /* traslate 2 */
+        continent[i].ncont = x.ncont;
+        continent[i].count = x.count;
+      }
+
+  /* we assign continents to armies */
+  i = 0;
+  for (ndx = 0; ndx < MAX_ARMIES_PER_PLAYER; ndx++) {
+    if ( pplayer->ai.army[ndx]->state == ARMY_STATE_IDLE ||
+         ( pplayer->ai.army[ndx]->state == ARMY_STATE_FORMING &&
+           recalculate_accept_units(pplayer->ai.army[ndx]) == 0 ) ) {
+      pplayer->ai.army[ndx]->continent = continent[i].ncont;
+      i++;
+    }
+  }
+
+}
+
+/**********************************************************************
+  Function for activating armies based on number of cities.
+***********************************************************************/
+void activate_armies(struct player *pplayer)
+{ long num_cities;
+  int ndx;
+
+  /* find number of cities */
+  num_cities = city_list_size(&pplayer->cities);
+
+  /* activate armies */
+  for (ndx = 0; (ndx <= (num_cities / 5)) && (ndx < MAX_ARMIES_PER_PLAYER); 
ndx++) {
+    pplayer->ai.army[ndx]->active = TRUE;
+    (void)recalculate_accept_units(pplayer->ai.army[ndx]);
+  }
+
+  assign_army_continent(pplayer);
+
+}
+
+/**********************************************************************
+  Function for looking an id in the armies of a player.
+***********************************************************************/
+bool is_unit_in_an_army(struct player *pplayer, int id)
+{ int ndx, i, res = FALSE;
+
+  /* manage each army */
+  for (ndx = 0; ndx < MAX_ARMIES_PER_PLAYER; ndx++) {
+
+    if (!pplayer->ai.army[ndx]->active)
+      continue;
+
+    for (i = 0; i < MAX_ATTACK_PER_ARMY; i++)
+      if ( pplayer->ai.army[ndx]->attack_unit[i] == id ) {
+        res = TRUE;
+        break;
+      }
+
+    for (i = 0; i < MAX_DEFENSE_PER_ARMY; i++)
+      if ( pplayer->ai.army[ndx]->defense_unit[i] == id ) {
+        res = TRUE;
+        break;
+      }
+
+    for (i = 0; i < MAX_NAVAL_PER_ARMY; i++)
+      if ( pplayer->ai.army[ndx]->naval_unit[i] == id ) {
+        res = TRUE;
+        break;
+      }
+
+    for (i = 0; i < MAX_TRANSPORT_PER_ARMY; i++)
+      if ( pplayer->ai.army[ndx]->transport[i] == id ) {
+        res = TRUE;
+        break;
+      }
+
+  }
+
+  return res;
+
+}
+
+/**********************************************************************
+  Function for looking an id in the armies of a player.
+***********************************************************************/
+void assign_army_location(struct player *pplayer, struct ai_army *parmy)
+{ int i;
+  struct city *pcity;
+  struct unit *punit;
+  int total_x = 0;
+  int total_y = 0;
+  int num_units = 0, num_cities = 0;
+
+  if (!parmy->active) {
+    parmy->army_x = 0;
+    parmy->army_y = 0;
+    return;
+  }
+
+  if (recalculate_accept_units(parmy) > 0) {
+    /* mean of units */
+    for (i = 0; i < MAX_ATTACK_PER_ARMY; i++)
+      if (!parmy->attack_unit[i]) {
+        punit = find_unit_by_id(parmy->attack_unit[i]);
+        if (!punit)
+          continue;
+        total_x += punit->x;
+        total_y += punit->y;
+        num_units++;
+      }
+
+    for (i = 0; i < MAX_DEFENSE_PER_ARMY; i++)
+      if (!parmy->defense_unit[i]) {
+        punit = find_unit_by_id(parmy->defense_unit[i]);
+        if (!punit)
+          continue;
+        total_x += punit->x;
+        total_y += punit->y;
+        num_units++;
+      }
+
+    /* do not count naval units! */
+
+    /* final compute */
+    if (!num_units) {
+      parmy->army_x = 0;
+      parmy->army_y = 0;
+    } else {
+      parmy->army_x = total_x / num_units;
+      parmy->army_y = total_y / num_units;
+    }
+
+  } else {
+    /* mean of continent cities */
+    city_list_iterate(pplayer->cities, pcity) {
+      if (map_get_continent(pcity->x, pcity->y) == parmy->continent) {
+        total_x += pcity->x;
+        total_y += pcity->y;
+        num_cities++;
+      }
+    } city_list_iterate_end;
+
+    /* final compute */
+    if (!num_cities) {
+      parmy->army_x = 0;
+      parmy->army_y = 0;
+    } else {
+      parmy->army_x = total_x / num_cities;
+      parmy->army_y = total_y / num_cities;
+    }
+
+  }
+
+}
+
+/**********************************************************************
+  Function for assign or not a unit to an army.
+***********************************************************************/
+bool assign_unit_to_army(struct player *pplayer, struct ai_army *parmy, struct 
unit *punit)
+{ bool success = FALSE;
+  int ndx;
+
+  if ( is_ground_unit(punit) &&
+       ( parmy->continent == map_get_continent(punit->x, punit->y) || 
parmy->continent == 0 ) ) {
+    /* we try to assign to that army depending on type */
+    if ( (unit_has_role(punit->type, L_ATTACK_FAST) ||
+         unit_has_role(punit->type, L_ATTACK_STRONG)) && parmy->accept_attack) 
{
+      /* attacking units */
+      for (ndx = 0; ndx < MAX_ATTACK_PER_ARMY; ndx++) {
+        if ( parmy->attack_unit[ndx] == 0 ) {
+          parmy->attack_unit[ndx] = punit->id;
+          success = TRUE;
+          break;
+        }
+      }
+    } else if ( unit_has_role(punit->type, L_DEFEND_GOOD) && 
parmy->accept_defense) {
+      /* defending units */
+      for (ndx = 0; ndx < MAX_DEFENSE_PER_ARMY; ndx++) {
+        if ( parmy->defense_unit[ndx] == 0 ) {
+          parmy->defense_unit[ndx] = punit->id;
+          success = TRUE;
+          break;
+        }
+      }
+    }
+  }
+
+  if (is_water_unit(punit->type) && parmy->accept_naval) {
+    /* sea attacking units */
+    for (ndx = 0; ndx < MAX_NAVAL_PER_ARMY; ndx++) {
+      if ( parmy->naval_unit[ndx] == 0 ) {
+        parmy->naval_unit[ndx] = punit->id;
+        success = TRUE;
+        break;
+      }
+    }
+  }
+
+  (void)recalculate_accept_units(parmy);
+
+  return success;
+
+}
+
+/**********************************************************************
+  Function for know if a unit goes near a city.
+***********************************************************************/
+bool unit_goes_to_city(struct city *pcity, struct unit *punit)
+{ int x, y;
+
+  adjc_iterate(pcity->x, pcity->y, x, y) {
+    if ((punit->goto_dest_x == x) && (punit->goto_dest_y == y)) {
+      return TRUE;
+    }
+  } adjc_iterate_end;
+
+  return FALSE;
+
+}
+
+
+/**********************************************************************
+  Function for assigning units of ai to armies.
+***********************************************************************/
+void assign_units_to_army(struct player *pplayer, struct ai_army *parmy)
+{ struct unit *punit;
+  struct city *pcityobj = find_city_by_id(parmy->objective_id);
+
+  if (!parmy->active )
+    return;
+
+  if ( parmy->state == ARMY_STATE_FORMING || parmy->state == ARMY_STATE_IDLE ) 
{
+    /* it's ok */
+  } else {
+    return;
+  }
+
+  if (!pcityobj) {
+    return;
+  }
+
+  unit_list_iterate(pplayer->units, punit) {
+    if (is_military_unit(punit)) {
+      if ( punit->ai.ai_role == AIUNIT_NONE ||
+           punit->ai.ai_role == AIUNIT_PILLAGE ||
+           punit->ai.ai_role == AIUNIT_ARMY ) {
+        /* the unit is idle or not doing any damage, so take it! */
+        if ( is_unit_in_an_army(pplayer, punit->id) )
+          continue;
+
+        if ( stay_and_defend(punit) )
+          continue;
+
+        /* we try to assign the unit to the army */
+        if ( assign_unit_to_army(pplayer, parmy, punit) ) {
+          punit->ai.ai_role = AIUNIT_ARMY;
+        }
+
+      } else if ((punit->ai.ai_role == AIUNIT_ATTACK) && 
unit_goes_to_city(pcityobj, punit)) {
+
+        /* the unit is idle or not doing any damage, so take it! */
+        if ( is_unit_in_an_army(pplayer, punit->id) )
+          continue;
+
+        /* we try to assign the unit to the army */
+        if ( assign_unit_to_army(pplayer, parmy, punit) ) {
+          punit->ai.ai_role = AIUNIT_ARMY;
+        }
+
+      }
+
+    }
+  } unit_list_iterate_end;
+
+}
+
+/**********************************************************************
+  Returns attacking power of an army. does not count defenses
+  nor naval units.
+***********************************************************************/
+long army_power(struct ai_army *parmy)
+{ struct unit *punit;
+  int ndx, attack = 0;
+
+  for (ndx = 0; ndx < MAX_ATTACK_PER_ARMY; ndx++) {
+    if ( parmy->attack_unit[ndx] != 0 ) {
+      punit = find_unit_by_id(parmy->attack_unit[ndx]);
+      if (!punit)
+        continue;
+      attack += unit_belligerence_basic(punit);
+    }
+  }
+
+  return attack;
+}
+
+/**********************************************************************
+  Desire to attack that city, based on city trade, our attack force and
+  its defence capability.
+***********************************************************************/
+double army_desire_attack_city(struct ai_army *parmy, struct city *pcity)
+{ int ndx;
+  struct unit *punit;
+  int defense = 0;
+  int attack = 0;
+
+  defense = assess_defense(pcity);
+
+  for (ndx = 0; ndx < MAX_ATTACK_PER_ARMY; ndx++) {
+    if ( parmy->attack_unit[ndx] != 0 ) {
+      punit = find_unit_by_id(parmy->attack_unit[ndx]);
+      if (!punit)
+        continue;
+      attack += unit_belligerence_basic(punit);
+    }
+  }
+
+  if (attack == 0)
+    attack = 1;
+
+  if ( defense == 0 ) {
+    return 99999;
+  } else {
+   return ((pcity->trade_prod + (pcity->size * POPULATION_WEIGHT) * attack) / 
defense);
+  }
+
+}
+
+/**********************************************************************
+  Function for finding objectives to armies.
+***********************************************************************/
+long find_army_objective(struct player *pplayer, struct ai_army *parmy)
+{ struct city *acity;
+  struct player *aplayer;
+  int capital_continent = 0;
+  double bestchoice = 0, desire;
+  long dest = 0;
+
+  acity = find_palace(pplayer);
+  if ( acity ) {
+    capital_continent = map_get_continent(acity->x, acity->y);
+  }
+
+  /* first we try to find cities to attack in the continents we are in,
+    favour the one of our capital to secure it */
+  players_iterate(aplayer) {
+    if ( pplayers_at_war(pplayer, aplayer) ) {
+      city_list_iterate(aplayer->cities, acity) {
+        if ( map_get_continent(acity->x, acity->y) == parmy->continent ) {
+          desire = army_desire_attack_city(parmy, acity) * 20;
+          /* penalize distance */
+          desire /= real_map_distance(parmy->army_x, parmy->army_y, acity->x, 
acity->y);
+          /* is that occupied city mine? */
+          if (acity->original == pplayer->player_no) desire *= 1.50;
+          /* favour cities in capital continent */
+          if ( capital_continent == map_get_continent(acity->x, acity->y) ) 
desire *= 1.50;
+          /* TODO: favour enemy cities in our continents */
+          if ( desire > bestchoice && desire > 0 ) {
+            bestchoice = desire;
+            dest = acity->id;
+          }
+        }
+      } city_list_iterate_end;
+    }
+  } players_iterate_end;
+
+  return dest;
+}
+
+/**********************************************************************
+  Function for finding objectives for an army and set the attack
+  needed to conquer that city.
+***********************************************************************/
+void assign_objective_to_army(struct player *pplayer, struct ai_army *parmy)
+{ struct city *pcityobj;
+
+  /* we find an objective! */
+  parmy->objective_id = find_army_objective(pplayer, parmy);
+  pcityobj = find_city_by_id(parmy->objective_id);
+  if (!pcityobj ) {
+    /* fall back */
+    parmy->objective_id = 0;
+    parmy->attack_needed = 0;
+    parmy->meeting_id = 0;
+    parmy->obj_x = 0;
+    parmy->obj_y = 0;
+    return;
+  }
+
+  /* better that wait */
+  parmy->attack_needed = assess_defense(pcityobj) * 1.25;
+
+  parmy->accept_attack = TRUE;
+  parmy->accept_defense = TRUE;
+  if (is_at_coast(pcityobj->x, pcityobj->y)) {
+    parmy->accept_naval = TRUE;
+  }
+  parmy->accept_transport = FALSE;
+
+  (void)recalculate_accept_units(parmy);
+
+}
+
+/**********************************************************************
+  Function to group the atttacking units prior to attack a city.
+  Returns a TRUE if are all together and can proceded to attack.
+***********************************************************************/
+bool army_group_troups(struct player *pplayer, struct ai_army *parmy)
+{ struct unit *punit_first = NULL;
+  struct unit *punit = NULL;
+  struct unit_type *punittype;
+  struct city *pcityobj = NULL;         /* city victim */
+  struct city *pcitynear = NULL;        /* our city nearest */
+  int first_unit_id = 0;
+  int ndx;
+  int res;
+  int result = FALSE;
+
+  /* first unit is the leader unit, find it */
+  for (ndx = 0; ndx < MAX_ATTACK_PER_ARMY; ndx++) {
+    if ( parmy->attack_unit[ndx] != 0 ) {
+      first_unit_id = parmy->attack_unit[ndx];
+      punit_first = find_unit_by_id(first_unit_id);
+      break;
+    }
+  }
+  /* sanity check */
+  if ( !first_unit_id ) {
+    return FALSE;
+  }
+
+  switch (parmy->state)
+  {
+    case ARMY_STATE_ATTACKING:
+      /* if we are attacking doesn't need to enter here */
+      return TRUE;
+
+    case ARMY_STATE_FORMING:
+
+      /* we retrieve the objective */
+      pcityobj = find_city_by_id(parmy->objective_id);
+      if (!pcityobj ) {
+        /* fall back */
+        parmy->state = ARMY_STATE_FORMING;
+        parmy->objective_id = 0;
+        parmy->attack_needed = 0;
+        parmy->meeting_id = 0;
+        return FALSE;
+      }
+
+      /* we retrieve the nearest city of objective */
+      /* we find the nearest city of the objective to coordinate attack to a 
city.
+       for a unit is not necessary */
+      pcitynear = find_nearest_friendly_city(punit_first, pcityobj->x, 
pcityobj->y);
+      if (!pcitynear ) {
+        /* fall back */
+        parmy->state = ARMY_STATE_FORMING;
+        parmy->objective_id = 0;
+        parmy->attack_needed = 0;
+        parmy->meeting_id = 0;
+        return FALSE;
+      } else {
+        parmy->meeting_id = pcitynear->id;
+        if ( !pplayers_allied(city_owner(pcitynear), pplayer) ) {
+          /* fall back */
+          parmy->state = ARMY_STATE_FORMING;
+          parmy->objective_id = 0;
+          parmy->attack_needed = 0;
+          parmy->meeting_id = 0;
+          return FALSE;
+        }
+      }
+
+      if (army_power(parmy) > parmy->attack_needed ) {
+        /* we are grouping the forces if we have an objective that we can 
attack! */
+        parmy->state = ARMY_STATE_GROUPING;
+      }
+
+      break;
+
+    case ARMY_STATE_GROUPING:
+
+      /* we retrieve the objective */
+      pcityobj = find_city_by_id(parmy->objective_id);
+      if (!pcityobj ) {
+        /* fall back */
+        parmy->state = ARMY_STATE_FORMING;
+        parmy->objective_id = 0;
+        parmy->attack_needed = 0;
+        parmy->meeting_id = 0;
+        return FALSE;
+      }
+
+      /* we retrieve the nearest city of objective */
+      pcitynear = find_city_by_id(parmy->meeting_id);
+      if (!pcitynear ) {
+        /* fall back */
+        parmy->state = ARMY_STATE_FORMING;
+        parmy->objective_id = 0;
+        parmy->attack_needed = 0;
+        parmy->meeting_id = 0;
+        return FALSE;
+      } else {
+        if ( !pplayers_allied(city_owner(pcitynear), pplayer) ) {
+          /* fall back */
+          parmy->state = ARMY_STATE_FORMING;
+          parmy->objective_id = 0;
+          parmy->attack_needed = 0;
+          parmy->meeting_id = 0;
+          return FALSE;
+        }
+      }
+
+      break;
+
+    default:
+      return FALSE;
+  }
+
+  /* there is some unit far enough to wait? */
+  res = TRUE;
+  for (ndx = 0; ndx < MAX_ATTACK_PER_ARMY; ndx++) {
+    /* attacking units */
+    if ( parmy->attack_unit[ndx] != 0 ) {
+      punit = find_unit_by_id(parmy->attack_unit[ndx]);
+      if (!punit)
+        continue;
+
+      generate_warmap(map_get_city(punit->x, punit->y), punit);
+      if ( warmap.cost[punit_first->x][punit_first->y] > 0 )
+        res = FALSE;
+
+      /* go to nearest city! */
+      if ( (punit->x == pcitynear->x) && (punit->y == pcitynear->y) ) {
+        /* we are in the city, is the unit damaged?
+           if true recover hit points, if not idle */
+        punittype = get_unit_type(punit->type);
+        if (punit->hp < punittype->hp) {
+          res = FALSE;
+        }
+        /* does the unit have all movement points? */
+        if (punit->moves_left < punittype->move_rate) {
+          res = FALSE;
+        }
+      } else {
+        /* go there */
+        punit->goto_dest_x = pcitynear->x;
+        punit->goto_dest_y = pcitynear->y;
+        set_unit_activity(punit, ACTIVITY_GOTO);
+        result = do_unit_goto(punit, GOTO_MOVE_ANY, FALSE);
+        if (result == GR_DIED) {
+          /* We're dead. */
+          parmy->attack_unit[ndx] = 0;
+        }
+      }
+    }
+  }
+
+  for (ndx = 0; ndx < MAX_DEFENSE_PER_ARMY; ndx++) {
+    /* defending units */
+    if ( parmy->defense_unit[ndx] != 0 ) {
+      punit = find_unit_by_id(parmy->defense_unit[ndx]);
+      if (!punit)
+        continue;
+
+      generate_warmap(map_get_city(punit->x, punit->y), punit);
+      if ( warmap.cost[punit_first->x][punit_first->y] > 0 )
+        res = FALSE;
+
+      /* go to nearest city! */
+      if ( (punit->x == pcitynear->x) && (punit->y == pcitynear->y) ) {
+        /* we are in the city, is the unit damaged?
+           if true recover hit points, if not idle */
+        punittype = get_unit_type(punit->type);
+        if (punit->hp < punittype->hp) {
+          res = FALSE;
+        }
+        /* does the unit have all movement points? */
+        if (punit->moves_left < punittype->move_rate) {
+          res = FALSE;
+        }
+      } else {
+        /* go there */
+        punit->goto_dest_x = pcitynear->x;
+        punit->goto_dest_y = pcitynear->y;
+        set_unit_activity(punit, ACTIVITY_GOTO);
+        result = do_unit_goto(punit, GOTO_MOVE_ANY, FALSE);
+        if (result == GR_DIED) {
+          /* We're dead. */
+          parmy->defense_unit[ndx] = 0;
+        }
+      }
+    }
+
+  }
+
+  return res;
+}
+
+/**********************************************************************
+  Function to find a unit of the army which needs protection.
+***********************************************************************/
+bool find_attacking_unit_whitout_defense(struct ai_army *parmy, struct unit 
*aunit, int *dest_x, int *dest_y)
+{ int ndx;
+
+  /* for each unit attack */
+  for (ndx = 0; ndx < MAX_ATTACK_PER_ARMY; ndx++) {
+    if ( parmy->attack_unit[ndx] != 0 ) {
+
+      aunit = find_unit_by_id(parmy->attack_unit[ndx]);
+
+      if (!aunit) {
+        /* dead */
+        parmy->attack_unit[ndx] = 0;
+        continue;
+      }
+
+      *dest_x = aunit->x;
+      *dest_y = aunit->y;
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+
+}
+
+/**********************************************************************
+  Function to attack a city and any unit near the army.
+***********************************************************************/
+void army_attack_victim(struct ai_army *parmy)
+{ struct unit *aunit;
+  struct city *acity = find_city_by_id(parmy->objective_id);
+  int ndx, result;
+  int dest_x, dest_y;
+
+  /* we are grouped, attack */
+  parmy->state = ARMY_STATE_ATTACKING;
+
+  /* sanity check */
+  if (!acity) {
+    parmy->state = ARMY_STATE_FORMING;
+    return;
+  }
+
+  dest_x = acity->x; dest_y = acity->y;
+
+  /* for each attack unit */
+  for (ndx = 0; ndx < MAX_ATTACK_PER_ARMY; ndx++) {
+    if ( parmy->attack_unit[ndx] != 0 ) {
+
+      aunit = find_unit_by_id(parmy->attack_unit[ndx]);
+      if (!aunit) {
+        /* dead */
+        parmy->attack_unit[ndx] = 0;
+        continue;
+      }
+
+      /* first attack units side by side */
+      if (!ai_military_rampage(aunit, 2)) {
+        /* dead */
+        parmy->attack_unit[ndx] = 0;
+        continue;
+      }
+
+      /* go to attack the city! */
+      aunit->goto_dest_x = dest_x;
+      aunit->goto_dest_y = dest_y;
+      set_unit_activity(aunit, ACTIVITY_GOTO);
+      result = do_unit_goto(aunit, GOTO_MOVE_ANY, FALSE);
+      if (result == GR_DIED) {
+        /* We're dead. */
+        parmy->attack_unit[ndx] = 0;
+        continue;
+      }
+
+      /* first attack units side by side */
+      if (!ai_military_rampage(aunit, 2)) {
+        /* dead */
+        parmy->attack_unit[ndx] = 0;
+        continue;
+      }
+
+    }
+  }
+
+  /* for each naval attack unit */
+  for (ndx = 0; ndx < MAX_ATTACK_PER_ARMY; ndx++) {
+    if ( parmy->attack_unit[ndx] != 0 ) {
+
+      aunit = find_unit_by_id(parmy->naval_unit[ndx]);
+      if (!aunit) {
+        /* dead */
+        parmy->naval_unit[ndx] = 0;
+        continue;
+      }
+
+      /* first attack units side by side */
+      if (!ai_military_rampage(aunit, 2)) {
+        /* dead */
+        parmy->attack_unit[ndx] = 0;
+        continue;
+      }
+
+      /* go to attack the city! */
+      aunit->goto_dest_x = dest_x;
+      aunit->goto_dest_y = dest_y;
+      set_unit_activity(aunit, ACTIVITY_GOTO);
+      result = do_unit_goto(aunit, GOTO_MOVE_ANY, FALSE);
+      if (result == GR_DIED) {
+        /* We're dead. */
+        parmy->naval_unit[ndx] = 0;
+        continue;
+      }
+
+      /* first attack units side by side */
+      if (!ai_military_rampage(aunit, 2)) {
+        /* dead */
+        parmy->naval_unit[ndx] = 0;
+        continue;
+      }
+
+    }
+  }
+}
+
+/**********************************************************************
+  Function to protect all the attacking units of an army.
+***********************************************************************/
+void army_protect_attacking_units(struct ai_army *parmy)
+{ struct unit *aunit;
+  int ndx, result;
+  int dest_x, dest_y;
+
+  /* for each defense unit */
+  for (ndx = 0; ndx < MAX_DEFENSE_PER_ARMY; ndx++) {
+    if ( parmy->defense_unit[ndx] != 0 ) {
+
+      aunit = find_unit_by_id(parmy->defense_unit[ndx]);
+      if (!aunit) {
+        /* dead */
+        parmy->defense_unit[ndx] = 0;
+        continue;
+      }
+
+      /*find an attacking unit who has attacked and needs a bodyguard! */
+      result = find_attacking_unit_whitout_defense(parmy, aunit, &dest_x, 
&dest_y);
+      if ( result == FALSE )
+        continue;
+
+      /* go to attack the city! */
+      aunit->goto_dest_x = dest_x;
+      aunit->goto_dest_y = dest_y;
+      set_unit_activity(aunit, ACTIVITY_GOTO);
+      result = do_unit_goto(aunit, GOTO_MOVE_ANY, FALSE);
+      if (result == GR_DIED) {
+        /* We're dead. */
+        parmy->defense_unit[ndx] = 0;
+        continue;
+      }
+
+      /* is some unprotected city around? */
+      if (!ai_military_rampage(aunit, 99999)) {
+        /* dead */
+        parmy->defense_unit[ndx] = 0;
+        continue;
+      }
+
+      /* try to protect an own attacking unit */
+      set_unit_activity(aunit, ACTIVITY_FORTIFYING);
+    }
+  }
+
+}
+
+/**********************************************************************
+  Function to secure the conquered city. Just wait all units to enter
+  in the city and recover hitpoints.
+***********************************************************************/
+void army_secure_city_conquered(struct ai_army *parmy)
+{ struct unit *aunit;
+  struct city *acity = find_city_by_id(parmy->objective_id);
+  int dest_x, dest_y, ndx, result;
+
+  /* sanity check */
+  if (!acity) {
+    /* the city doesn't exist, we wipe out it! */
+    return;
+  }
+
+  dest_x = acity->x; dest_y = acity->y;
+
+  /* for each attack unit */
+  for (ndx = 0; ndx < MAX_ATTACK_PER_ARMY; ndx++) {
+    if ( parmy->attack_unit[ndx] != 0 ) {
+
+      aunit = find_unit_by_id(parmy->attack_unit[ndx]);
+      if (!aunit) {
+        /* dead */
+        parmy->attack_unit[ndx] = 0;
+        continue;
+      }
+
+      /* first attack units side by side */
+      if (!ai_military_rampage(aunit, 2)) {
+        /* dead */
+        parmy->attack_unit[ndx] = 0;
+        continue;
+      }
+
+      /* enter in the city! */
+      aunit->goto_dest_x = dest_x;
+      aunit->goto_dest_y = dest_y;
+      set_unit_activity(aunit, ACTIVITY_GOTO);
+      result = do_unit_goto(aunit, GOTO_MOVE_ANY, FALSE);
+      if (result == GR_DIED) {
+        /* We're dead. */
+        parmy->attack_unit[ndx] = 0;
+        continue;
+      }
+
+      /* first attack units side by side */
+      if (!ai_military_rampage(aunit, 2)) {
+        /* dead */
+        parmy->attack_unit[ndx] = 0;
+        continue;
+      }
+
+    }
+  }
+
+  /* for each defense unit */
+  for (ndx = 0; ndx < MAX_DEFENSE_PER_ARMY / 2; ndx++) {
+    if ( parmy->defense_unit[ndx] != 0 ) {
+
+      aunit = find_unit_by_id(parmy->defense_unit[ndx]);
+      if (!aunit) {
+        /* dead */
+        parmy->defense_unit[ndx] = 0;
+        continue;
+      }
+
+      /* enter in the city! */
+      aunit->goto_dest_x = dest_x;
+      aunit->goto_dest_y = dest_y;
+      set_unit_activity(aunit, ACTIVITY_GOTO);
+      result = do_unit_goto(aunit, GOTO_MOVE_ANY, FALSE);
+      if (result == GR_DIED) {
+        /* We're dead. */
+        parmy->defense_unit[ndx] = 0;
+        continue;
+      }
+
+      /* try to protect an own attacking unit */
+      set_unit_activity(aunit, ACTIVITY_FORTIFYING);
+    }
+  }
+}
+
+/**********************************************************************
+  Did the attack function finish & conquerer or killed
+  the enemy position?
+***********************************************************************/
+bool did_army_finish_attack(struct player *pplayer, struct ai_army *parmy)
+{ struct city *pcityobj;
+
+  if ( parmy->state == ARMY_STATE_ATTACKING ) {
+    /* conquering cities */
+    pcityobj = find_city_by_id(parmy->objective_id);
+    if (!pcityobj) {
+      /* we erase the city */
+      return TRUE;
+    }
+    if (city_owner(pcityobj) == pplayer) {
+      /* we conquer the city */
+      return TRUE;
+    }
+    return FALSE;
+  } else {
+    return FALSE;
+  }
+
+}
+
+/**********************************************************************
+  Function that disbands an army if we have allocated units without
+  an objective.
+***********************************************************************/
+void eventually_disband_army(struct ai_army *parmy)
+{
+
+  if ( recalculate_accept_units(parmy) > 0 ) {
+    /* we have troups allocated */
+    if ( parmy->objective_id == 0 ) {
+      /* disband army */
+      disband_army(parmy);
+    }
+  }
+}
+
+/**********************************************************************
+  Function that manages an army.
+***********************************************************************/
+void ai_manage_army(struct player *pplayer, struct ai_army *parmy)
+{
+  if ( !parmy->active ) {
+    return;
+  }
+
+  /* we can't group if we don't have enough units!!! */
+  if ( recalculate_accept_units(parmy) <= 1 )
+    return;
+
+  /* we have a victim, prepare for coordinated attack */
+  if ( army_group_troups(pplayer, parmy) ) {
+    army_attack_victim(parmy);
+    army_protect_attacking_units(parmy);
+    if (did_army_finish_attack(pplayer, parmy)) {
+      army_secure_city_conquered(parmy);
+      parmy->objective_id = 0;
+      parmy->meeting_id = 0;
+      parmy->obj_x = 0; parmy->obj_y = 0;
+      parmy->state = ARMY_STATE_FORMING;
+    }
+  } else {
+    /* we are not attacking, it means that we are don't have an army,
+       or we don't have an objective, or we are yet not grouped */
+      /* we have units in the army, do we have an objective?
+         if not disband the army!! */
+    eventually_disband_army(parmy);
+  }
+
+}
+
+/**********************************************************************
+  Makes a description of an army.
+***********************************************************************/
+char *desc_army(struct player *pplayer, struct ai_army *parmy, int ndxarm, 
char *buf)
+{ struct city *pcityobj = find_city_by_id(parmy->objective_id);
+  struct city *pcitymeet = find_city_by_id(parmy->meeting_id);
+  int ndx;
+  int num_attack = 0;
+  int num_defense = 0;
+  int num_naval = 0;
+  int num_transport = 0;
+  char army_status[10];
+
+  for (ndx = 0; ndx < MAX_ATTACK_PER_ARMY; ndx++)
+    if ( parmy->attack_unit[ndx] != 0 ) num_attack++;
+
+  for (ndx = 0; ndx < MAX_DEFENSE_PER_ARMY; ndx++)
+    if ( parmy->defense_unit[ndx] != 0 ) num_defense++;
+
+  for (ndx = 0; ndx < MAX_NAVAL_PER_ARMY; ndx++)
+    if ( parmy->naval_unit[ndx] != 0 ) num_naval++;
+
+  for (ndx = 0; ndx < MAX_TRANSPORT_PER_ARMY; ndx++)
+    if ( parmy->transport[ndx] != 0 ) num_transport++;
+
+  switch(parmy->state)
+  {
+    case ARMY_STATE_IDLE: mystrlcpy(army_status, "Idle", sizeof(army_status)); 
break;
+    case ARMY_STATE_FORMING: mystrlcpy(army_status, "Forming", 
sizeof(army_status)); break;
+    case ARMY_STATE_GROUPING: mystrlcpy(army_status, "Grouping", 
sizeof(army_status)); break;
+    case ARMY_STATE_ATTACKING: mystrlcpy(army_status, "Attacking", 
sizeof(army_status)); break;
+    default: mystrlcpy(army_status, "Uknown", sizeof(army_status)); break;
+  }
+
+
+  sprintf(buf, "%s's army %d, %s on cont(%d),at(%d),de(%d),na(%d),tr(%d)",
+                pplayer->name, (ndxarm + 1),
+                army_status, parmy->continent,
+                num_attack, num_defense, num_naval, num_transport);
+
+  if (pcityobj) {
+    sprintf(buf+strlen(buf), ", Dest->%s(%d,%d)", pcityobj->name, pcityobj->x, 
pcityobj->y);
+  }
+  if (pcitymeet) {
+    sprintf(buf+strlen(buf), ", Meet->%s(%d,%d)", pcitymeet->name, 
pcitymeet->x, pcitymeet->y);
+  }
+
+  return buf;
+}
+
+/**********************************************************************
+  Function to check for dead units in the army.
+***********************************************************************/
+void army_check_units(struct ai_army *parmy)
+{ int ndx;
+  struct unit *aunit;
+  int num_attack = 0;
+  int num_defense = 0;
+  int num_naval = 0;
+  int num_transport = 0;
+
+  /* for each attack unit */
+  for (ndx = 0; ndx < MAX_ATTACK_PER_ARMY; ndx++) {
+    if ( parmy->attack_unit[ndx] != 0 ) {
+
+      aunit = find_unit_by_id(parmy->attack_unit[ndx]);
+
+      if (!aunit)
+        parmy->attack_unit[ndx] = 0;
+
+      num_attack++;
+    }
+  }
+
+  /* for each defense unit */
+  for (ndx = 0; ndx < MAX_DEFENSE_PER_ARMY; ndx++) {
+    if ( parmy->defense_unit[ndx] != 0 ) {
+
+      aunit = find_unit_by_id(parmy->defense_unit[ndx]);
+
+      if (!aunit)
+        parmy->defense_unit[ndx] = 0;
+
+      num_defense++;
+    }
+  }
+
+  /* if the army was wiped out, reset state */
+  if ( num_attack + num_defense + num_naval + num_transport == 0 )
+    parmy->state = ARMY_STATE_IDLE;
+
+}
+
+/**********************************************************************
+  Main function for managing armies. called from ai_data.c
+***********************************************************************/
+void ai_manage_armies(struct player *pplayer)
+{ int ndx;
+  char desc[256];
+
+  if (!ai_handicap(pplayer, H_EXPERIMENTAL))
+    return;
+
+  /* first inicialization */
+  ai_init_armies(pplayer);
+
+  /* activate armies & assign units to armies to make war */
+  activate_armies(pplayer);
+
+  /* manage each army */
+  for (ndx = 0; ndx < MAX_ARMIES_PER_PLAYER; ndx++) {
+    if ( pplayer->ai.army[ndx]->active ) {
+      assign_army_location(pplayer, pplayer->ai.army[ndx]);
+      assign_objective_to_army(pplayer, pplayer->ai.army[ndx]);
+      army_check_units(pplayer->ai.army[ndx]);
+      assign_units_to_army(pplayer, pplayer->ai.army[ndx]);
+      ai_manage_army(pplayer, pplayer->ai.army[ndx]);
+      freelog(LOG_ARMY, "%s", desc_army(pplayer, pplayer->ai.army[ndx], ndx, 
desc));
+    }
+  }
+
+}
+
+/**********************************************************************
+  Main function for managing armies. called from ai_data.c
+***********************************************************************/
+void army_advisor_choose_build(struct player *pplayer, struct city *pcity,
+                                  struct ai_choice *choice)
+{
+  Unit_Type_id unit_type;
+  struct city *pcityobj;
+  long benefit = 0, ndx;
+  struct ai_choice bestchoice;
+
+  if (!ai_handicap(pplayer, H_EXPERIMENTAL))
+    return;
+
+  init_choice(&bestchoice);
+
+  for(ndx = 0; ndx < MAX_ARMIES_PER_PLAYER; ndx++) {
+    if (map_get_continent(pcity->x, pcity->y) == 
pplayer->ai.army[ndx]->continent) {
+
+      /* has objective? */
+      if (!pplayer->ai.army[ndx]->objective_id) {
+        continue;
+      }
+
+      /* exists the city? */
+      pcityobj = find_city_by_id(pplayer->ai.army[ndx]->objective_id);
+      if (!pcityobj) {
+        continue;
+      }
+
+      /* the army is forming? */
+      if (pplayer->ai.army[ndx]->state == ARMY_STATE_IDLE ||
+          pplayer->ai.army[ndx]->state == ARMY_STATE_FORMING) {
+        /* ok build units */
+      } else {
+        continue;
+      }
+
+      /* Consider a land attacker */
+      unit_type = 0;
+      if (pplayer->ai.army[ndx]->accept_attack)
+        unit_type = ai_choose_attacker(pcity, LAND_MOVING);
+      if (unit_type >= 0) {
+        benefit = pcity->trade_prod * 0.10 / 
get_unit_type(unit_type)->build_cost;
+        process_attacker_want(pplayer, pcity, benefit, unit_type, TRUE, 
pcityobj->x, pcityobj->y,
+                             FALSE, &bestchoice.want, &bestchoice.type, 0, 0, 
0, FALSE);
+      }
+
+      /* Consider a sea attacker */
+      unit_type = 0;
+      if (pplayer->ai.army[ndx]->accept_naval)
+        unit_type = ai_choose_attacker(pcity, SEA_MOVING);
+      if (unit_type >= 0) {
+        benefit = pcity->trade_prod * 0.10 / 
get_unit_type(unit_type)->build_cost;
+        process_attacker_want(pplayer, pcity, benefit, unit_type, TRUE, 
pcityobj->x, pcityobj->y,
+                             FALSE, &bestchoice.want, &bestchoice.type, 0, 0, 
0, FALSE);
+      }
+
+    }
+  }
+
+  /* check to not waste money! */
+  bestchoice.type = CT_ATTACKER;
+
+  /* if better copy */
+  copy_if_better_choice(&bestchoice, choice);
+
+  /* output result */
+  if (choice->want > 0)
+    if (is_unit_choice_type(choice->type)) {
+      freelog(LOG_ARMY, "%s: military advisor choice: %s (want %d)",
+              pcity->name, unit_types[choice->choice].name, bestchoice.want);
+  }
+
+}
diff -Nur -b -Xfreeciv-cvs-Oct-25/diff_ignore freeciv-cvs-Nov-16/ai/aiarmy.h 
freeciv-cvs-army/ai/aiarmy.h
--- freeciv-cvs-Nov-16/ai/aiarmy.h      Thu Jan  1 01:00:00 1970
+++ freeciv-cvs-army/ai/aiarmy.h        Thu Jan  9 17:46:53 2003
@@ -0,0 +1,51 @@
+/**********************************************************************
+ 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__AIARMY_H
+#define FC__AIARMY_H
+
+#include "shared.h"            /* bool type */
+
+/* defined in player.h
+#define MAX_ARMIES_PER_PLAYER 6
+*/
+#define MAX_ATTACK_PER_ARMY 8
+#define MAX_DEFENSE_PER_ARMY 4
+#define MAX_NAVAL_PER_ARMY 4
+#define MAX_TRANSPORT_PER_ARMY 6
+
+#define ARMY_STATE_IDLE 0
+#define ARMY_STATE_FORMING 1
+#define ARMY_STATE_GROUPING 2
+#define ARMY_STATE_ATTACKING 4
+
+struct ai_army {
+  long attack_unit[MAX_ATTACK_PER_ARMY];
+  long defense_unit[MAX_DEFENSE_PER_ARMY];
+  long naval_unit[MAX_NAVAL_PER_ARMY];
+  long transport[MAX_TRANSPORT_PER_ARMY];
+  bool active, accept_attack, accept_defense, accept_naval, accept_transport;
+  int objective_id;
+  int obj_x, obj_y;
+  int meeting_id;
+  int attack_needed;
+  int state;
+  int continent;
+  int army_x, army_y;
+};
+
+void ai_manage_armies(struct player *pplayer);
+void ai_init_armies(struct player *pplayer);
+void army_advisor_choose_build(struct player *pplayer, struct city *pcity,
+                                  struct ai_choice *choice);
+
+#endif /* FC__AIARMY_H */
diff -Nur -b -Xfreeciv-cvs-Oct-25/diff_ignore freeciv-cvs-Nov-16/ai/aicity.c 
freeciv-cvs-army/ai/aicity.c
--- freeciv-cvs-Nov-16/ai/aicity.c      Thu Nov 14 10:14:49 2002
+++ freeciv-cvs-army/ai/aicity.c        Thu Jan  9 14:42:18 2003
@@ -46,6 +46,7 @@
 #include "advisland.h"
 #include "advleader.h"
 #include "advmilitary.h"
+#include "aiarmy.h"
 #include "aihand.h"
 #include "aitools.h"
 #include "aidata.h"
@@ -496,6 +497,7 @@

   city_list_iterate(pplayer->cities, pcity)
     military_advisor_choose_build(pplayer, pcity, &pcity->ai.choice);
+    army_advisor_choose_build(pplayer, pcity, &pcity->ai.choice);
 /* note that m_a_c_b mungs the seamap, but we don't care */
     establish_city_distances(pplayer, pcity); /* in advmilitary for warmap */
 /* e_c_d doesn't even look at the seamap */
diff -Nur -b -Xfreeciv-cvs-Oct-25/diff_ignore freeciv-cvs-Nov-16/ai/aidata.h 
freeciv-cvs-army/ai/aidata.h
--- freeciv-cvs-Nov-16/ai/aidata.h      Fri Nov 15 23:15:01 2002
+++ freeciv-cvs-army/ai/aidata.h        Sat Jan  4 01:30:05 2003
@@ -15,6 +15,8 @@

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

+#include "aiarmy.h"
+
 struct player;
 struct unit;

@@ -50,6 +52,7 @@
   } stats;

   int num_continents; /* last time we updated our continent data */
+
 };

 void ai_data_turn_init(struct player *pplayer);
diff -Nur -b -Xfreeciv-cvs-Oct-25/diff_ignore freeciv-cvs-Nov-16/ai/aihand.c 
freeciv-cvs-army/ai/aihand.c
--- freeciv-cvs-Nov-16/ai/aihand.c      Thu Nov 14 10:14:50 2002
+++ freeciv-cvs-army/ai/aihand.c        Sat Jan  4 01:45:42 2003
@@ -34,6 +34,7 @@
 #include "spacerace.h"
 #include "unithand.h"

+#include "aiarmy.h"
 #include "aicity.h"
 #include "aitech.h"
 #include "aitools.h"
@@ -363,6 +364,9 @@
 **************************************************************************/
 void ai_do_first_activities(struct player *pplayer)
 {
+  freelog(LOG_DEBUG, "Managing %s's armies.", pplayer->name);
+  ai_manage_armies(pplayer);
+  freelog(LOG_DEBUG, "Managing %s's units.", pplayer->name);
   ai_manage_units(pplayer); /* STOP.  Everything else is at end of turn. */
 }

diff -Nur -b -Xfreeciv-cvs-Oct-25/diff_ignore freeciv-cvs-Nov-16/ai/aiunit.c 
freeciv-cvs-army/ai/aiunit.c
--- freeciv-cvs-Nov-16/ai/aiunit.c      Thu Nov 14 10:14:50 2002
+++ freeciv-cvs-army/ai/aiunit.c        Sun Jan  5 01:26:52 2003
@@ -63,7 +63,7 @@
 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 struct unit *ai_military_rampage(struct unit *punit, int threshold);
+struct unit *ai_military_rampage(struct unit *punit, int threshold);

 static int unit_move_turns(struct unit *punit, int x, int y);
 static bool unit_can_defend(Unit_Type_id type);
@@ -634,7 +634,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;
@@ -676,7 +676,7 @@
 /**********************************************************************
   ...
 ***********************************************************************/
-static int unit_belligerence_primitive(struct unit *punit)
+int unit_belligerence_primitive(struct unit *punit)
 {
   return (base_unit_belligerence_primitive(punit->type, punit->veteran,
                                           punit->moves_left, punit->hp));
@@ -2268,6 +2278,11 @@
 {
   int id = punit->id;

+  if (punit->ai.ai_role == AIUNIT_ARMY ) {
+    /* all done */
+    return;
+  }
+
   if (punit->activity != ACTIVITY_IDLE)
     handle_unit_activity_request(punit, ACTIVITY_IDLE);

@@ -2300,6 +2315,9 @@
   case AIUNIT_EXPLORE:
     (void) ai_manage_explorer(punit);
     break;
+  case AIUNIT_ARMY:
+    /* don't do nothing, it's done by aiarmy.c */
+    break;
   default:
     assert(FALSE);
   }
diff -Nur -b -Xfreeciv-cvs-Oct-25/diff_ignore freeciv-cvs-Nov-16/ai/aiunit.h 
freeciv-cvs-army/ai/aiunit.h
--- freeciv-cvs-Nov-16/ai/aiunit.h      Thu Mar 21 21:57:29 2002
+++ freeciv-cvs-army/ai/aiunit.h        Sat Jan  4 16:19:07 2003
@@ -44,6 +44,7 @@
 int find_beachhead(struct unit *punit, int dest_x, int dest_y, int *x, int *y);

 int build_cost_balanced(Unit_Type_id type);
+bool stay_and_defend(struct unit *punit);
 int base_unit_belligerence_primitive(Unit_Type_id type, bool veteran,
                                     int moves_left, int hp);
 int unit_belligerence_basic(struct unit *punit);
@@ -56,6 +57,7 @@
 int unit_vulnerability(struct unit *punit, struct unit *pdef);
 int kill_desire(int benefit, int attack, int loss, int vuln, int attack_count);
 int military_amortize(int value, int delay, int build_cost);
+struct unit *ai_military_rampage(struct unit *punit, int threshold);

 bool is_on_unit_upgrade_path(Unit_Type_id test, Unit_Type_id base);

diff -Nur -b -Xfreeciv-cvs-Oct-25/diff_ignore 
freeciv-cvs-Nov-16/common/player.c freeciv-cvs-army/common/player.c
--- freeciv-cvs-Nov-16/common/player.c  Thu Nov 14 10:15:02 2002
+++ freeciv-cvs-army/common/player.c    Sat Jan  4 01:35:49 2003
@@ -94,6 +94,11 @@
   plr->ai.fuzzy = 0;
   plr->ai.expand = 100;
   plr->ai.barbarian_type = NOT_A_BARBARIAN;
+
+  /* init armies of player */
+  for (i = 0; i < MAX_ARMIES_PER_PLAYER; i++)
+    plr->ai.army[i] = NULL;
+
   plr->future_tech=0;
   plr->economic.tax=PLAYER_DEFAULT_TAX_RATE;
   plr->economic.science=PLAYER_DEFAULT_SCIENCE_RATE;
diff -Nur -b -Xfreeciv-cvs-Oct-25/diff_ignore 
freeciv-cvs-Nov-16/common/player.h freeciv-cvs-army/common/player.h
--- freeciv-cvs-Nov-16/common/player.h  Thu Nov  7 19:55:25 2002
+++ freeciv-cvs-army/common/player.h    Sat Jan  4 01:33:25 2003
@@ -23,6 +23,7 @@
 #include "unit.h"

 struct tile;
+struct ai_army;

 #define PLAYER_DEFAULT_TAX_RATE 0
 #define PLAYER_DEFAULT_SCIENCE_RATE 100
@@ -114,6 +115,8 @@
   int spaceship;
 };

+#define MAX_ARMIES_PER_PLAYER 6
+
 struct player_ai {
   bool control;
   int tech_goal;
@@ -127,6 +130,7 @@
   int expand;                  /* percentage factor to value new cities */
   int warmth; /* threat of global warming */
   enum barbarian_type barbarian_type;
+  struct ai_army *army[MAX_ARMIES_PER_PLAYER];
 };

 /* Diplomatic states (how one player views another).
diff -Nur -b -Xfreeciv-cvs-Oct-25/diff_ignore freeciv-cvs-Nov-16/common/unit.h 
freeciv-cvs-army/common/unit.h
--- freeciv-cvs-Nov-16/common/unit.h    Fri Nov 15 22:24:30 2002
+++ freeciv-cvs-army/common/unit.h      Sat Jan  4 02:15:40 2003
@@ -50,7 +50,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_PILLAGE, AIUNIT_ARMY };

 enum goto_move_restriction {
   GOTO_MOVE_ANY,

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