Complete.Org: Mailing Lists: Archives: freeciv-dev: April 2005:
[Freeciv-Dev] (PR#12734) quick&dirty CM v5
Home

[Freeciv-Dev] (PR#12734) quick&dirty CM v5

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [Freeciv-Dev] (PR#12734) quick&dirty CM v5
From: "Per I. Mathisen" <per@xxxxxxxxxxx>
Date: Mon, 11 Apr 2005 14:34:20 -0700
Reply-to: bugs@xxxxxxxxxxx

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

This patch is still quite raw. Look in server/cityturn.c to know what it
is doing before you mess around with it.

CHANGES:
 - Generalized the priorities in which it will soft fail. Added API for
   this to CM parameter.
 - Luxury need calculation did not take into account citizens made happy
   by effects. Now it does.
 - The ugly distribute() calls are gone, replaced by a reverse lookup
   table for distribute(), so that we can query what amount of trade we
   need to achieve for a given minimum of a secondary output.
 - We can now use trade instead of a secondary output rather accurately.
 - We take specialists from unhappy citizens when this is possible.
 - Refactored code a little bit.
 - Addressed a number of points that Benoit made.
 - It now does much less stupid things. For smallish cities (less than 8
   in size), it almost always does the same as CM does, given the same CM
   parameter. For larger cities, they diverge rapidly, with quick&dirty
   favouring food and other high-minimum outputs much more highly.

TODO:
 - Encapsulate the reverse lookup table so that we can handle queries over
   the limit (breaking it down into multiple queries).
 - Even more testing.
 - Try to do away with the need to run a full city refresh first, for
   server. This actually takes about 4-9 times as long as the quick&dirty
   CM itself.
 - Make code less ugly...
 - Move it somewhere sane.

  - Per

Index: common/city.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/city.c,v
retrieving revision 1.325
diff -u -r1.325 city.c
--- common/city.c       10 Apr 2005 23:55:24 -0000      1.325
+++ common/city.c       11 Apr 2005 21:19:53 -0000
@@ -2641,3 +2643,419 @@
   unit_list_free(pcity->units_supported);
   free(pcity);
 }
+
+/**************************************************************************
+  Calculate necessary luxury to achieve goals for this city.
+
+  We ignore here the fact that specialists may be taken from unhappy 
+  citizens when we no longer have any content ones left.
+**************************************************************************/
+int city_luxury_need(struct city *pcity, bool order, bool celebrate)
+{
+  struct player *pplayer = city_owner(pcity);
+  int result = 0;
+  int happy, content, unhappy, angry;
+
+  if (order || celebrate) {
+    /* We need to figure out how much luxuries we need. */
+    citizen_base_mood(pplayer, 0, &happy, &content, &unhappy, &angry, 
+                      pcity->size);
+    citizen_content_buildings(pcity, &content, &unhappy, &angry);
+    citizen_happy_units(pcity, &happy, &content, &unhappy, &angry);
+    citizen_happy_wonders(pcity, &happy, &content, &unhappy, &angry);
+  }
+  if (order || celebrate) {
+    /* Find out how much luxuries we need to make everyone content. */
+    result = MAX((angry * 2 + unhappy - happy) * HAPPY_COST, 0);
+  }
+  if (celebrate) {
+    /* Find out how much extra luxuries we will need to make enough
+     * citizens happy that we can celebrate. */
+    result += MAX(((pcity->size - happy + 1) / 2) * HAPPY_COST, 0);
+  }
+
+  return result;
+}
+
+/****************************** Fast CM **********************************/
+
+#define REVERSE_LIMIT 100
+#define GCALOG(...) if (pcity->debug) { freelog(LOG_NORMAL, __VA_ARGS__); }
+
+static const int SCIENCE = 0, TAX = 1, LUXURY = 2;
+static int reverse_calc[3][REVERSE_LIMIT];
+
+BV_DEFINE(bv_fail, O_MAX);
+
+struct best_result {
+  int value, x, y, sp;
+  int output[O_MAX];
+};
+
+/**************************************************************************
+  Check if our requirements hold up. If so, copy temp to best. We allow
+  either of trade or the secondary outputs meet our criteria.
+**************************************************************************/
+static void greedy_copy_if_better(struct player *pplayer, 
+                                  bv_fail fail_reqs, struct best_result *temp, 
+                                  struct best_result *best)
+{
+  /* There is enough trade, so it does not matter if we fail the 
+   * secondary outputs directly. These are already counted as part of 
+   * trade if tax rates are higher than zero for this output. */
+  if (!BV_ISSET(fail_reqs, O_TRADE)
+      && pplayer->economic.luxury > 0) {
+    BV_CLR(fail_reqs, O_LUXURY);
+  }
+  if (!BV_ISSET(fail_reqs, O_TRADE)
+      && pplayer->economic.science > 0) {
+    BV_CLR(fail_reqs, O_SCIENCE);
+  }
+  if (!BV_ISSET(fail_reqs, O_TRADE)
+      && pplayer->economic.gold > 0) {
+    BV_CLR(fail_reqs, O_GOLD);
+  }
+
+  /* If we can get the secondary output minimums anyhow, we do not
+   * need trade, since trade is just a means to secondary output. */
+  if (BV_ISSET(fail_reqs, O_TRADE) 
+      && !BV_ISSET(fail_reqs, O_LUXURY)
+      && !BV_ISSET(fail_reqs, O_GOLD)
+      && !BV_ISSET(fail_reqs, O_SCIENCE)) {
+    BV_CLR(fail_reqs, O_TRADE);
+  }
+
+   /* If we fail none of our minimums, copy if higher. */
+   if (!BV_ISSET_ANY(fail_reqs) && temp->value > best->value) {
+     *best = *temp;
+   }
+}
+
+/**************************************************************************
+  Calculate reverse tax lookups. This is needed for the fast CM.
+**************************************************************************/
+static void city_precompute_taxes(struct player *pplayer)
+{
+  int i;
+  static int reverse_rates[3] = { -1, -1, -1 };
+
+  if (reverse_rates[SCIENCE] == pplayer->economic.science
+      && reverse_rates[LUXURY] == pplayer->economic.luxury) {
+    return; /* has not changed */
+  }
+
+  reverse_rates[SCIENCE] = pplayer->economic.science;
+  reverse_rates[LUXURY] = pplayer->economic.luxury;
+  reverse_rates[TAX] = 100 - reverse_rates[SCIENCE] - reverse_rates[LUXURY];
+  memset(reverse_calc, 0, sizeof(reverse_calc));
+
+  /* We run it in reverse to get the smallest possible amount of
+   * trade that gives us what we want, since we overwrite previous 
+   * results. */
+  for (i = REVERSE_LIMIT - 1; i > 0 ; i--) {
+    int result[3];
+
+    distribute(i, 3, reverse_rates, result);
+    assert(result[SCIENCE] <= i);
+    assert(result[LUXURY] <= i);
+    assert(result[TAX] <= i);
+    reverse_calc[SCIENCE][result[SCIENCE]] = i;
+    reverse_calc[LUXURY][result[LUXURY]] = i;
+    reverse_calc[TAX][result[TAX]] = i;
+  }
+}
+
+/**************************************************************************
+  This function implements a greedy citizen allocation algorithm. It
+  is meant to be very fast, but not optimal.
+
+  The basic algorithm is as follows: Figure out (through pcity->usage[]
+  and some luxury magic) what our needs are, and place one tile at a
+  time, each aiming to produce to its share of what is necessary for
+  the whole city's needs.
+
+  We employ a number of tricks to make this work. First we try to delay
+  the necessary picks, so that perhaps picks by cmp->factor that are done
+  first may solve our needs by accident. Second, if we cannot find all
+  the requirements for a placement, we try to swap out requirements
+  for this placement with that of the next (push_retry). This solves
+  a number of 'deadlock'-like situations. Third, we drop requirements
+  as we detect situations we cannot handle, starting with gold, then
+  either food or luxuries, and lastly just dropping all requirements.
+
+  It is crucial to note that we do _not_ attempt to find a solution
+  where we end up with negative shields. The human user is expected
+  to solve these himself, and usually we end up throwing the city into 
+  disorder. Through disorder this problem is made patently obvious. 
+  Similarly for the AI, its emergency management code is meant to solve 
+  situations like this, which can try to solve it by rehoming units, 
+  for example.
+
+  We treat trade as a special case: We reverse the distribution of
+  secondary output from trade to get how much trade we need in order
+  to satisfy our secondary output minimums. We can either satisfy
+  our trade demand or our secondary minimum demand, given that we
+  have tax settings that allow us to do such conversions.
+
+  If we can take specialists from unhappy citizens (number of 
+  base content is citizens is zero), we do so instead of grabbing
+  luxuries.
+
+  There are some problems with this approach, however. First, we treat
+  the secondary outputs (gold, science, luxuries) as a linear 
+  accumulation, which they are not. Second, our calculation of city 
+  effects on outputs is done on the gradual accumulation of outputs,
+  which is also not precise. Third, some pathological cases, 
+  especially where we can only avoid disorder by _not_ picking the 
+  most desirable tiles first, get us in trouble we cannot solve.
+
+  Not that we never give up - this function always returns a valid 
+  result.
+
+  cmr->surplus does not return the correct result for secondary
+  outputs due to the special way we handle them here.
+
+  To debug, use "/debug city x y" (server-side usage only).
+
+  All cities passed to this function must have the following data
+  (and only the following data) up to date refreshed and valid:
+    * pcity->usage[]
+    * pcity->tile_output[][][]
+    * pcity->surplus[]
+    * pcity->city_map[][]
+    * pcity->size
+    * pcity->bonus
+
+  TODO:
+   - arrange only one or some citizens method (treat existing as 
+     free tiles? and use no. new instead of city size in for loop?)
+   - for future tiles, increase food condition by FOOD_COST for each
+**************************************************************************/
+void greedy_citizen_arrange(struct city *pcity, const struct cm_parameter *cmp,
+                            struct cm_result *cmr)
+{
+  struct player *pplayer = city_owner(pcity);
+  int minimums_priority = 0;                  /* minimums we will satisfy */
+  int minimum_stats[O_COUNT];                 /* total requirements */
+  int requirements[O_COUNT][pcity->size];     /* requirements by citizen */
+  int sums[O_COUNT];                          /* accumulated outputs */
+  bool taken[CITY_MAP_SIZE][CITY_MAP_SIZE];   /* taken worker positions */
+  int push_retry = 0;                         /* push requirements forward? */
+  double bonus[O_COUNT];                      /* multipliers from effects */
+  int i, specialists = 0;
+  int content = content_citizens(pplayer);
+  int factor[O_COUNT];
+
+  memcpy(factor, cmp->factor, sizeof(factor));
+
+  /* Trade cannot be set to zero here. */
+  factor[O_TRADE] = MAX(factor[O_TRADE], 
+                        MAX(factor[O_LUXURY], 
+                            MAX(factor[O_GOLD],
+                                factor[O_SCIENCE])));
+
+  /* Check secondary output reverse cache */
+  city_precompute_taxes(pplayer);
+
+  for (i = 0; i < O_COUNT && cmp->minimums_priority[i] != O_LAST; i++) {
+    minimums_priority++;
+  }
+
+  /*** Find minimums. ***/
+
+  output_type_iterate(o) {
+    minimum_stats[o] = pcity->usage[o];
+    bonus[o] = pcity->bonus[o] / 100;
+  } output_type_iterate_end;
+  minimum_stats[O_LUXURY] += 
+    city_luxury_need(pcity, !cmp->allow_disorder, cmp->require_happy);
+  minimum_stats[O_TRADE] += reverse_calc[SCIENCE][minimum_stats[O_SCIENCE]];
+  minimum_stats[O_TRADE] += reverse_calc[LUXURY][minimum_stats[O_LUXURY]];
+  minimum_stats[O_TRADE] += reverse_calc[TAX][minimum_stats[O_GOLD]];
+
+  /* Distribute workload among citizens. We try to put off most of the
+   * work as late as possible (for last citizens considered) in the hope
+   * that earlier labour will give us what we want anyway. */
+  memset(requirements, 0, sizeof(requirements));
+  output_type_iterate(o) {
+    int load = minimum_stats[o];
+
+    while (load > 0) {
+      for (i = pcity->size - 1; i >= 0 && load > 0; i--) {
+        requirements[o][i]++;
+        load--;
+      }
+    }
+  } output_type_iterate_end;
+
+  /*** Try to satisfy found minimums. ***/
+
+  /* Clear vars */
+  memset(sums, 0, sizeof(sums));
+  memset(cmr, 0, sizeof(*cmr));
+  city_map_iterate(x, y) {
+    taken[x][y] = FALSE;
+    if (!is_free_worked_tile(x, y)) {
+      cmr->worker_positions_used[x][y] = FALSE;
+    } else {
+      cmr->worker_positions_used[x][y] = TRUE;
+    }
+  } city_map_iterate_end;
+
+  /* Add city center (and other free tiles) */
+  city_map_iterate(x, y) {
+    /* Query: Can a free tile ever be unavailable in the future? */
+    /* Assumption: That is not very likely. In any case, checking for
+     * this may cause more problems than it is worth. */
+    if (is_free_worked_tile(x, y)) {
+      output_type_iterate(o) {
+        sums[o] += pcity->tile_output[x][y][o];
+        taken[x][y] = TRUE;
+      } output_type_iterate_end;
+    }
+  } city_map_iterate_end;
+
+  for (i = 0; i < pcity->size; i++) {
+    struct best_result best;
+
+    best.value = -1;
+    do {
+      /* Find best city tile */
+      city_map_iterate(x, y) {
+        bv_fail fail_reqs;      /* do we satisfy current requirements? */
+        struct best_result temp;
+
+        if (taken[x][y] || pcity->city_map[x][y] == C_TILE_UNAVAILABLE) {
+          continue;
+        }
+        BV_CLR_ALL(fail_reqs);
+        output_type_iterate(o) {
+          temp.output[o] = pcity->tile_output[x][y][o];
+        } output_type_iterate_end;
+        temp.value = 0;
+        output_type_iterate(o) {
+          if (temp.output[o] * bonus[o] < requirements[o][i] 
+              && sums[o] * bonus[o] < minimum_stats[o]) {
+            GCALOG("%s: pick %d, tile(%d, %d) not satisfy %s (want %d, got %d;"
+                   " sum got %g, sum want %d)",
+                   pcity->name, i, x, y, get_output_name(o), 
requirements[o][i],
+                   temp.output[o], sums[o] * bonus[o], minimum_stats[o]);
+            BV_SET(fail_reqs, o);
+          }
+          temp.value += temp.output[o] * factor[o];
+        } output_type_iterate_end;
+        temp.x = x;
+        temp.y = y;
+        temp.sp = -1;
+        greedy_copy_if_better(pplayer, fail_reqs, &temp, &best);
+      } city_map_iterate_end;
+
+      /* Find better specialist, if any */
+      specialist_type_iterate(sp) {
+        if (city_can_use_specialist(pcity, sp)) {
+          bv_fail fail_reqs;      /* do we satisfy current requirements? */
+          struct best_result temp;
+
+          BV_CLR_ALL(fail_reqs);
+          output_type_iterate(o) {
+            temp.output[o] = game.rgame.specialists[sp].bonus[o];
+          } output_type_iterate_end;
+          if (specialists + 1 > content) {
+            /* We have an opportunity to take a specialist out of the
+             * ranks of the unhappy! This counts as if we had lux here. */
+            temp.output[O_LUXURY] += HAPPY_COST;
+          }
+          temp.value = 0;
+          output_type_iterate(o) {
+            if (temp.output[o] * bonus[o] < requirements[o][i]
+                && sums[o] * bonus[o] < minimum_stats[o]) {
+              BV_SET(fail_reqs, o);
+            }
+            temp.value += temp.output[o] * factor[o];
+          } output_type_iterate_end;
+          temp.x = -1;
+          temp.y = -1;
+          temp.sp = sp;
+          greedy_copy_if_better(pplayer, fail_reqs, &temp, &best);
+        }
+      } specialist_type_iterate_end;
+
+      /* Now we may or may not have a best candidate. If we don't, see
+       * if we can relax the requirements a bit and try again. Do not
+       * push twice in a row, though. */
+      if (best.value == -1) {
+        if (push_retry == 0 && i < pcity->size - 1) {
+          /* Try to push all other requirements ahead of us if we can,
+           * this solves some deadlock situations. We push our req from
+           * the next requirements level back, though, to avoid it next 
+           * time. */
+          enum output_type chosen_req = O_LAST;
+          output_type_iterate(o) {
+            if (requirements[o][i] > 0
+                && sums[o] * bonus[o] < minimum_stats[o]
+                && chosen_req == O_LAST) {
+              chosen_req = o;
+              requirements[o][i] += requirements[o][i + 1];
+              requirements[o][i + 1] = 0;
+            } else if (requirements[o][i] > 0) {
+              requirements[o][i + 1] += requirements[o][i];
+              requirements[o][i] = 0;
+            }
+          } output_type_iterate_end;
+          push_retry += 2; /* now don't try this next citizen */
+          GCALOG("%s: pushed other reqs than %s ahead at %d", pcity->name, 
+                 get_output_name(chosen_req), i);
+        } else if (minimums_priority > 0) {
+          /* Relax least prioritized minimum */
+          minimum_stats[cmp->minimums_priority[minimums_priority - 1]] = 
-FC_INFINITY;
+          GCALOG("%s: relaxed %s req at %d (priority %d)", pcity->name, 
+                 get_output_name(cmp->minimums_priority[minimums_priority - 
1]),
+                 i, minimums_priority);
+          minimums_priority--;
+        } else {
+          /* Rulesets should always have a specialist as a fallback. */
+          die("Neither usable tile nor specialist found for %s. Error in "
+              "ruleset?", pcity->name);
+        }
+      }
+    } while (best.value == -1);
+
+    /* Fill cmr with best result and move on to next citizen */
+    if (best.sp >= 0) {
+      cmr->specialists[best.sp]++;
+      specialists++;
+    } else {
+      cmr->worker_positions_used[best.x][best.y] = TRUE;
+      taken[best.x][best.y] = TRUE;
+    }
+    output_type_iterate(o) {
+      sums[o] += best.output[o];
+    } output_type_iterate_end;
+    output_type_iterate(o) {
+      GCALOG("%s pick %d (%d,%d|%d), here %d/%d, total %d/%d, of %s (p%d)",
+              pcity->name, i, best.x, best.y, best.sp, 
+              best.output[o], requirements[o][i], sums[o], minimum_stats[o], 
+              get_output_name(o), minimums_priority);
+    } output_type_iterate_end;
+    if (push_retry > 0) {
+      push_retry--;
+    }
+  }
+
+  output_type_iterate(o) {
+    cmr->surplus[o] = (sums[o] * bonus[o]) - pcity->usage[o];
+  } output_type_iterate_end;
+  /* Make an educated guess - ignoring external constraints */
+  if (minimum_stats[O_LUXURY] <= sums[O_LUXURY] && cmp->require_happy) {
+    cmr->happy = TRUE;
+  } else {
+    cmr->happy = FALSE;
+  }
+  if (minimum_stats[O_LUXURY] <= sums[O_LUXURY] 
+      && (!cmp->allow_disorder || cmp->require_happy)) {
+    cmr->disorder = FALSE;
+  } else {
+    cmr->happy = TRUE;
+  }
+  cmr->found_a_valid = TRUE; /* Always */
+}
Index: common/city.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/city.h,v
retrieving revision 1.204
diff -u -r1.204 city.h
--- common/city.h       10 Apr 2005 21:53:30 -0000      1.204
+++ common/city.h       11 Apr 2005 21:19:53 -0000
@@ -525,6 +526,11 @@
 int city_pollution_types(const struct city *pcity, int shield_total,
                         int *pollu_prod, int *pollu_pop, int *pollu_mod);
 int city_pollution(const struct city *pcity, int shield_total);
+int city_luxury_need(struct city *pcity, bool order, bool celebrate);
+
+/* city management */
+void greedy_citizen_arrange(struct city *pcity, const struct cm_parameter *cmp,
+                            struct cm_result *cmr);
 
 /*
  * Iterates over all improvements which are built in the given city.
Index: common/fc_types.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/fc_types.h,v
retrieving revision 1.17
diff -u -r1.17 fc_types.h
--- common/fc_types.h   29 Mar 2005 12:28:26 -0000      1.17
+++ common/fc_types.h   11 Apr 2005 21:19:53 -0000
@@ -54,6 +54,9 @@
 struct tile;
 struct unit;
 
+struct cm_parameter;
+struct cm_result;
+
 #define SP_MAX 20
 #define MAX_NUM_REQS 2
 
Index: common/aicore/cm.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/cm.c,v
retrieving revision 1.62
diff -u -r1.62 cm.c
--- common/aicore/cm.c  7 Apr 2005 04:36:02 -0000       1.62
+++ common/aicore/cm.c  11 Apr 2005 21:19:54 -0000
@@ -1859,6 +1859,7 @@
   output_type_iterate(stat) {
     dest->minimal_surplus[stat] = 0;
     dest->factor[stat] = 1;
+    dest->minimums_priority[stat] = O_LAST;
   } output_type_iterate_end;
 
   dest->happy_factor = 1;
@@ -1876,6 +1877,7 @@
   output_type_iterate(stat) {
     dest->minimal_surplus[stat] = -FC_INFINITY;
     dest->factor[stat] = 1;
+    dest->minimums_priority[stat] = O_LAST;
   } output_type_iterate_end;
 
   dest->happy_factor = 1;
Index: common/aicore/cm.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/cm.h,v
retrieving revision 1.13
diff -u -r1.13 cm.h
--- common/aicore/cm.h  3 Dec 2004 09:39:41 -0000       1.13
+++ common/aicore/cm.h  11 Apr 2005 21:19:54 -0000
@@ -26,6 +26,7 @@
  * the weighted sum over the surplus of each type.
  */
 
+#include "fc_types.h"
 #include "city.h"              /* CITY_MAP_SIZE */
 #include "shared.h"            /* bool type */
 
@@ -38,6 +39,9 @@
 
   int factor[O_MAX];
   int happy_factor;
+
+  /* The priority in which we will attempt to achieve surplus minimums. */
+  Output_type_id minimums_priority[O_MAX];
 };
 
 /* A result which can examined. */
Index: server/cityturn.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/cityturn.c,v
retrieving revision 1.300
diff -u -r1.300 cityturn.c
--- server/cityturn.c   18 Mar 2005 11:26:24 -0000      1.300
+++ server/cityturn.c   11 Apr 2005 21:19:54 -0000
@@ -34,6 +34,7 @@
 #include "support.h"
 #include "tech.h"
 #include "unit.h"
+#include "timing.h"
 
 #include "citytools.h"
 #include "gamelog.h"
@@ -145,7 +146,7 @@
   Rearrange workers according to a cm_result struct.  The caller must make
   sure that the result is valid.
 **************************************************************************/
-void apply_cmresult_to_city(struct city *pcity, struct cm_result *cmr)
+void apply_cmresult_to_city(struct city *pcity, const struct cm_result *cmr)
 {
   /* The caller had better check this! */
   if (!cmr->found_a_valid) {
@@ -173,6 +174,7 @@
   } specialist_type_iterate_end;
 }
 
+#if 1
 /**************************************************************************
   You need to call sync_cities so that the affected cities are synced with 
   the client.
@@ -181,6 +183,14 @@
 {
   struct cm_parameter cmp;
   struct cm_result cmr;
+  struct cm_result cmr2;
+  static struct timer *t1 = NULL;
+  static struct timer *t2 = NULL;
+
+  if (t1 == NULL) {
+    t1 = new_timer(TIMER_CPU, TIMER_ACTIVE);
+    t2 = new_timer(TIMER_CPU, TIMER_ACTIVE);
+  }
 
   /* See comment in freeze_workers(): we can't rearrange while
    * workers are frozen (i.e. multiple updates need to be done). */
@@ -210,6 +220,7 @@
   cmp.require_happy = FALSE;
   cmp.allow_disorder = FALSE;
   cmp.allow_specialists = TRUE;
+  cmp.search_type = SEARCH_FIRST_SUFFICIENT_SOLUTION;
 
   /* We used to look at pplayer->ai.xxx_priority to determine the values
    * to be used here.  However that doesn't work at all because those values
@@ -226,12 +237,17 @@
     cmp.factor[O_FOOD] = 20;
   }
   cmp.factor[O_SHIELD] = 5;
-  cmp.factor[O_TRADE] = 0; /* Trade only provides gold/science. */
+  cmp.factor[O_TRADE] = 0;
   cmp.factor[O_GOLD] = 2;
-  cmp.factor[O_LUXURY] = 0; /* Luxury only influences happiness. */
+  cmp.factor[O_LUXURY] = 0;
   cmp.factor[O_SCIENCE] = 2;
   cmp.happy_factor = 0;
 
+  cmp.minimums_priority[0] = O_LUXURY;
+  cmp.minimums_priority[1] = O_SHIELD;
+  cmp.minimums_priority[2] = O_FOOD;
+  cmp.minimums_priority[3] = O_GOLD;
+
   cmp.minimal_surplus[O_FOOD] = 1;
   cmp.minimal_surplus[O_SHIELD] = 1;
   cmp.minimal_surplus[O_TRADE] = 0;
@@ -239,8 +255,14 @@
   cmp.minimal_surplus[O_LUXURY] = 0;
   cmp.minimal_surplus[O_SCIENCE] = 0;
 
-  cm_query_result(pcity, &cmp, &cmr);
+  /* TEST */
+  start_timer(t2);
+  city_refresh(pcity);
+  greedy_citizen_arrange(pcity, &cmp, &cmr2);
+  stop_timer(t2);
 
+  start_timer(t1);
+  cm_query_result(pcity, &cmp, &cmr);
   if (!cmr.found_a_valid) {
     /* Drop surpluses and try again. */
     cmp.minimal_surplus[O_FOOD] = 0;
@@ -266,14 +288,114 @@
     cm_init_emergency_parameter(&cmp);
     cm_query_result(pcity, &cmp, &cmr);
   }
+  stop_timer(t1);
   assert(cmr.found_a_valid);
+  assert(cmr2.found_a_valid);
 
-  apply_cmresult_to_city(pcity, &cmr);
+  {
+    static int sum1 = 0, sum2 = 0;
+    struct ai_data *ai = ai_data_get(city_owner(pcity));
+    int total1, total2 = 0;
+
+    /* Compare */
+    apply_cmresult_to_city(pcity, &cmr);
+    city_refresh(pcity);
+    sanity_check_city(pcity);
+    total1 = ai_eval_calc_city(pcity, ai);
+
+    apply_cmresult_to_city(pcity, &cmr2);
+    city_refresh(pcity);
+    sanity_check_city(pcity);
+    total2 = ai_eval_calc_city(pcity, ai);
+
+    sum1 += total1 / 40;
+    sum2 += total2 / 40;
+    if (total1 != total2) {
+    freelog(LOG_NORMAL, "%s: CM=%d (%g)[%d], greed=%d (%g)[%d]", pcity->name,
+            total1, read_timer_seconds(t1), sum1, total2, 
read_timer_seconds(t2), 
+            sum2);
+//    cm_print_result(pcity, &cmr);
+//    cm_print_result(pcity, &cmr2);
+    }
+  }
+}
+#else
+/**************************************************************************
+  You need to call sync_cities so that the affected cities are synced with 
+  the client.
+**************************************************************************/
+void auto_arrange_workers(struct city *pcity)
+{
+  struct cm_parameter cmp;
+  struct cm_result cmr;
+
+  /* See comment in freeze_workers(): we can't rearrange while
+   * workers are frozen (i.e. multiple updates need to be done). */
+  if (pcity->server.workers_frozen > 0) {
+    pcity->server.needs_arrange = TRUE;
+    return;
+  }
+
+  /* Freeze the workers and make sure all the tiles around the city
+   * are up to date.  Then thaw, but hackishly make sure that thaw
+   * doesn't call us recursively, which would waste time. */
+  city_freeze_workers(pcity);
+  pcity->server.needs_arrange = FALSE;
 
+  map_city_radius_iterate(pcity->tile, ptile) {
+    update_city_tile_status_map(pcity, ptile);
+  } map_city_radius_iterate_end;
+
+  pcity->server.needs_arrange = FALSE;
+  city_thaw_workers(pcity);
+
+  /* Now start actually rearranging. */
   sanity_check_city(pcity);
 
+  cm_init_parameter(&cmp);
+  cmp.require_happy = FALSE;
+  cmp.allow_disorder = FALSE;
+  cmp.minimums_priority[0] = O_LUXURY;
+  cmp.minimums_priority[1] = O_FOOD;
+  cmp.minimums_priority[2] = O_SHIELD;
+  cmp.minimums_priority[3] = O_GOLD;
+
+  /* We used to look at pplayer->ai.xxx_priority to determine the values
+   * to be used here.  However that doesn't work at all because those values
+   * are on a different scale.  Later the ai may wish to adjust its
+   * priorities - this should be done via a separate set of variables. */
+  if (pcity->size > 1) {
+    if (pcity->size <= game.notradesize) {
+      cmp.factor[O_FOOD] = 15;
+    } else {
+      cmp.factor[O_FOOD] = 10;
+    }
+  } else {
+    /* Growing to size 2 is the highest priority. */
+    cmp.factor[O_FOOD] = 20;
+  }
+  cmp.factor[O_SHIELD] = 5;
+  cmp.factor[O_TRADE] = 0;
+  cmp.factor[O_GOLD] = 2;
+  cmp.factor[O_LUXURY] = 0;
+  cmp.factor[O_SCIENCE] = 2;
+  cmp.happy_factor = 0;
+
   city_refresh(pcity);
+
+  greedy_citizen_arrange(pcity, &cmp, &cmr);
+  apply_cmresult_to_city(pcity, &cmr);
+  city_refresh(pcity);
+  output_type_iterate(o) {
+    /* Compare actual results to calculated results */
+    if (pcity->surplus[o] != cmr.surplus[o]) {
+      freelog(LOG_NORMAL, "%s::%s imagined %d, actual %d", pcity->name, 
+              get_output_name(o), cmr.surplus[o], pcity->surplus[o]);
+    }
+  } output_type_iterate_end;
+  sanity_check_city(pcity);
 }
+#endif
 
 /**************************************************************************
 Notices about cities that should be sent to all players.
Index: server/cityturn.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/cityturn.h,v
retrieving revision 1.36
diff -u -r1.36 cityturn.h
--- server/cityturn.h   3 Sep 2004 04:22:37 -0000       1.36
+++ server/cityturn.h   11 Apr 2005 21:19:54 -0000
@@ -25,7 +25,7 @@
 void global_city_refresh(struct player *pplayer); /* tax/govt changed */
 
 void auto_arrange_workers(struct city *pcity); /* will arrange the workers */
-void apply_cmresult_to_city(struct city *pcity, struct cm_result *cmr);
+void apply_cmresult_to_city(struct city *pcity, const struct cm_result *cmr);
 
 bool city_reduce_size(struct city *pcity, int pop_loss);
 void send_global_city_turn_notifications(struct conn_list *dest);

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