Complete.Org: Mailing Lists: Archives: freeciv-dev: March 2006:
[Freeciv-Dev] Re: (PR#15249) Aggressive AI Could Fight Itself to Obsolen
Home

[Freeciv-Dev] Re: (PR#15249) Aggressive AI Could Fight Itself to Obsolen

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: badamson@xxxxxxxxxxxxx
Subject: [Freeciv-Dev] Re: (PR#15249) Aggressive AI Could Fight Itself to Obsolence
From: "Benedict Adamson" <badamson@xxxxxxxxxxx>
Date: Fri, 17 Mar 2006 10:50:40 -0800
Reply-to: bugs@xxxxxxxxxxx

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

Here is a patch. I have extracted this patch from my reworking of the 
wants code (PR#14923). This patch should cause the AI to be less stupid 
when it has extreme tax needs. It uses negative feedback to correct the 
tax rate.



diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/aihand.c freeciv.PR15249/ai/aihand.c
--- vendor.freeciv.current/ai/aihand.c  2005-12-19 22:41:47.000000000 +0000
+++ freeciv.PR15249/ai/aihand.c 2006-03-17 18:45:57.000000000 +0000
@@ -83,6 +83,81 @@
   }
 }
 
+/*
+ * Over how many turns to try and smooth out changes in the gold reserves.
+ * WAG
+ */
+#define T_RESERVE_SMOOTH 4
+
+/**************************************************************************
+  How much tax should we raise this turn?
+  This may be negative, indicating we may diminish our reserves instead of
+  collecting tax.
+
+  This uses several WAGS to guess a good value.
+**************************************************************************/
+static int ai_min_income(struct player *pplayer,
+                         int trade,
+                         int expenses)
+{
+  const struct ai_data *ai = ai_data_get(pplayer);
+  const int n_cities = city_list_size(pplayer->cities);
+  const int reserve = ai_gold_reserve(pplayer);
+
+  /* The fraction of our income to divert to purchases,
+   * as a percentage.
+   */
+  int f;
+
+  int purchases, income;
+
+  assert(pplayer);
+  assert(0 <= expenses);
+
+  f = 5; /* minimum */
+  if (ai_wants_no_science(pplayer)) {
+    /* Instead of science research, we should purchase things. */
+    f += 20;
+  }
+  if (ai_on_war_footing(pplayer)) {
+    /* We should spend income on attack units */
+    f += 35;
+  }
+  if (n_cities / 8 < ai->stats.units.upgradeable) {
+    /* We should spend cash on upgrading units */
+    f += 10;
+  }
+
+  if (0 < trade) {
+    /* If our reserves are low or expenses are high,
+     * we should reduce our purchases.
+     */
+    const int f2 = 100 * expenses / trade;
+    const int f3 = 100 * (reserve - pplayer->economic.gold) / trade;
+
+    f -= MIN(f2 / 3, f); /* WAG */
+    if (0 < f3) {
+      f -= MIN(f3 / 4, f); /* WAG */
+    }
+  }
+
+  f = MIN(f, 100);
+  assert(0 <= f && f <= 100);
+
+  /* How much of our resources should we spend on purchases this turn? */
+  purchases = f * trade / 100;
+
+  /* Use simple feedback to ensure that our taxation will roughly balance
+   * our expenditure, even if our estimation has been inaccurate:
+   * if we raise more tax than we need, pplayer->economic.gold will grow
+   * until it exceeds (purchases + reserve), which will than cause
+   * us to reduce our taxation.
+   */
+  income = expenses + (purchases + reserve - pplayer->economic.gold)
+                      / T_RESERVE_SMOOTH;
+  return income;
+}
+
 /**************************************************************************
   Set tax/science/luxury rates.
 
@@ -90,18 +165,31 @@
   all cities are content and the trade output (minus what is consumed by 
   luxuries) is maximal.  For this we need some more information from the 
   city management code.
-
-  TODO: Audit the use of pplayer->ai.maxbuycost in the code elsewhere,
-  then add support for it here.
 **************************************************************************/
 static void ai_manage_taxes(struct player *pplayer) 
 {
-  int maxrate = (ai_handicap(pplayer, H_RATES) 
-                 ? get_player_bonus(pplayer, EFT_MAX_RATES) : 100);
+  const bool no_science = ai_wants_no_science(pplayer);
+  /* Should we celebrate? */
+  /* TODO: In the future, we should check if we should 
+   * celebrate for other reasons than growth. Currently 
+   * this is ignored. Maybe we need ruleset AI hints. */
+  /* TODO: Allow celebrate individual cities? No modpacks use this yet. */
+  const bool try_celebrate = get_player_bonus(pplayer, EFT_RAPTURE_GROW) > 0
+                             && !ai_handicap(pplayer, H_AWAY);
+  const int maxrate = (ai_handicap(pplayer, H_RATES) 
+                       ? get_player_bonus(pplayer, EFT_MAX_RATES) : 100);
+  const int rate2 = MIN(maxrate, 100 - maxrate);
+  const int rate3 = (100 - maxrate - rate2); /* Spillover, often 0 */
+
   bool celebrate = TRUE;
   int can_celebrate = 0, total_cities = 0;
   int trade = 0; /* total amount of trade generated */
   int expenses = 0; /* total amount of gold upkeep */
+  int min_income = 0;
+
+  assert(maxrate + rate2 + rate3 == 100);
+  assert(rate2 <= maxrate);
+  assert(rate3 <= rate2);
 
   if (!game.info.changable_tax) {
     return; /* This ruleset does not support changing tax rates. */
@@ -117,15 +205,32 @@
     expenses += pcity->usage[O_GOLD];
   } city_list_iterate_end;
 
-  /* Find minimum tax rate which gives us a positive balance. We assume
-   * that we want science most and luxuries least here, and reverse or 
-   * modify this assumption later. on */
+  min_income = ai_min_income(pplayer, trade, expenses);
+
+  /* Find minimum tax rate which gives us the required net income. */
 
   /* First set tax to the minimal available number */
-  pplayer->economic.science = maxrate; /* Assume we want science here */
-  pplayer->economic.tax = MAX(0, 100 - maxrate * 2); /* If maxrate < 50% */
-  pplayer->economic.luxury = (100 - pplayer->economic.science
-                             - pplayer->economic.tax); /* Spillover */
+  if (no_science && try_celebrate) {
+    /* Science, rather than taxes, should be the minimum */
+    pplayer->economic.luxury = maxrate;
+    pplayer->economic.tax = rate2;
+    pplayer->economic.science = rate3;
+  } else if (no_science) {
+    /* Science and luxuries are not worthwhile, so no real choice here. */
+    pplayer->economic.tax = maxrate;
+    pplayer->economic.luxury = rate2;
+    pplayer->economic.science = rate3;
+  } else if (try_celebrate) {
+    /* Assume we want science most */
+    pplayer->economic.science = maxrate;
+    pplayer->economic.luxury = rate2;
+    pplayer->economic.tax = rate3;
+  } else {
+    /* Assume we want science most and that luxuries are worthless. */
+    pplayer->economic.science = maxrate;
+    pplayer->economic.tax = rate2;
+    pplayer->economic.luxury = rate3;
+  }
 
   /* Now find the minimum tax with positive balance */
   while(pplayer->economic.tax < maxrate
@@ -142,7 +247,8 @@
     rates[TAX] = 100 - rates[SCIENCE] - rates[LUXURY];
     distribute(trade, 3, rates, result);
 
-    if (expenses - result[TAX] > 0) {
+    if (result[TAX] < min_income) {
+      /* Try a higher tax rate */
       pplayer->economic.tax += 10;
       if (pplayer->economic.luxury > 0) {
         pplayer->economic.luxury -= 10;
@@ -151,27 +257,12 @@
       }
     } else {
       /* Ok, got positive balance */
-      if (pplayer->economic.gold < ai_gold_reserve(pplayer)) {
-        /* Need to refill coffers, increase tax a bit */
-        pplayer->economic.tax += 10;
-        if (pplayer->economic.luxury > 0) {
-          pplayer->economic.luxury -= 10;
-        } else {
-          pplayer->economic.science -= 10;
-        }
-      }
       /* Done! Break the while loop */
       break;
     }
   }
 
-  /* Should we celebrate? */
-  /* TODO: In the future, we should check if we should 
-   * celebrate for other reasons than growth. Currently 
-   * this is ignored. Maybe we need ruleset AI hints. */
-  /* TODO: Allow celebrate individual cities? No modpacks use this yet. */
-  if (get_player_bonus(pplayer, EFT_RAPTURE_GROW) > 0
-      && !ai_handicap(pplayer, H_AWAY)) {
+  if (try_celebrate) {
     int luxrate = pplayer->economic.luxury;
     int scirate = pplayer->economic.science;
     struct cm_parameter cmp;
@@ -240,16 +331,6 @@
     /* TODO: Add general luxury code here. */
   }
 
-  /* Ok, we now have the desired tax and luxury rates. Do we really want
-   * science? If not, swap it with tax if it is bigger. */
-  if ((ai_wants_no_science(pplayer) || ai_on_war_footing(pplayer))
-      && pplayer->economic.science > pplayer->economic.tax) {
-    int science = pplayer->economic.science;
-    /* Swap science and tax */
-    pplayer->economic.science = pplayer->economic.tax;
-    pplayer->economic.tax = science;
-  }
-
   assert(pplayer->economic.tax + pplayer->economic.luxury 
          + pplayer->economic.science == 100);
   freelog(LOGLEVEL_TAX, "%s rates: Sci=%d Lux=%d Tax=%d trade=%d expenses=%d"
diff -ruN -Xvendor.freeciv.current/diff_ignore 
vendor.freeciv.current/ai/aitools.c freeciv.PR15249/ai/aitools.c
--- vendor.freeciv.current/ai/aitools.c 2006-01-22 20:34:17.000000000 +0000
+++ freeciv.PR15249/ai/aitools.c        2006-03-17 18:45:57.000000000 +0000
@@ -1106,15 +1106,55 @@
 }
 
 /**************************************************************************
-  Credits the AI wants to have in reserves. We need some gold to bribe
-  and incite cities.
+  How large a gold reserve should we try to maintain, on average?
+  This is the amount of gold we should usually aim to have after all
+  expenditures this turn. We may have more or less, but we should try
+  not to do so.
 
-  "I still don't trust this function" -- Syela
+  This uses several WAGS to guess a good value.
 **************************************************************************/
 int ai_gold_reserve(struct player *pplayer)
 {
-  int i = total_player_citizens(pplayer)*2;
-  return MAX(pplayer->ai.maxbuycost, i);
+  const struct ai_data *ai = ai_data_get(pplayer);
+  const int n_cities = city_list_size(pplayer->cities);
+
+  /* Number of turns of income we should hold in reserve,
+   * multiplied by 100.
+   */
+  int t_reserve;
+
+  int expenses = 0;
+  int reserve;
+
+  assert(pplayer);
+
+  /* Find total gold expenses */
+  city_list_iterate(pplayer->cities, pcity) {
+    expenses += pcity->usage[O_GOLD];
+  } city_list_iterate_end;
+
+  t_reserve = 300; /* minimum */
+  if (ai_wants_no_science(pplayer)) {
+    /* Instead of science research, we should build up a cash reserve */
+    t_reserve += 300;
+  }
+  if (n_cities / 8 < ai->stats.units.upgradeable) {
+    /* We should save cash to spend on emergency upgrades of units. */
+    t_reserve += 20;
+  }
+  assert(0 < t_reserve);
+
+  reserve = expenses * t_reserve / 100;
+
+  /* Ensure protection against extra upkeep for new city improvements,
+   * even if we currently have no expenses.
+   */
+  reserve = MAX(reserve, n_cities / 8);
+
+  /* Ensure we always have something in reserve */
+  reserve = MAX(reserve, 5);
+
+  return reserve;
 }
 
 /**************************************************************************

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