Complete.Org: Mailing Lists: Archives: freeciv-dev: December 2002:
[Freeciv-Dev] (PR#2519) Change AI handling of governments
Home

[Freeciv-Dev] (PR#2519) Change AI handling of governments

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients:;
Subject: [Freeciv-Dev] (PR#2519) Change AI handling of governments
From: "Per I. Mathisen via RT" <rt@xxxxxxxxxxxxxx>
Date: Sun, 8 Dec 2002 15:17:45 -0800
Reply-to: rt@xxxxxxxxxxxxxx

This is a minimalist patch for AI evaluation and change of government. It
removes more code than it adds, and yet, it properly evaluates all
government types (even modpack ones), sets techs appropriately and changes
to the best form of government.

Changes since last version:

I tried several approaches to achieve a "cooldown" effect to avoid always
researching goverments. The best solution I found was simply to use
amortize on the want. This seemed to work very well. Usually the AI left
Democracy alone until it was close in the tech tree anyway so that the
amortization didn't penalise it so much.

I marked the wild guesses with /* WAG */ but didn't follow Raimar's
suggestion that they be placed in a separate file. This should be done
properly in a separate patch instead. We need to think about how this
should relate to nation ruleset AI hints. At present, the government AI
hint in nation rulesets doesn't mean much - if it is Democracy it is
likely to get it faster, but if it is Monarchy it gets ignored - Monarchy
is too bad even with a 20% bonus.

I didn't remove government subgoals with this patch, but this should be
done afterwards, since they will now be unused, and I see no further use
for them.

I believe the patch is ready for inclusion and want someone to review it,
and preferably commit it soon. Further work on an AI that understands the
direct effects of improvements (needed for general effects) will wait
until this patch is in, since I reuse some code there from this patch.

  - Per

Index: ai/aicity.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aicity.c,v
retrieving revision 1.129
diff -u -r1.129 aicity.c
--- ai/aicity.c 19 Nov 2002 13:31:07 -0000      1.129
+++ ai/aicity.c 8 Dec 2002 23:05:18 -0000
@@ -55,6 +55,26 @@
 #include "aicity.h"
 
 static void ai_manage_city(struct player *pplayer, struct city *pcity);
+
+/**************************************************************************
+  This calculates the usefulness of pcity to us. Note that you can pass
+  another player's ai_data structure here for evaluation by different
+  priorities.
+**************************************************************************/
+int ai_eval_calc_city(struct city *pcity, struct ai_data *ai)
+{
+  int i = (pcity->food_surplus * ai->food_priority
+           + pcity->shield_surplus * ai->shield_priority
+           + pcity->luxury_total * ai->trade_priority
+           + pcity->tax_total * ai->trade_priority
+           + pcity->science_total * ai->trade_priority
+           + pcity->ppl_happy[4] * ai->happy_priority
+           - pcity->ppl_unhappy[4] * ai->unhappy_priority
+           - pcity->ppl_angry[4] * ai->angry_priority
+           - pcity->pollution * ai->pollution_priority);
+
+  return i;
+}
      
 /************************************************************************** 
 ...
Index: ai/aicity.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aicity.h,v
retrieving revision 1.16
diff -u -r1.16 aicity.h
--- ai/aicity.h 7 Nov 2002 15:40:24 -0000       1.16
+++ ai/aicity.h 8 Dec 2002 23:05:18 -0000
@@ -18,6 +18,9 @@
 struct player;
 struct city;
 struct ai_choice;
+struct ai_data;
+
+int ai_eval_calc_city(struct city *pcity, struct ai_data *ai);
 
 void ai_manage_cities(struct player *pplayer);
 void ai_choose_ferryboat(struct player *pplayer, struct city *pcity, struct 
ai_choice *choice);
Index: ai/aidata.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.c,v
retrieving revision 1.3
diff -u -r1.3 aidata.c
--- ai/aidata.c 14 Nov 2002 09:14:50 -0000      1.3
+++ ai/aidata.c 8 Dec 2002 23:05:18 -0000
@@ -188,6 +188,20 @@
       ai->stats.workers[ptile->continent]++;
     }
   } unit_list_iterate_end;
+
+  /* 
+   * Priorities. NEVER set these to zero! Weight values are usually
+   * multiplied by these values, so be careful with them. They are
+   * used in city calculations, and food and shields should be slightly
+   * bigger because we only look at surpluses there.
+   */
+  ai->food_priority = FOOD_WEIGHTING;
+  ai->shield_priority = SHIELD_WEIGHTING;
+  ai->trade_priority = TRADE_WEIGHTING;
+  ai->happy_priority = TRADE_WEIGHTING;
+  ai->unhappy_priority = TRADE_WEIGHTING;
+  ai->angry_priority = TRADE_WEIGHTING * 3; /* danger */
+  ai->pollution_priority = SHIELD_WEIGHTING;
 }
 
 /**************************************************************************
@@ -195,10 +209,11 @@
 **************************************************************************/
 void ai_data_turn_done(struct player *pplayer) {
   struct ai_data *ai = &aidata[pplayer->player_no];
-  free(ai->explore.continent);
-  free(ai->threats.continent);
-  free(ai->stats.workers);
-  free(ai->stats.cities);
+
+  free(ai->explore.continent); ai->explore.continent = NULL;
+  free(ai->threats.continent); ai->threats.continent = NULL;
+  free(ai->stats.workers);     ai->stats.workers = NULL;
+  free(ai->stats.cities);      ai->stats.cities = NULL;
 }
 
 /**************************************************************************
Index: ai/aidata.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.h,v
retrieving revision 1.2
diff -u -r1.2 aidata.h
--- ai/aidata.h 15 Nov 2002 22:15:01 -0000      1.2
+++ ai/aidata.h 8 Dec 2002 23:05:18 -0000
@@ -50,6 +50,15 @@
   } stats;
 
   int num_continents; /* last time we updated our continent data */
+
+  /* Dynamic weights used in addition to Syela's hardcoded weights */
+  int shield_priority;
+  int food_priority;
+  int trade_priority;
+  int happy_priority;
+  int unhappy_priority;
+  int angry_priority;
+  int pollution_priority;
 };
 
 void ai_data_turn_init(struct player *pplayer);
Index: ai/aihand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aihand.c,v
retrieving revision 1.71
diff -u -r1.71 aihand.c
--- ai/aihand.c 14 Nov 2002 09:14:50 -0000      1.71
+++ ai/aihand.c 8 Dec 2002 23:05:19 -0000
@@ -31,10 +31,13 @@
 
 #include "citytools.h"
 #include "cityturn.h"
+#include "plrhand.h"
 #include "spacerace.h"
+#include "settlers.h" /* amortize */
 #include "unithand.h"
 
 #include "aicity.h"
+#include "aidata.h"
 #include "aitech.h"
 #include "aitools.h"
 #include "aiunit.h"
@@ -303,59 +306,93 @@
 }
 
 /**************************************************************************
- change the government form, if it can and there is a good reason
+  Change the government form, if it can and there is a good reason.
 **************************************************************************/
 static void ai_manage_government(struct player *pplayer)
 {
-  int goal;
-  int subgoal;
-  int failsafe;
-  
-  goal = game.ai_goal_government;
-  /* Was G_REPUBLIC; need to be REPUBLIC+ to love */
-  
-  /* advantages of DEMOCRACY:
-        partisans, no bribes, no corrup, +1 content if courthouse;
-     disadvantages of DEMOCRACY:
-        doubled unhappiness from attacking units, anarchy 
-     realistically we should allow DEMOC in some circumstances but
-     not yet -- Syela
-  */
-  if (pplayer->government == goal) {
-    freelog(LOG_DEBUG, "ai_man_gov (%s): there %d", pplayer->name, goal);
-    return;
-  }
-  if (can_change_to_government(pplayer, goal)) {
-    freelog(LOG_DEBUG, "ai_man_gov (%s): change %d", pplayer->name, goal);
-    ai_government_change(pplayer, goal);
-    return;
-  }
-  failsafe = 0;
-  while((subgoal = get_government(goal)->subgoal) >= 0) {
-    if (can_change_to_government(pplayer, subgoal)) {
-      freelog(LOG_DEBUG, "ai_man_gov (%s): change sub %d (%d)",
-             pplayer->name, subgoal, goal);
-      ai_government_change(pplayer, subgoal);
-      break;
-    }
-    goal = subgoal;
-    if (++failsafe > game.government_count) {
-      freelog(LOG_ERROR, "Loop in ai_manage_government? (%s)",
-             pplayer->name);
-      return;
+  struct ai_data *ai = ai_data_get(pplayer);
+  int best_gov = 0;
+  int best_val = 0;
+  int really_best_val = 0;
+  int really_best_req = A_NONE;
+  int i;
+  int bonus = 0; /* in percentage */
+  int current_gov = pplayer->government;
+
+  for (i = 0; i < game.government_count; ++i) {
+    struct government *gov = &governments[i];
+    int val = 0;
+    int dist;
+
+    if (i == game.government_when_anarchy) {
+      continue; /* pointless */
     }
-  }
+    pplayer->government = gov->index;
+    check_player_government_rates(pplayer);
+    update_all_effects();
+    city_list_iterate(pplayer->cities, acity) {
+      generic_city_refresh(acity, TRUE);
+      auto_arrange_workers(acity);
+      if (ai_fix_unhappy(acity)) {
+        ai_scientists_taxmen(acity);
+      } 
+    } city_list_iterate_end;
+    city_list_iterate(pplayer->cities, pcity) {
+      val += ai_eval_calc_city(pcity, ai);
+    } city_list_iterate_end;
+
+    /* Bonuses for non-economic abilities */
+    if (government_has_flag(gov, G_BUILD_VETERAN_DIPLOMAT)) {
+      bonus += 3; /* WAG */
+    }
+    if (government_has_flag(gov, G_REVOLUTION_WHEN_UNHAPPY)) {
+      bonus -= 1; /* Not really a problem for us */ /* WAG */
+    }
+    if (government_has_flag(gov, G_UNBRIBABLE)) {
+      bonus += 10; /* WAG */
+    }
+    if (government_has_flag(gov, G_INSPIRES_PARTISANS)) {
+      bonus += 5; /* WAG */
+    }
+    if (government_has_flag(gov, G_RAPTURE_CITY_GROWTH)) {
+      bonus += 8; /* WAG */
+    }
+    if (government_has_flag(gov, G_FANATIC_TROOPS)) {
+      bonus += 3; /* WAG */
+    }
+    if (get_nation_by_idx(pplayer->nation)->goals.government == i) {
+      bonus += 20; /* WAG */
+    }
+
+    val += (val * bonus) / 100;
 
-  if (pplayer->government == game.government_when_anarchy) {
-    /* if the ai ever intends to stay anarchy, */
-    /* change condition to if( (pplayer->revolution==0) && */
-    if( ((pplayer->revolution<=0) || (pplayer->revolution>5))
-       && can_change_to_government(pplayer, game.default_government)) {
-      freelog(LOG_DEBUG, "ai_man_gov (%s): change from anarchy",
-             pplayer->name);
-      ai_government_change(pplayer, game.default_government);
+    dist = MAX(1, num_unknown_techs_for_goal(pplayer, gov->required_tech));
+    val = amortize(val, dist);
+    if (val > best_val && can_change_to_government(pplayer, i)) {
+      best_val = val;
+      best_gov = i;
     }
+    if (val > really_best_val) {
+      really_best_val = val;
+      really_best_req = gov->required_tech;
+    }
+    freelog(LOG_DEBUG, "%s govt eval %s (dist %d): %d",
+            pplayer->name, gov->name, dist, val);
+  }
+  if (best_gov != current_gov) {
+    ai_government_change(pplayer, best_gov); /* change */
+  } else {
+    pplayer->government = current_gov; /* reset */
+  }
+
+  /* Crank up tech want */
+  if (get_invention(pplayer, really_best_req) == TECH_KNOWN) {
+    return; /* already got it! */
   }
+  pplayer->ai.tech_want[really_best_req] += (really_best_val - best_val);
+  freelog(LOG_DEBUG, "%s wants %s with want %d", pplayer->name,
+          advances[really_best_req].name, 
+          pplayer->ai.tech_want[really_best_req]);
 }
 
 /**************************************************************************
Index: ai/aitech.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitech.c,v
retrieving revision 1.35
diff -u -r1.35 aitech.c
--- ai/aitech.c 14 Nov 2002 09:14:50 -0000      1.35
+++ ai/aitech.c 8 Dec 2002 23:05:19 -0000
@@ -37,40 +37,6 @@
 #include "aitech.h"
 
 /**************************************************************************
-.. AI got some tech goals, and should try to fulfill them. 
-**************************************************************************/
-
-/**************************************************************************
-.. calculate next government wish.
-**************************************************************************/
-static Tech_Type_id get_government_tech(struct player *plr)
-{
-  int goal = get_nation_by_plr(plr)->goals.government;
-  int subgoal = get_government(goal)->subgoal;
-  
-  if (can_change_to_government(plr, goal)) {
-    freelog(LOG_DEBUG, "get_gov_tech (%s): have %d", plr->name, goal);
-    return A_NONE;
-  }
-
-  if (subgoal >= 0) {
-    struct government *subgov = get_government(subgoal);
-    if (get_invention(plr, subgov->required_tech) == TECH_KNOWN) {
-      freelog(LOG_DEBUG, "get_gov_tech (%s): have sub %d %s",
-             plr->name, goal, subgov->name);
-      return get_government(goal)->required_tech;
-    } else {
-      freelog(LOG_DEBUG, "get_gov_tech (%s): do sub %d %s",
-             plr->name, goal, subgov->name);
-      return subgov->required_tech;
-    }
-  } else {
-    freelog(LOG_DEBUG, "get_gov_tech (%s): no sub %d", plr->name, goal);
-    return get_government(goal)->required_tech;
-  }
-}
-
-/**************************************************************************
   Returns tech corresponding to players wonder goal from nations[],
   if it makes sense, and wonder is not already built and not obsolete.
   Otherwise returns A_NONE.
@@ -113,14 +79,6 @@
       break; /* remove this to restore old functionality -- Syela */
     }
   } 
-  tech = get_government_tech(pplayer);
-  if (tech != A_NONE && tech_exists(tech)) {
-    dist = num_unknown_techs_for_goal(pplayer, tech);
-    if (dist < bestdist) { 
-      bestdist = dist;
-      goal = tech;
-    }
-  }
   tech = get_wonder_tech(pplayer);
   if (tech != A_NONE) {
     dist = num_unknown_techs_for_goal(pplayer, tech);
Index: ai/aitools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitools.c,v
retrieving revision 1.63
diff -u -r1.63 aitools.c
--- ai/aitools.c        2 Dec 2002 22:48:13 -0000       1.63
+++ ai/aitools.c        8 Dec 2002 23:05:19 -0000
@@ -398,16 +398,18 @@
 }
 
 /**************************************************************************
-.. change government,pretty fast....
+  Change government, pretty fast...
 **************************************************************************/
 void ai_government_change(struct player *pplayer, int gov)
 {
   struct packet_player_request preq;
-  if (gov == pplayer->government)
+
+  if (gov == pplayer->government) {
     return;
-  preq.government=gov;
-  pplayer->revolution=0;
-  pplayer->government=game.government_when_anarchy;
+  }
+  preq.government = gov;
+  pplayer->revolution = 0;
+  pplayer->government = game.government_when_anarchy;
   handle_player_government(pplayer, &preq);
   pplayer->revolution = -1; /* yes, I really mean this. -- Syela */
 }
@@ -572,80 +574,4 @@
  
   if (unhap < 0) unhap = 0;
   return unhap > 0;
-}
-
-/**********************************************************************
-  Evaluate a government form, still pretty sketchy.
-
-  This evaluation should be more dynamic (based on players current
-  needs, like expansion, at war, etc, etc). -SKi
-
-  0 is my first attempt at government evaluation
-  1 is new evaluation based on patch from rizos -SKi
-**********************************************************************/
-int ai_evaluate_government (struct player *pplayer, struct government *g)
-{
-  int current_gov      = pplayer->government;
-  int shield_surplus   = 0;
-  int food_surplus     = 0;
-  int trade_prod       = 0;
-  int shield_need      = 0;
-  int food_need        = 0;
-  bool gov_overthrown   = FALSE;
-  int score;
-
-  pplayer->government = g->index;
-
-  city_list_iterate(pplayer->cities, pcity) {
-    city_refresh(pcity);
-
-    /* the lines that follow are copied from ai_manage_city -
-       we don't need the sell_obsolete_buildings */
-    auto_arrange_workers(pcity);
-    if (ai_fix_unhappy (pcity))
-      ai_scientists_taxmen(pcity);
-
-    trade_prod     += pcity->trade_prod;
-    if (pcity->shield_prod > 0)
-      shield_surplus += pcity->shield_surplus;
-    else
-      shield_need    += pcity->shield_surplus;
-    if (pcity->food_surplus > 0)
-      food_surplus   += pcity->food_surplus;
-    else
-      food_need      += pcity->food_surplus;
-
-    if (city_unhappy(pcity)) {
-      /* the following is essential to prevent falling into anarchy */
-      if (pcity->anarchy > 0
-         && government_has_flag(g, G_REVOLUTION_WHEN_UNHAPPY)) 
-        gov_overthrown = TRUE;
-    }
-  } city_list_iterate_end;
-
-  pplayer->government = current_gov;
-
-  /* Restore all cities. */
-  city_list_iterate(pplayer->cities, pcity) {
-    city_refresh(pcity);
-
-    /* the lines that follow are copied from ai_manage_city -
-       we don't need the sell_obsolete_buildings */
-    auto_arrange_workers(pcity);
-    if (ai_fix_unhappy (pcity))
-      ai_scientists_taxmen(pcity);
-  } city_list_iterate_end;
-  sync_cities();
-
-  score =
-    3 * trade_prod
-  + 2 * shield_surplus + 2 * food_surplus
-  - 4 * shield_need - 4 * food_need;
-
-  score = gov_overthrown ? 0 : MAX(score, 0);
-
-  freelog(LOG_DEBUG, "a_e_g (%12s) = score=%3d; trade=%3d; shield=%3d/%3d; "
-                     "food=%3d/%3d;", g->name, score, trade_prod, 
-                     shield_surplus, shield_need, food_surplus, food_need);
-  return score;
 }

[Prev in Thread] Current Thread [Next in Thread]
  • [Freeciv-Dev] (PR#2519) Change AI handling of governments, Per I. Mathisen via RT <=