Complete.Org: Mailing Lists: Archives: freeciv-dev: April 2005:
[Freeciv-Dev] (PR#12734) A new, fast and greedy CM implementation
Home

[Freeciv-Dev] (PR#12734) A new, fast and greedy CM implementation

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [Freeciv-Dev] (PR#12734) A new, fast and greedy CM implementation
From: "Per I. Mathisen" <per@xxxxxxxxxxx>
Date: Wed, 6 Apr 2005 16:04:08 -0700
Reply-to: bugs@xxxxxxxxxxx

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

I was tired of slow AI games, so I decided to handle the root of all evil
- the city management code. I wrote my own CM from scratch, and stuffed it
in the bottom of city.c. At approx 300 lines of code, it is not very big.

In a very difficult, very large savegame that I used for reference, CM did
26% better in finding optimal solutions according to the scoring test I
made. The greedy function was, however, 154 times faster.

Yes, you read that right. It really is blazing fast. Playing on huge maps
with lots and lots of cities under AI control is a rather different
experience with this patch.

If you want to measure for yourself, change "#if 0" to "#if 1" in
server/cityturn.c; if you want more comments on the design, see the huge
function header comment on the greedy function itself.

Enjoy.

  - Per

Index: common/city.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/city.c,v
retrieving revision 1.323
diff -u -r1.323 city.c
--- common/city.c       3 Apr 2005 20:19:51 -0000       1.323
+++ common/city.c       6 Apr 2005 22:59:39 -0000
@@ -1780,33 +1780,30 @@
 /**************************************************************************
   Create content, unhappy and angry citizens.
 **************************************************************************/
-static void citizen_base_mood(struct city *pcity,
+static void citizen_base_mood(struct player *pplayer, int specialists,
                              int *happy, int *content,
-                             int *unhappy, int *angry)
+                             int *unhappy, int *angry, int size)
 {
-  /* Number of specialists in city */
-  int specialists = city_specialists(pcity);
-
   /* This is the number of citizens that may start out content, depending
    * on empire size and game's city unhappysize. This may be bigger than
    * the size of the city, since this is a potential. */
-  int base_content = content_citizens(city_owner(pcity));
+  int base_content = content_citizens(pplayer);
 
   /* Create content citizens. Take specialists from their ranks. */
-  *content = MAX(0, MIN(pcity->size, base_content) - specialists);
+  *content = MAX(0, MIN(size, base_content) - specialists);
 
   /* Create angry citizens only if we have a negative number of possible
    * content citizens. This happens when empires grow really big. */
   if (game.angrycitizen == FALSE) {
     *angry = 0;
   } else {
-    *angry = MIN(MAX(0, -base_content), pcity->size - specialists);
+    *angry = MIN(MAX(0, -base_content), size - specialists);
   }
 
   /* Create unhappy citizens. In the beginning, all who are not content,
    * specialists or angry are unhappy. This is changed by luxuries and 
    * buildings later. */
-  *unhappy = (pcity->size - specialists - *content - *angry);
+  *unhappy = (size - specialists - *content - *angry);
 
   /* No one is born happy. */
   *happy = 0;
@@ -2132,7 +2129,7 @@
 
   /* Add base amounts for building upkeep and citizen consumption. */
   pcity->usage[O_GOLD] += city_building_upkeep(pcity, O_GOLD);
-  pcity->usage[O_FOOD] += 2 * pcity->size;
+  pcity->usage[O_FOOD] += FOOD_COST * pcity->size;
 
   /*
    * If you modify anything here these places might also need updating:
@@ -2246,6 +2243,7 @@
                          void (*send_unit_info) (struct player * pplayer,
                                                  struct unit * punit))
 {
+  struct player *pplayer = city_owner(pcity);
   int prev_tile_trade = pcity->citizen_base[O_TRADE];
 
   if (full_refresh) {
@@ -2255,8 +2253,9 @@
   }
   get_citizen_output(pcity, pcity->citizen_base); /* Calculate output from 
citizens. */
   set_city_production(pcity);
-  citizen_base_mood(pcity, &pcity->ppl_happy[0], &pcity->ppl_content[0],
-                    &pcity->ppl_unhappy[0], &pcity->ppl_angry[0]);
+  citizen_base_mood(pplayer, city_specialists(pcity), &pcity->ppl_happy[0], 
+                    &pcity->ppl_content[0], &pcity->ppl_unhappy[0], 
+                    &pcity->ppl_angry[0], pcity->size);
   pcity->pollution = city_pollution(pcity, pcity->prod[O_SHIELD]);
   citizen_happy_luxury(pcity); /* with our new found luxuries */
   happy_copy(pcity, 1);
@@ -2620,3 +2619,327 @@
   unit_list_free(pcity->units_supported);
   free(pcity);
 }
+
+/**************************************************************************
+  This function implements a greedy citizen allocation algorithm. It
+  is meant to be ultrafast, somewhat but not completely optimal.
+
+  The basic algorithm is as follows: Figure out (through pcity->usage[]
+  and some luxury magic) what are needs are, and place one city 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 desire 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 by throwing the city into 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.
+
+  It is many, many times faster than CM.
+
+  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, in some pathological cases, 
+  especially where we can only get a stable regime by _not_ picking
+  the mest desirable tiles first, get us in trouble we cannot solve.
+
+  Not that we ever give up - this function always returns a valid 
+  result.
+
+  To debug, use "/debug city x y" (server-side usage only).
+
+  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)
+#define GCALOG(...) if (pcity->debug) { freelog(LOG_NORMAL, __VA_ARGS__); }
+{
+  /* We would rather have food than luxuries; this is not always true,
+   * we may want to starve the city rather than let it continue to riot.
+   * This does not hold true for shields, though, since we are no more
+   * likely to be able to carry the upkeep if we shrink. */
+  bool food_first = FALSE; /* DEBUG ... set to TRUE for normal cmp */
+  /* If we fail to satisfy all our minimums, we try to drop O_GOLD and
+   * try again. Then we try to drop either O_FOOD or O_TRADE, depending
+   * on above variable, and try again. If that fails, we give up. At 
+   * repeated == 0, we no longer look at minimums. */
+  int repeated = 3;
+  int minimum_stats[O_LAST], i, happy, content, unhappy, angry;
+  int requirements[O_LAST][pcity->size];
+  int sums[O_LAST];
+  bool taken[CITY_MAP_SIZE][CITY_MAP_SIZE];
+  int rates[3];
+  const int SCIENCE = 0, TAX = 1, LUXURY = 2;
+  struct player *pplayer = city_owner(pcity);
+  int push_retry = 0;
+  int tile_outputs[CITY_MAP_SIZE][CITY_MAP_SIZE][O_LAST];
+
+  rates[SCIENCE] = pplayer->economic.science;
+  rates[LUXURY] = pplayer->economic.luxury;
+  rates[TAX] = 100 - rates[SCIENCE] - rates[LUXURY];
+
+  /* Cache */
+  city_map_iterate(x, y) {
+    if (pcity->city_map[x][y] != C_TILE_UNAVAILABLE) {
+      output_type_iterate(o) {
+        tile_outputs[x][y][o] = base_city_get_output_tile(x, y, pcity, FALSE, 
o);
+      } output_type_iterate_end;
+    }
+  } city_map_iterate_end;
+
+  /*** Step one: Find minimums. ***/
+
+  if (!cmp->allow_disorder || cmp->require_happy) {
+    /* 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 (cmp->minimal_surplus[O_FOOD] < 0) {
+    food_first = FALSE;
+  }
+  output_type_iterate(o) {
+    minimum_stats[o] = pcity->usage[o];
+  } output_type_iterate_end;
+  if (!cmp->allow_disorder || cmp->require_happy) {
+    /* Find out how much luxuries we need to keep city content. 
+     * This ignores the fact that specialists may be taken from
+     * unhappy citizens when we no longer have any content ones
+     * left. We should ideally never get there. */
+    GCALOG("%s: needs %d luxuries to be content (%d unhappy, %d angry)", 
+           pcity->name, (angry * 2 + unhappy) * HAPPY_COST, unhappy, angry);
+    minimum_stats[O_LUXURY] += (angry * 2 + unhappy) * HAPPY_COST;
+  }
+  if (cmp->require_happy) {
+    /* Find out how much extra luxuries we will need to make enough
+     * citizens happy that we can celebrate. */
+    GCALOG("%s: wants to celebrate, needs %d luxuries extra", 
+           pcity->name, ((pcity->size + 1) / 2) * HAPPY_COST);
+    minimum_stats[O_LUXURY] += ((pcity->size + 1) / 2) * HAPPY_COST;
+  }
+  /* 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;
+
+  /*** Step two: Try to satisfy minimums. ***/
+
+  /* Clear vars */
+  output_type_iterate(o) {
+    sums[o] = 0;
+    cmr->surplus[o] = 0;
+  } output_type_iterate_end;
+  specialist_type_iterate(sp) {
+    cmr->specialists[sp] = 0;
+  } specialist_type_iterate_end;
+  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] += tile_outputs[x][y][o];
+        taken[x][y] = TRUE;
+      } output_type_iterate_end;
+    }
+  } city_map_iterate_end;
+
+  for (i = 0; i < pcity->size; i++) {
+    int best = -1, bestx = -1, besty = -1, bestsp = -1;
+    int best_out[O_LAST]; /* remember output in case we use them */
+
+    do {
+      city_map_iterate(x, y) {
+        bool satisfy_reqs = TRUE;
+        int total = 0;
+        int temp[O_LAST];
+        int result[3];
+
+        if (taken[x][y] || pcity->city_map[x][y] == C_TILE_UNAVAILABLE) {
+          continue;
+        }
+        output_type_iterate(o) {
+          temp[o] = tile_outputs[x][y][o];
+        } output_type_iterate_end;
+        distribute(temp[O_TRADE], 3, rates, result);
+        temp[O_SCIENCE] += result[SCIENCE];
+        temp[O_LUXURY] += result[LUXURY];
+        temp[O_GOLD] += result[TAX];
+        output_type_iterate(o) {
+          if (repeated > 0
+              && sums[o] * pcity->bonus[o] / 100 < minimum_stats[o]
+              && temp[o] < requirements[o][i]) {
+            GCALOG("%s: tile (%d, %d) does not satisfy %s(%d,%d<%d,%d<%d)",
+                   pcity->name, x, y, get_output_name(o), repeated, 
+                   sums[o] * pcity->bonus[o] / 100, 
+                   minimum_stats[o], temp[o], requirements[o][i]);
+            satisfy_reqs = FALSE;
+            break;
+          }
+          total += temp[o] * cmp->factor[o];
+        } output_type_iterate_end;
+        if (satisfy_reqs && total > best) {
+          bestx = x;
+          besty = y;
+          bestsp = -1;
+          best = total;
+          memcpy(best_out, temp, sizeof(best_out));
+        }
+      } city_map_iterate_end;
+      specialist_type_iterate(sp) {
+        if (city_can_use_specialist(pcity, sp)) {
+          int total = 0;
+          bool satisfy_reqs = TRUE;
+          int temp[O_LAST];
+          int result[3];
+
+          output_type_iterate(o) {
+            temp[o] = game.rgame.specialists[sp].bonus[o];
+          } output_type_iterate_end;
+          /* Calculate lux, gold and science as best as we can */
+          distribute(temp[O_TRADE], 3, rates, result);
+          temp[O_SCIENCE] += result[SCIENCE];
+          temp[O_LUXURY] += result[LUXURY];
+          temp[O_GOLD] += result[TAX];
+          output_type_iterate(o) {
+            if (repeated > 0
+                && sums[o] * pcity->bonus[o] / 100 < minimum_stats[o]
+                && temp[o] < requirements[o][i]) {
+              satisfy_reqs = FALSE;
+              break;
+            }
+            total += temp[o] * cmp->factor[o];
+          } output_type_iterate_end;
+          if (satisfy_reqs && total > best) {
+            bestx = -1;
+            besty = -1;
+            bestsp = sp;
+            best = total;
+            memcpy(best_out, temp, sizeof(best_out));
+          }
+        }
+      } 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 == -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] * pcity->bonus[o] / 100 < 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 (repeated == 3) {
+          minimum_stats[O_GOLD] = -1;
+          GCALOG("%s: relaxed gold req at %d", pcity->name, i);
+        } else if (repeated == 2) {
+          if (food_first) {
+            minimum_stats[O_LUXURY] = -1;
+            GCALOG("%s: relaxed lux req at %d", pcity->name, i);
+          } else {
+            minimum_stats[O_FOOD] = -1;
+            GCALOG("%s: relaxed food req at %d", pcity->name, i);
+          }
+        } else if (repeated == 1) {
+          /* Set repeated to zero, this shorts out any attempt to fulfill
+           * our obligations. */
+          GCALOG("%s: relaxed all reqs at %d", pcity->name, i);
+        } else {
+          /* Rulesets should always have a specialist as a fallback. */
+          die("Neither usable tile nor specialist found. Error in ruleset?");
+        }
+        repeated--;
+      }
+    } while (best == -1);
+
+    /* Fill cmr with best result and move on to next citizen */
+    if (bestsp >= 0) {
+      cmr->specialists[bestsp]++;
+    } else {
+      cmr->worker_positions_used[bestx][besty] = TRUE;
+      taken[bestx][besty] = TRUE;
+    }
+    output_type_iterate(o) {
+      sums[o] += best_out[o];
+    } output_type_iterate_end;
+    output_type_iterate(o) {
+      GCALOG("%s chose (%d,%d|%d), has %d of %s (total %d, minimum %d,"
+              " req %d for %d)", pcity->name, bestx, besty, bestsp, 
best_out[o], 
+              get_output_name(o), sums[o], minimum_stats[o], 
requirements[o][i], i);
+    } output_type_iterate_end;
+    if (push_retry > 0) {
+      push_retry--;
+    }
+  }
+
+  output_type_iterate(o) {
+    cmr->surplus[o] = (sums[0] * pcity->bonus[o] / 100) - 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.203
diff -u -r1.203 city.h
--- common/city.h       26 Mar 2005 05:59:14 -0000      1.203
+++ common/city.h       6 Apr 2005 22:59:39 -0000
@@ -18,6 +18,9 @@
 #include "unit.h"              /* struct unit_list */
 #include "worklist.h"
 
+struct cm_parameter;
+struct cm_result;
+
 enum production_class_type {
   TYPE_UNIT, TYPE_NORMAL_IMPROVEMENT, TYPE_WONDER
 };
@@ -86,6 +89,9 @@
 /* Cost in luxuries to make one citizen happier by one level. */
 #define HAPPY_COST 2
 
+/* Cost in food to feed one citizen. */
+#define FOOD_COST 2
+
 /* Iterate a city map, from the center (the city) outwards */
 extern struct iter_index {
   int dx, dy, dist;
@@ -523,6 +529,10 @@
                         int *pollu_prod, int *pollu_pop, int *pollu_mod);
 int city_pollution(const struct city *pcity, int shield_total);
 
+/* 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: 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   6 Apr 2005 22:59:40 -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 0
 /**************************************************************************
   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). */
@@ -239,8 +249,13 @@
   cmp.minimal_surplus[O_LUXURY] = 0;
   cmp.minimal_surplus[O_SCIENCE] = 0;
 
-  cm_query_result(pcity, &cmp, &cmr);
+  /* TEST */
+  start_timer(t2);
+  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 +281,105 @@
     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;
+    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);
+  }
+}
+#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.allow_specialists = TRUE;
+
+  /* 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; /* Trade only provides gold/science. */
+  cmp.factor[O_GOLD] = 2;
+  cmp.factor[O_LUXURY] = 0; /* Luxury only influences happiness. */
+  cmp.factor[O_SCIENCE] = 2;
+  cmp.happy_factor = 0;
+
+  cmp.minimal_surplus[O_FOOD] = 1;
+  cmp.minimal_surplus[O_SHIELD] = 1;
+  cmp.minimal_surplus[O_TRADE] = 0;
+  cmp.minimal_surplus[O_GOLD] = -FC_INFINITY;
+  cmp.minimal_surplus[O_LUXURY] = 0;
+  cmp.minimal_surplus[O_SCIENCE] = 0;
+
+  greedy_citizen_arrange(pcity, &cmp, &cmr);
+  apply_cmresult_to_city(pcity, &cmr);
   city_refresh(pcity);
+  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   6 Apr 2005 22:59:40 -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]
  • [Freeciv-Dev] (PR#12734) A new, fast and greedy CM implementation, Per I. Mathisen <=