[freeciv-ai] Patch Army
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
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,
- [freeciv-ai] Patch Army,
Jordi Negrevernis i Font <=
|
|