Complete.Org: Mailing Lists: Archives: freeciv-dev: January 2005:
[Freeciv-Dev] (PR#11907) a better init_min_production
Home

[Freeciv-Dev] (PR#11907) a better init_min_production

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [Freeciv-Dev] (PR#11907) a better init_min_production
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Wed, 12 Jan 2005 14:01:07 -0800
Reply-to: bugs@xxxxxxxxxxx

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

This patch rewrites init_min_production.

- First of all I removed the generic_city_refresh along with the backup 
city.  This is just removing a hack that's no longer necessary (because 
generic_city_refresh is already called at the start of cm_query_result).

- Secondly I removed the hard-coded cases for food and shields min 
production.  Instead there's a loop over output type.  This is more 
extensible: for instance it will work with a food bonus or a trade bonus.

-  pcity->usage[] is used instead of calculating usage manually.  This 
is shorter and more accurate (pcity->usage[] wasn't available when this 
code was first written).

- Min productions are set for all output types.  This is because of the 
loop of course, and it potentially allows pruning based on gold.  To 
make this accurate however we have to subtract off all "free" sources of 
production: not only the city center but also city tithes and trade 
routes.  This is potentially buggy since it's reproducing a large part 
of the city.c code.

One point of interest is the division by the bonus.  This used to be 
done using floating-point math, adding on 0.1 (10%) to the bonus ratio. 
  But even such a small value makes a huge difference when you're 
looking at a food bonus (the old code didn't handle a food bonus).  To 
get really effective pruning you need to be accurate to the nearest 
integer here.  The new code just does integer division and rounds down, 
which I believe is the correct thing to do.

In the single measurement I did this dropped the CM runtime from 30% to 
29% of the server runtime - statistically insignificant.  Pruning by 
gold is useless for the server CM (which has -infinity for the gold 
surplus) but could be helpful for the client CM (which has 0 for the 
default gold surplus...although it should probably have -infinity also).

This code is potentially bug-prone.  Benoit, you should look at it 
closely (or rewrite it to your liking).  The two main goals of this 
patch are to allow a food-bonus and to use pcity->usage[] instead of 
manually calculated usage[].  But I ended up pretty much rewriting the 
whole function.

-jason

Index: common/aicore/cm.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/cm.c,v
retrieving revision 1.58
diff -u -r1.58 cm.c
--- common/aicore/cm.c  7 Jan 2005 02:59:39 -0000       1.58
+++ common/aicore/cm.c  12 Jan 2005 21:51:47 -0000
@@ -1509,67 +1509,45 @@
 ****************************************************************************/
 static void init_min_production(struct cm_state *state)
 {
-  int x = CITY_MAP_RADIUS, y = CITY_MAP_RADIUS;
-  int usage[O_COUNT];
+  const int x = CITY_MAP_RADIUS, y = CITY_MAP_RADIUS;
   struct city *pcity = state->pcity;
   bool is_celebrating = base_city_celebrating(pcity);
-  struct city backup;
 
-  /* make sure the city's numbers make sense (sometimes they don't,
-   * somehow) */
-  memcpy(&backup, pcity, sizeof(*pcity));
-  generic_city_refresh(pcity, FALSE, NULL);
+  /* We used to call generic_city_refresh here, but that's no longer
+   * necessary since it's called at the start of cm_query_result. */
 
   memset(state->min_production, 0, sizeof(state->min_production));
+  output_type_iterate(o) {
+    /* Calculate minimum output.  This assumes no waste.  Pruning
+     * is done mainly based on minimum production, so we want to find the
+     * absolute highest minimum possible.  However if the minimum we find
+     * here is incorrectly too high, then the algorithm will fail. */
+    int min;
+
+    /* 1.  Calculate the minimum final production that is needed.
+     * 2.  Divide by the bonus (rounding down) to get the minimum citizen
+     *     production that is needed.
+     * 3.  Subtract off any "free" production (trade routes, tithes, and
+     *     city-center). */
+    min = pcity->usage[o] + state->parameter.minimal_surplus[o];
+    min = min * 100 / pcity->bonus[o];
+    if (o == O_TRADE) {
+      int i;
 
-  /* If the city is content, then we know the food usage is just
-   * prod-surplus; otherwise, we know it's at least 2*size but we
-   * can't easily compute the settlers. */
-  if (!city_unhappy(pcity)) {
-    usage[O_FOOD] = pcity->prod[O_FOOD] - pcity->surplus[O_FOOD];
-  } else {
-    usage[O_FOOD] = pcity->size * 2;
-  }
-  state->min_production[O_FOOD] = usage[O_FOOD]
-    + state->parameter.minimal_surplus[O_FOOD]
-    - base_city_get_output_tile(x, y, pcity, is_celebrating, O_FOOD);
-
-  /* surplus = (factories-waste) * production - shield_usage, so:
-   *   production = (surplus + shield_usage)/(factories-waste)
-   * waste >= 0, so:
-   *   production >= (surplus + usage)/factories
-   * Solving with surplus >= min_surplus, we get:
-   *   production >= (min_surplus + usage)/factories
-   * 'factories' is the pcity->bonus[O_SHIELD]/100.  Increase it a bit to avoid
-   * rounding errors.
-   *
-   * pcity->prod[O_SHIELD] = (factories-waste) * production.
-   * Therefore, shield_usage = pcity->prod[O_SHIELD] - pcity->shield_surplus
-   */
-  if (!city_unhappy(pcity)) {
-    double sbonus;
-
-    usage[O_SHIELD] = pcity->prod[O_SHIELD] - pcity->surplus[O_SHIELD];
-
-    sbonus = ((double)pcity->bonus[O_SHIELD]) / 100.0;
-    sbonus += .1;
-    state->min_production[O_SHIELD]
-      = ((usage[O_SHIELD] + state->parameter.minimal_surplus[O_SHIELD])
-        / sbonus);
-    state->min_production[O_SHIELD]
-      -= base_city_get_output_tile(x, y, pcity, is_celebrating, O_SHIELD);
-  } else {
-    /* Dunno what the usage is, so it's pointless to set the
-     * min_production */
-    usage[O_SHIELD] = 0;
-    state->min_production[O_SHIELD] = 0;
-  }
-
-  /* we should be able to get a min_production on gold and trade, too;
-     also, lux, if require_happy, but not otherwise */
+      for (i = 0; i < NUM_TRADEROUTES; i++) {
+       min -= pcity->trade_value[i];
+      }
+    } else if (o == O_GOLD) {
+      min -= get_city_tithes_bonus(pcity);
+    }
+    if (is_free_worked_tile(x, y)) {
+      min -= base_city_get_output_tile(x, y, pcity, is_celebrating, o);
+    }
+    state->min_production[o] = min;
+  } output_type_iterate_end;
 
-  /* undo any effects from the refresh */
-  memcpy(pcity, &backup, sizeof(*pcity));
+  /* We could get a minimum on luxury if we knew how many luxuries were
+   * needed to make us content. */
 }
 
 /****************************************************************************

[Prev in Thread] Current Thread [Next in Thread]
  • [Freeciv-Dev] (PR#11907) a better init_min_production, Jason Short <=