Complete.Org: Mailing Lists: Archives: freeciv-ai: April 2005:
[freeciv-ai] Re: [Freeciv-Dev] Re: (PR#11995) Stupid AI Creates Tall Sta
Home

[freeciv-ai] Re: [Freeciv-Dev] Re: (PR#11995) Stupid AI Creates Tall Sta

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [freeciv-ai] Re: [Freeciv-Dev] Re: (PR#11995) Stupid AI Creates Tall Stacks
From: "Benedict Adamson" <badamson@xxxxxxxxxxx>
Date: Tue, 12 Apr 2005 14:12:50 -0700
Reply-to: bugs@xxxxxxxxxxx

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

Benedict Adamson wrote:
...
> Detecting which tiles have dangerously large stacks is quick and easy 
> using a movemap to find which enemies can attack the tile.
...

And here is the patch.


diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/Makefile.am freeciv.PR11995/ai/Makefile.am
--- vendor.freeciv.current/ai/Makefile.am       2005-01-24 20:43:05.000000000 
+0000
+++ freeciv.PR11995/ai/Makefile.am      2005-04-12 22:12:28.000000000 +0100
@@ -35,6 +35,8 @@
                aisettler.h     \
                aitech.c        \
                aitech.h        \
+               aithreat.c      \
+               aithreat.h      \
                aitools.c       \
                aitools.h       \
                aiunit.c        \
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/aithreat.c freeciv.PR11995/ai/aithreat.c
--- vendor.freeciv.current/ai/aithreat.c        1970-01-01 01:00:00.000000000 
+0100
+++ freeciv.PR11995/ai/aithreat.c       2005-04-12 22:12:28.000000000 +0100
@@ -0,0 +1,229 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 2004 - The Freeciv Project
+   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 "diplomats.h"
+#include "government.h"
+#include "log.h"
+#include "map.h"
+#include "movement.h"
+#include "path_finding.h"
+#include "pf_tools.h"
+#include "unit.h"
+
+#include "aidata.h"
+#include "ailog.h"
+#include "aisupport.h"
+#include "aitools.h"
+#include "aiunit.h"
+
+#include "aithreat.h"
+
+/* get 'struct int_vector' and related functions: */
+#define SPECVEC_TAG int
+#define SPECVEC_TYPE int
+#include "specvec.h"
+
+
+
+#define LOGLEVEL_THREAT LOG_DEBUG
+
+
+/**********************************************************************
+  The probability that we will be at war with a player within one turn.
+  Compare with the is_player_dangerous function.
+***********************************************************************/
+Probability aithreat_player_danger(struct player *pplayer,
+                                  struct player *aplayer)
+{
+  struct ai_data *ai = ai_data_get(pplayer);
+  struct ai_dip_intel *adip =
+      &ai->diplomacy.player_intel[aplayer->player_no];
+  const int reason = pplayer->diplstates[aplayer->player_no]
+      .has_reason_to_cancel;
+  Probability peace = 1;
+
+  if (pplayer == aplayer) {
+    /* Have to check if aplayer == pplayer explicitly because our reputation
+     * can be so low that we'd fear being stabbed in the back by ourselves */
+    return 0;
+  }
+
+  if (pplayers_at_war(pplayer, aplayer)) {
+    peace = 0;
+  }
+  /* WAGs */
+  if (ai->diplomacy.target == aplayer) {
+    peace = peace * 0.8;
+  }
+  if (reason != 0) {
+    peace = peace * 0.5;
+  }
+  if (ai->diplomacy.acceptable_reputation > aplayer->reputation) {
+    peace = peace * 0.9;
+  }
+  if (adip->is_allied_with_enemy) {
+    peace = peace * 0.6;
+  }
+
+  return 1.0 - peace;
+}
+
+/**************************************************************************
+  If a particular attacker tried to attack a given target,
+  who would be the defender?
+  The target might have a bodyguard or be aboard a ferry.
+**************************************************************************/
+static struct unit *get_real_defender(struct unit *attacker,
+                                     struct unit *target)
+{
+  struct unit *guard = find_unit_by_id(target->ai.bodyguard);
+
+  if (guard && guard->ai.charge != target->id) {
+    /* Should never happpen, but it seems our guard has left us */
+    guard = 0;
+  }
+  if (guard && guard->tile != target->tile) {
+    /* Our guard is not (yet) here to protect us */
+    guard = 0;
+  }
+  if (guard && !unit_can_defend_here(guard)) {
+    /* Our bodyguard can not help us on a ferry */
+    guard = 0;
+  }
+
+  if (guard && unit_win_chance(attacker, guard)
+      < unit_win_chance(attacker, target)) {
+    return guard;
+  }
+  /* else no guard or our guard has been wounded */
+
+  if (!unit_can_defend_here(target)) {
+    /* We might  be on a ferry */
+    struct unit *ferry = find_unit_by_id(target->transported_by);
+    if (ferry) {
+       return get_real_defender(attacker, ferry);
+    }
+  }
+
+  return target;
+}
+
+/**************************************************************************
+  The danger that a given defender unit would be in if attacked
+  by a given attacker at a given place,
+  expressed as a probability of being killed.
+**************************************************************************/
+static Probability unit_danger(struct unit *attacker, struct unit *defender,
+                               struct tile *ptile)
+{
+  struct player *attacker_owner = unit_owner(attacker);
+  struct player *defender_owner = unit_owner(defender);
+  const bool soldier = is_attack_unit(attacker);
+  Probability p_death = 0;
+
+  if (soldier && defender_owner != attacker_owner) {
+    const Probability p_war = aithreat_player_danger(defender_owner,
+                                                     attacker_owner);
+    /* Perhaps my bodyguard will protect me? */
+    struct unit *real_defender = get_real_defender(attacker, defender);
+    const Probability p_win = unit_win_chance(attacker, real_defender);
+
+    p_death = p_war * p_win;
+  }
+
+  return p_death;
+}
+
+/*********************************************************************
+  The value of the units belonging to a given player on a given tile.
+*********************************************************************/
+static Danger stack_value(const struct tile *ptile,
+                          const struct player *pplayer)
+{
+  int cost = 0;
+
+  if (is_stack_vulnerable(ptile)) {
+    unit_list_iterate(ptile->units, punit) {
+      if (unit_owner(punit) == pplayer) {
+       cost += unit_build_shield_cost(punit->type);
+      }
+    } unit_list_iterate_end;
+  }
+
+  return cost;
+}
+
+/****************************************************************************
+  Compute the danger rating, due to potential attacks by
+  enemy units, if a given unit were at a given position, considering only
+  attackers that can attack this turn. The computation accounts for the
+  total value of the stack of units at the given position.
+
+  This assumes that the unit will be attacked by at most one unit;
+  it ignores the extra danger because of follow-up attacks on the weakened
+  defender.
+
+  The units of danger are gross expected-loss in shields.
+  By using *gross* loss, we assume that our enemies care nothing for the
+  replacement costs of their attackers, which is pessimistic.
+
+  This function requires an up to date movemap.
+****************************************************************************/
+Danger aithreat_stack_danger_at(struct unit *defender, struct tile *ptile)
+{
+  struct unit *guard = find_unit_by_id(defender->ai.bodyguard);
+  struct tile *defender_tile = defender->tile;
+  struct tile *guard_tile = guard? guard->tile: 0;
+  Danger value = stack_value(ptile, unit_owner(defender));
+  Probability p_death = 0;
+
+  assert(defender);
+
+  /* If the defenders were *moved* to that tile, they would add to the
+   * value of the stack at that tile */
+  if (defender_tile != ptile) {
+    value += unit_build_shield_cost(defender->type);
+  }
+  if (guard && guard_tile != ptile) {
+    value += unit_build_shield_cost(guard->type);
+  }
+
+  /* Temporarilly move the defenders to the given place */
+  defender->tile = ptile;
+  if (guard) {
+    guard->tile = ptile;
+  }
+
+  /* We do not expect enemies on our tiles,
+   * but our allies might be untrustworthy */
+  unit_list_iterate(ptile->units, attacker) {
+    p_death = MAX(p_death, unit_danger(attacker, defender, ptile));
+  } unit_list_iterate_end;
+
+  /* Compute the danger from units that can arrive this turn */
+  movemap_iterate(ptile, 0, attacker) {
+    p_death = MAX(p_death, unit_danger(attacker, defender, ptile));
+  } movemap_iterate_end;
+
+  /* Restore the moved units to their original positions */
+  defender->tile = defender_tile;
+  if (guard) {
+    guard->tile = guard_tile;
+  }
+
+  return p_death * value;
+}
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/aithreat.h freeciv.PR11995/ai/aithreat.h
--- vendor.freeciv.current/ai/aithreat.h        1970-01-01 01:00:00.000000000 
+0100
+++ freeciv.PR11995/ai/aithreat.h       2005-04-12 22:12:28.000000000 +0100
@@ -0,0 +1,25 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 2002 - The Freeciv Project
+   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__AITHREAT_H
+#define FC__AITHREAT_H
+
+#include "shared.h"            /* bool type */
+#include "fc_types.h"
+#include "aithreattype.h"
+
+Probability aithreat_player_danger(struct player *pplayer,
+                                  struct player *aplayer);
+
+Danger aithreat_stack_danger_at(struct unit *defender, struct tile *ptile);
+
+#endif /* FC__AITHREAT_H */
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/aithreattype.h freeciv.PR11995/ai/aithreattype.h
--- vendor.freeciv.current/ai/aithreattype.h    1970-01-01 01:00:00.000000000 
+0100
+++ freeciv.PR11995/ai/aithreattype.h   2005-04-12 22:12:28.000000000 +0100
@@ -0,0 +1,25 @@
+/**********************************************************************
+ Freeciv - Copyright (C) 2004 - The Freeciv Project
+   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__AITHREATTYPE_H
+#define FC__AITHREATTYPE_H
+
+typedef double Probability;
+
+/* Danger values measured in expected-loss in shields.
+ * For these values, beware of applying WAGs, because we need to
+ * to retain correspondance between dangers ratings and unit values,
+ * so we can make sensible cost-benefit comparisons. */
+typedef double Danger;
+
+#endif /* FC__AITHREATTYPE_H */
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/aitools.c freeciv.PR11995/ai/aitools.c
--- vendor.freeciv.current/ai/aitools.c 2005-04-12 20:48:41.000000000 +0100
+++ freeciv.PR11995/ai/aitools.c        2005-04-12 22:12:28.000000000 +0100
@@ -52,6 +52,7 @@
 #include "aiferry.h"
 #include "ailog.h"
 #include "aitech.h"
+#include "aithreat.h"
 #include "aiunit.h"
 
 #include "aitools.h"
@@ -418,61 +419,6 @@
   return alive;
 }
 
-
-/*********************************************************************
-  The value of the units belonging to a given player on a given tile.
-*********************************************************************/
-static int stack_value(const struct tile *ptile,
-                      const struct player *pplayer)
-{
-  int cost = 0;
-
-  if (is_stack_vulnerable(ptile)) {
-    unit_list_iterate(ptile->units, punit) {
-      if (unit_owner(punit) == pplayer) {
-       cost += unit_build_shield_cost(punit->type);
-      }
-    } unit_list_iterate_end;
-  }
-
-  return cost;
-}
-
-/*********************************************************************
-  How dangerous would it be stop on a particular tile,
-  because of enemy attacks,
-  expressed as the probability of being killed.
-
-  TODO: This implementation is a kludge until we compute a more accurate
-  probability using the movemap.
-  Also, we should take into account the reduced probability of death
-  if we have a bodyguard travelling with us.
-*********************************************************************/
-static double chance_killed_at(const struct tile *ptile,
-                              struct ai_risk_cost *risk_cost,
-                              struct pf_parameter *param)
-{
-  double db;
-  /* Compute the basic probability */
-  /* WAG */
-  /* In the early stages of a typical game, ferries
-   * are effectively invulnerable (not until Frigates set sail),
-   * so we make seas appear safer.
-   * If we don't do this, the amphibious movement code has too strong a
-   * desire to minimise the length of the path,
-   * leading to poor choice for landing beaches */
-  double p = is_ocean(ptile->terrain)? 0.05: 0.15;
-
-  /* If we are on defensive terrain, we are more likely to survive */
-  db = get_tile_type(ptile->terrain)->defense_bonus;
-  if (map_has_special(ptile, S_RIVER)) {
-    db += (db * terrain_control.river_defense_bonus) / 100;
-  }
-  p *= 10.0 / db;
-
-  return p;
-}
-
 /*********************************************************************
   PF stack risk cost. How undesirable is passing through a tile
   because of risks?
@@ -493,11 +439,7 @@
                      struct pf_parameter *param)
 {
   double risk = 0;
-  /* Compute the risk of destruction, assuming we will stop at this tile */
-  const double value = risk_cost->base_value
-                       + stack_value(ptile, param->owner);
-  const double p_killed = chance_killed_at(ptile, risk_cost, param);
-  double danger = value * p_killed;
+  Danger danger = aithreat_stack_danger_at(risk_cost->punit, (struct tile 
*)ptile);
 
   if (terrain_has_flag(ptile->terrain, TER_UNSAFE)) {
     danger += risk_cost->unsafe_terrain_cost;
@@ -555,6 +497,7 @@
   parameter->data = risk_cost;
   parameter->get_EC = prefer_short_stacks;
   parameter->turn_mode = TM_WORST_TIME;
+  risk_cost->punit = punit;
   risk_cost->base_value = unit_build_shield_cost(punit->type);
   risk_cost->fearfulness = fearfulness * linger_fraction;
 
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/aitools.h freeciv.PR11995/ai/aitools.h
--- vendor.freeciv.current/ai/aitools.h 2005-04-05 22:40:45.000000000 +0100
+++ freeciv.PR11995/ai/aitools.h        2005-04-12 22:12:28.000000000 +0100
@@ -27,8 +27,9 @@
 /*
  * WAGs: how hard to avoid tall stacks of units.
  * Pass as fearfulness values to ai_avoid_risks.
+ * Larger values mean more fearful.
  */
-#define NORMAL_STACKING_FEARFULNESS ((double)PF_TURN_FACTOR / 36.0)
+#define NORMAL_STACKING_FEARFULNESS ((double)PF_TURN_FACTOR * 
(double)SINGLE_MOVE / 40.0)
 
 #ifdef DEBUG
 #define CHECK_UNIT(punit)                                        \
@@ -50,6 +51,7 @@
  */
 struct ai_risk_cost
 {
+  struct unit *punit;
   double base_value;
   double fearfulness;
   double ocean_cost;
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/common/combat.c freeciv.PR11995/common/combat.c
--- vendor.freeciv.current/common/combat.c      2005-03-29 22:31:20.000000000 
+0100
+++ freeciv.PR11995/common/combat.c     2005-04-12 22:12:27.000000000 +0100
@@ -522,8 +522,8 @@
 Unlike the one got from win chance this doesn't potentially get insanely
 small if the units are unevenly matched, unlike win_chance.
 **************************************************************************/
-static int get_defense_rating(const struct unit *attacker,
-                             const struct unit *defender)
+int get_defense_rating(const struct unit *attacker,
+                       const struct unit *defender)
 {
   int afp, dfp;
 
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/common/combat.h freeciv.PR11995/common/combat.h
--- vendor.freeciv.current/common/combat.h      2005-01-24 20:45:29.000000000 
+0000
+++ freeciv.PR11995/common/combat.h     2005-04-12 22:12:27.000000000 +0100
@@ -50,6 +50,8 @@
 int get_attack_power(const struct unit *punit);
 int base_get_attack_power(Unit_Type_id type, int veteran, int moves_left);
 int base_get_defense_power(const struct unit *punit);
+int get_defense_rating(const struct unit *attacker,
+                       const struct unit *defender);
 int get_defense_power(const struct unit *punit);
 int get_total_defense_power(const struct unit *attacker,
                            const struct unit *defender);

[Prev in Thread] Current Thread [Next in Thread]
  • [freeciv-ai] Re: [Freeciv-Dev] Re: (PR#11995) Stupid AI Creates Tall Stacks, Benedict Adamson <=