[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 <=
|
|