Complete.Org: Mailing Lists: Archives: freeciv-ai: May 2004:
[freeciv-ai] (PR#8832) Cleaning ai/aitech.c
Home

[freeciv-ai] (PR#8832) Cleaning ai/aitech.c

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [freeciv-ai] (PR#8832) Cleaning ai/aitech.c
From: "Gregory Berkolaiko" <Gregory.Berkolaiko@xxxxxxxxxxxxx>
Date: Mon, 24 May 2004 22:08:36 -0700
Reply-to: rt@xxxxxxxxxxx

<URL: http://rt.freeciv.org/Ticket/Display.html?id=8832 >

ai/aitech.c is cleaned, code is shortened, comments added generously

The reason I attacked it was that it hijacked the ai_choice
(build-choice struct).  I gave it a personal and tailored ai_tech_choice.

Detected a little problem with it -- some time is spent on computing new
tech goal every turn.  Then this info is just neglected, when it comes
to actually act according to the goal -- choose new tech -- the goal is
recomputed.  Added commentary to this effect too.

In general I would like to rewrite the way it selects techs and goals to
make it more transparent.  But this is in the future.

For now the savegames are identical -- wasted 1 hour trying to achieve this.

G.
? core.3789
? settle4.gz
? settle5.gz
? stuck.gz
? ai/aisettler.c
? ai/aisettler.h
Index: ai/aicity.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aicity.c,v
retrieving revision 1.153
diff -u -r1.153 aicity.c
--- ai/aicity.c 24 May 2004 02:09:27 -0000      1.153
+++ ai/aicity.c 25 May 2004 04:58:27 -0000
@@ -561,8 +561,6 @@
   worker allocations,
   build choices,
   extra gold spending.
-
-  TODO: Treat ai_gov_tech_hints somewhere else
 **************************************************************************/
 void ai_manage_cities(struct player *pplayer)
 {
@@ -599,30 +597,6 @@
   } city_list_iterate_end;
 
   ai_spend_gold(pplayer);
-
-  /* use ai_gov_tech_hints: */
-  for(i=0; i<MAX_NUM_TECH_LIST; i++) {
-    struct ai_gov_tech_hint *hint = &ai_gov_tech_hints[i];
-    
-    if (hint->tech == A_LAST) {
-      break;
-    }
-    
-    if (get_invention(pplayer, hint->tech) != TECH_KNOWN) {
-      pplayer->ai.tech_want[hint->tech] +=
-       city_list_size(&pplayer->cities) 
-       * (hint->turns_factor 
-          * num_unknown_techs_for_goal(pplayer, hint->tech) 
-          + hint->const_factor);
-      if (hint->get_first) {
-       break;
-      }
-    } else {
-      if (hint->done) {
-       break;
-      }
-    }
-  }
 }
 
 /************************************************************************** 
Index: ai/aitech.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitech.c,v
retrieving revision 1.43
diff -u -r1.43 aitech.c
--- ai/aitech.c 14 Feb 2004 02:21:25 -0000      1.43
+++ ai/aitech.c 25 May 2004 04:58:28 -0000
@@ -31,6 +31,13 @@
 
 #include "aitech.h"
 
+struct ai_tech_choice {
+  Tech_Type_id choice;   /* The id of the most needed tech */
+  int want;              /* Want of the most needed tech */
+  int current_want;      /* Want of the tech which currenlty researched 
+                         * or is our current goal */
+};
+
 /**************************************************************************
   Returns tech corresponding to players wonder goal from nations[],
   if it makes sense, and wonder is not already built and not obsolete.
@@ -54,10 +61,11 @@
 }
 
 /**************************************************************************
-  ...
+  Puts into choice the closest (in terms of unknown techs needed) of
+    national tech goals,
+    requirements for the national wonder goal
 **************************************************************************/
-static void ai_next_tech_goal_default(struct player *pplayer, 
-                                     struct ai_choice *choice)
+static Tech_Type_id ai_next_tech_goal_default(struct player *pplayer)
 {
   struct nation_type *prace = get_nation_by_plr(pplayer);
   int bestdist = A_LAST + 1;
@@ -65,6 +73,7 @@
   Tech_Type_id goal = A_UNSET;
   Tech_Type_id tech;
 
+  /* Find the closest of the national tech goals */
   for (i = 0 ; i < MAX_NUM_TECH_GOALS; i++) {
     Tech_Type_id j = prace->goals.tech[i];
     if (!tech_is_available(pplayer, j) 
@@ -78,130 +87,175 @@
       break; /* remove this to restore old functionality -- Syela */
     }
   } 
+
+  /* Find the requirement for the national wonder */
   tech = get_wonder_tech(pplayer);
-  if (tech != A_UNSET) {
-    dist = num_unknown_techs_for_goal(pplayer, tech);
-    if (dist < bestdist) { 
-/*    bestdist = dist; */ /* useless, reinclude when adding a new if statement 
*/
-      goal = tech;
-    }
-  }
-  if (goal != A_UNSET) {
-    choice->choice = goal;
-    choice->want = 1;
+  if (tech != A_UNSET 
+      && num_unknown_techs_for_goal(pplayer, tech) < bestdist) {
+    goal = tech;
   }
+
+  return goal;
 }
 
 /**************************************************************************
-  ...
+  Massage the numbers provided to us by ai.tech_want into unrecognizable 
+  pulp.
+
+  TODO: Write a transparent formula.
+
+  Notes: 1. num_unknown_techs_for_goal returns 0 for known techs, 1 if tech 
+  is immediately available etc.
+  2. A tech is reachable means we can research it now; tech is available 
+  means it's on our tech tree (different nations can have different techs).
+  3. ai.tech_want is usually upped by each city, so it is divided by number
+  of cities here.
+  4. A tech isn't a requirement of itself.
 **************************************************************************/
-static void ai_select_tech(struct player *pplayer, struct ai_choice *choice,
-                          struct ai_choice *gol)
+static void ai_select_tech(struct player *pplayer, 
+                          struct ai_tech_choice *choice,
+                          struct ai_tech_choice *goal)
 {
-  Tech_Type_id k, l;
-  int j;
-  int num_cities_nonzero;
+  Tech_Type_id newtech, newgoal;
+  int num_cities_nonzero = MAX(1, city_list_size(&pplayer->cities));
   int values[A_LAST];
   int goal_values[A_LAST];
 
-  if((num_cities_nonzero = city_list_size(&pplayer->cities)) < 1)
-    num_cities_nonzero = 1;
   memset(values, 0, sizeof(values));
   memset(goal_values, 0, sizeof(goal_values));
+
+  /* Fill in values for the techs: want of the tech 
+   * + average want of those we will discover en route */
   tech_type_iterate(i) {
-    j = num_unknown_techs_for_goal(pplayer, i);
-    if (j > 0) { /* if we already got it we don't want it */
+    int steps = num_unknown_techs_for_goal(pplayer, i);
+
+    /* We only want it if we haven't got it (so AI is human after all) */
+    if (steps > 0) { 
       values[i] += pplayer->ai.tech_want[i];
       tech_type_iterate(k) {
        if (is_tech_a_req_for_goal(pplayer, k, i)) {
-         values[k] += pplayer->ai.tech_want[i] / j;
+         values[k] += pplayer->ai.tech_want[i] / steps;
        }
       } tech_type_iterate_end;
     }
   } tech_type_iterate_end;
 
+  /* Fill in the values for the tech goals */
   tech_type_iterate(i) {
-    if (num_unknown_techs_for_goal(pplayer, i) > 0) {
-      tech_type_iterate(k) {
-       if (is_tech_a_req_for_goal(pplayer, k, i)) {
-          goal_values[i] += values[k];
-        }
-      } tech_type_iterate_end;
-      goal_values[i] += values[i];
-      
-/* this is the best I could do.  It still sometimes does freaky stuff like
-setting goal to Republic and learning Monarchy, but that's what it's supposed
-to be doing; it just looks strange. -- Syela */
-      
-      goal_values[i] /= num_unknown_techs_for_goal(pplayer, i);
-      if (num_unknown_techs_for_goal(pplayer, i) < 6) {
-       freelog(LOG_DEBUG, "%s: want = %d, value = %d, goal_value = %d",
-               advances[i].name, pplayer->ai.tech_want[i],
-               values[i], goal_values[i]);
+    int steps = num_unknown_techs_for_goal(pplayer, i);
+
+    if (steps == 0) {
+      continue;
+    }
+
+    goal_values[i] = values[i];      
+    tech_type_iterate(k) {
+      if (is_tech_a_req_for_goal(pplayer, k, i)) {
+       goal_values[i] += values[k];
       }
-    } else goal_values[i] = 0;
+    } tech_type_iterate_end;
+    /* This is the best I could do.  It still sometimes does freaky stuff
+     * like setting goal to Republic and learning Monarchy, but that's what
+     * it's supposed to be doing; it just looks strange. -- Syela */
+    goal_values[i] /= steps;
+    if (steps < 6) {
+      freelog(LOG_DEBUG, "%s: want = %d, value = %d, goal_value = %d",
+             advances[i].name, pplayer->ai.tech_want[i],
+             values[i], goal_values[i]);
+    }
   } tech_type_iterate_end;
 
-  l = A_UNSET; /* currently researched tech */
-  k = A_UNSET; /* current tech goal */
+  newtech = A_UNSET;
+  newgoal = A_UNSET;
   tech_type_iterate(i) {
-    if (values[i] > values[l]
+    if (values[i] > values[newtech]
         && tech_is_available(pplayer, i)
         && get_invention(pplayer, i) == TECH_REACHABLE) {
-      l = i;
+      newtech = i;
     }
-    if (goal_values[i] > goal_values[k]
+    if (goal_values[i] > goal_values[newgoal]
         && tech_is_available(pplayer, i)) {
-      k = i;
+      newgoal = i;
     }
   } tech_type_iterate_end;
-  freelog(LOG_DEBUG, "%s wants %s with desire %d (%d).", pplayer->name,
-               advances[l].name, values[l], pplayer->ai.tech_want[l]);
+  freelog(LOG_DEBUG, "%s wants %s with desire %d (%d).", 
+         pplayer->name, advances[newtech].name, values[newtech], 
+         pplayer->ai.tech_want[newtech]);
   if (choice) {
-    choice->choice = l;
-    choice->want = values[l] / num_cities_nonzero;
-    choice->type = values[pplayer->research.researching] / num_cities_nonzero;
-    /* hijacking this ... in order to leave tech_wants alone */
+    choice->choice = newtech;
+    choice->want = values[newtech] / num_cities_nonzero;
+    choice->current_want = 
+      values[pplayer->research.researching] / num_cities_nonzero;
   }
 
-  if (gol) {
-    gol->choice = k;
-    gol->want = goal_values[k] / num_cities_nonzero;
-    gol->type = goal_values[pplayer->ai.tech_goal] / num_cities_nonzero;
+  if (goal) {
+    goal->choice = newgoal;
+    goal->want = goal_values[newgoal] / num_cities_nonzero;
+    goal->current_want = goal_values[pplayer->ai.tech_goal] / 
num_cities_nonzero;
     freelog(LOG_DEBUG,
-           "Gol->choice = %s, gol->want = %d, goal_value = %d, "
+           "Goal->choice = %s, goal->want = %d, goal_value = %d, "
            "num_cities_nonzero = %d",
-           advances[gol->choice].name, gol->want, goal_values[k],
+           advances[goal->choice].name, goal->want, goal_values[newgoal],
            num_cities_nonzero);
   }
   return;
 }
 
 /**************************************************************************
-  ...
+  Choose our next tech goal.  Called by the server when a new tech is 
+  discovered to determine new goal and, from it, the new tech to be 
+  researched, which is quite stupid since ai_manage_tech sets a goal in 
+  ai.tech_goal and we should either respect it or not bother doing it.
+
+  TODO: Kill this function.
 **************************************************************************/
-static void ai_select_tech_goal(struct player *pplayer, struct ai_choice 
*choice)
+void ai_next_tech_goal(struct player *pplayer)
 {
-  ai_select_tech(pplayer, NULL, choice);
+  struct ai_tech_choice goal_choice = {0, 0, 0};
+
+  ai_select_tech(pplayer, NULL, &goal_choice);
+
+  if (goal_choice.want == 0) {
+    goal_choice.choice = ai_next_tech_goal_default(pplayer);
+  }
+  if (goal_choice.choice != A_UNSET) {
+    pplayer->ai.tech_goal = goal_choice.choice;
+    freelog(LOG_DEBUG, "next_tech_goal for %s is set to %s",
+           pplayer->name, advances[goal_choice.choice].name);
+  }
 }
 
 /**************************************************************************
-  ...
+  Use AI tech hints provided in governments.ruleset to up corresponding 
+  tech wants.
+
+  TODO: The hints structure is too complicated, simplify.
 **************************************************************************/
-void ai_next_tech_goal(struct player *pplayer)
+static void ai_use_gov_tech_hint(struct player *pplayer)
 {
-  struct ai_choice bestchoice, curchoice;
+  int i;
 
-  init_choice(&bestchoice);
-
-  ai_select_tech_goal(pplayer, &curchoice);
-  copy_if_better_choice(&curchoice, &bestchoice);
-
-  if (bestchoice.want == 0) {
-    ai_next_tech_goal_default(pplayer, &bestchoice); 
-  }
-  if (bestchoice.want != 0) {
-    pplayer->ai.tech_goal = bestchoice.choice;
+  for(i=0; i<MAX_NUM_TECH_LIST; i++) {
+    struct ai_gov_tech_hint *hint = &ai_gov_tech_hints[i];
+    
+    if (hint->tech == A_LAST) {
+      break;
+    }
+    
+    if (get_invention(pplayer, hint->tech) != TECH_KNOWN) {
+      int steps = num_unknown_techs_for_goal(pplayer, hint->tech);
+
+      pplayer->ai.tech_want[hint->tech] += 
+       city_list_size(&pplayer->cities)
+       * (hint->turns_factor * steps + hint->const_factor);
+      if (hint->get_first) {
+       break;
+      }
+    } else {
+      if (hint->done) {
+       break;
+      }
+    }
   }
 }
 
@@ -211,9 +265,11 @@
 **************************************************************************/
 void ai_manage_tech(struct player *pplayer)
 {
-  struct ai_choice choice, gol;
-  int penalty;
+  struct ai_tech_choice choice, goal;
+  /* Penalty for switching research */
+  int penalty = (pplayer->got_tech ? 0 : pplayer->research.bulbs_researched);
 
+  /* If there are humans in our team, they will choose the techs */
   players_iterate(aplayer) {
     const struct player_diplstate *ds = pplayer_get_diplstate(pplayer, 
aplayer);
 
@@ -222,12 +278,12 @@
     }
   } players_iterate_end;
 
-  penalty = (pplayer->got_tech ? 0 : pplayer->research.bulbs_researched);
+  ai_use_gov_tech_hint(pplayer);
 
-  ai_select_tech(pplayer, &choice, &gol);
+  ai_select_tech(pplayer, &choice, &goal);
   if (choice.choice != pplayer->research.researching) {
     /* changing */
-    if ((choice.want - choice.type) > penalty &&
+    if ((choice.want - choice.current_want) > penalty &&
        penalty + pplayer->research.bulbs_researched <=
        total_bulbs_required(pplayer)) {
       freelog(LOG_DEBUG, "%s switching from %s to %s with penalty of %d.",
@@ -236,11 +292,14 @@
       choose_tech(pplayer, choice.choice);
     }
   }
+
   /* crossing my fingers on this one! -- Syela (seems to have worked!) */
-  if (gol.choice != pplayer->ai.tech_goal) {
-    freelog(LOG_DEBUG, "%s changing goal from %s (want = %d) to %s (want = 
%d)",
-           pplayer->name, advances[pplayer->ai.tech_goal].name, gol.type,
-           advances[gol.choice].name, gol.want);
-    choose_tech_goal(pplayer, gol.choice);
+  /* It worked, in particular, because the value it sets (ai.tech_goal)
+   * is practically never used, see the comment for ai_next_tech_goal */
+  if (goal.choice != pplayer->ai.tech_goal) {
+    freelog(LOG_DEBUG, "%s change goal from %s (want=%d) to %s (want=%d)",
+           pplayer->name, advances[pplayer->ai.tech_goal].name, 
+           goal.current_want, advances[goal.choice].name, goal.want);
+    choose_tech_goal(pplayer, goal.choice);
   }
 }
Index: common/government.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/government.h,v
retrieving revision 1.26
diff -u -r1.26 government.h
--- common/government.h 30 Jun 2003 20:53:24 -0000      1.26
+++ common/government.h 25 May 2004 04:58:31 -0000
@@ -157,8 +157,7 @@
 };
 
 /* This should possibly disappear; we don't bother sending these to client;
-   see code in ai_city.c: ai_manage_cities() for what they mean...
-*/
+ * See code in aitech.c for what the fields mean */
 struct ai_gov_tech_hint {
   int tech;
   int turns_factor;

[Prev in Thread] Current Thread [Next in Thread]
  • [freeciv-ai] (PR#8832) Cleaning ai/aitech.c, Gregory Berkolaiko <=