Complete.Org: Mailing Lists: Archives: freeciv-dev: May 2003:
[Freeciv-Dev] (PR#4335) CM in server
Home

[Freeciv-Dev] (PR#4335) CM in server

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [Freeciv-Dev] (PR#4335) CM in server
From: "Per I. Mathisen" <per@xxxxxxxxxxx>
Date: Sat, 31 May 2003 08:45:00 -0700
Reply-to: rt@xxxxxxxxxxxxxx

I made a frenzied attempt to implement CM in the server. This is the
result. (I coded around the previously reported bug in CM.)

A lot of code went bye-bye. I rewrote the tax code. It is _much_ cleaner
but I also removed some functionality that never really worked well - most
importantly the AI no longer makes any attempt to use luxuries or grow by
rapture, and it doesn't use 'maxbuycost'. I also removed the
can-use-100%-taxrate cheat - once we get the new settlers code in and
ferries to ship them, we won't need it.

First - is it slower? Hard to say exactly, since I had to change so much
other code. But it does not look that way. It might even make it faster.
This is also hard to say for sure, since the test I made with the new tax
code grafted onto the old code was such a crude hack that it might not
actually be entirely sane.

Anyway, numbers:

Autogame w/ new patch:
real    0m17.019s
user    0m16.150s
sys     0m0.040s

Autogame w/ old code + new tax code:
real    0m20.595s
user    0m19.340s
sys     0m0.050s

Autogame w/ old code:
real    0m18.705s
user    0m17.690s
sys     0m0.080s

I think this should move the speed concern out of the way. Although
Raimar should look at my use of CM's cache to see if it can be improved (I
basically remake the cache every time we rearrange workers).

Second - is it better? Overall and on its own, it is slightly better,
though not by much.

I use the same tests as for the new settlers patch:

Turns to republic:
                         1  2  3  4  5
Old code                49 70 61 51 60
New code (no tax cheat) 55 75 74 62 68
New code (+tax cheat)   41 66 59 48 65
Old code + new settler  51 53 51 48 42
New code + new settler  55 57 53 56 50 (no tax cheat)
New code + new settler  44 43 42 41 39 (+tax cheat)

(Compare first and last lines above!)

It seems the old code was already well optimized. I fooled around with the
CM parameters until I found something that seemed to work well - it can
probably be optimized a lot more. More importantly, it should be combined
with the aidata priorities - once we do this, we have a powerful tool to
change AI behaviour on the fly.

So it gets almost the same speed and results as the present code. The big
win is that using CM makes the code much more 'reflexive' and a lot
cleaner. By 'reflexive' I mean that we can now change some parameters
(eventually aidata priorities) and all AI behaviour everywhere will follow
suit. Once we get this right, we can add grow-by-rapture code that doesn't
suck, different settings for different kinds of cities and things like
'war', 'we have future tech' and 'Republic NOW' modes.

Of course, as the table shows, once we combine this patch with the new
settlers patch, the synergy effect is big.

  - Per

Index: ai/aicity.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aicity.c,v
retrieving revision 1.140
diff -u -r1.140 aicity.c
--- ai/aicity.c 2003/05/30 18:50:59     1.140
+++ ai/aicity.c 2003/05/31 14:23:32
@@ -55,6 +55,7 @@
 
 #include "aicity.h"
 
+static void resolve_city_emergency(struct player *pplayer, struct city *pcity);
 static void ai_manage_city(struct player *pplayer, struct city *pcity);
 
 /**************************************************************************
@@ -509,6 +510,10 @@
   pplayer->ai.maxbuycost = 0;
 
   city_list_iterate(pplayer->cities, pcity)
+    if (CITY_EMERGENCY(pcity)) {
+      /* Fix critical shortages or unhappiness */
+      resolve_city_emergency(pplayer, pcity);
+    }
     ai_manage_city(pplayer, pcity);
   city_list_iterate_end;
 
@@ -696,185 +701,8 @@
 static void ai_manage_city(struct player *pplayer, struct city *pcity)
 {
   auto_arrange_workers(pcity);
-  if (ai_fix_unhappy(pcity) && ai_fuzzy(pplayer, TRUE))
-    ai_scientists_taxmen(pcity);
   ai_sell_obsolete_buildings(pcity);
   sync_cities();
-/* ai_city_choose_build(pplayer, pcity); -- moved by Syela */
-}
-
-/**************************************************************************
-...
-**************************************************************************/
-static int ai_find_elvis_pos(struct city *pcity, int *xp, int *yp)
-{
-  struct government *g = get_gov_pcity(pcity);
-  int foodneed, prodneed, luxneed, pwr, e, worst_value = 0;
-
-  foodneed=(pcity->size *2) + settler_eats(pcity);
-  foodneed -= pcity->food_prod; /* much more robust now -- Syela */
-  prodneed = 0;
-  prodneed -= pcity->shield_prod;
-  luxneed = 2 * (2 * pcity->ppl_angry[4] + pcity->ppl_unhappy[4] -
-                pcity->ppl_happy[4]);
-  pwr = 2 * city_tax_bonus(pcity) / 100;
-  e = (luxneed + pwr - 1) / pwr;
-  if (e > 1) {
-    foodneed += (e - 1) * 2;
-    prodneed += (e - 1);
-  } /* not as good as elvising all at once, but should be adequate */
-
-  unit_list_iterate(pcity->units_supported, punit)
-    prodneed += utype_shield_cost(unit_type(punit), g);
-  unit_list_iterate_end;
-  
-  prodneed -= citygov_free_shield(pcity, g);
-
-  *xp = 0;
-  *yp = 0;
-  city_map_iterate(x, y) {
-    if (is_city_center(x, y))
-      continue; 
-    if (get_worker_city(pcity, x, y) == C_TILE_WORKER) {
-      int value = city_tile_value(pcity, x, y,
-                                 foodneed + city_get_food_tile(x, y, pcity),
-                                 prodneed + city_get_shields_tile(x, y,
-                                                                  pcity));
-
-      if ((*xp == 0 && *yp == 0) || value < worst_value) {
-       *xp = x;
-       *yp = y;
-       worst_value = value;
-      }
-    }
-  } city_map_iterate_end;
-  if (*xp == 0 && *yp == 0) return 0;
-  foodneed += city_get_food_tile(*xp, *yp, pcity);
-  prodneed += city_get_shields_tile(*xp, *yp, pcity);
-  if (e > 1) {
-    foodneed -= (e - 1) * 2; /* forgetting these two lines */
-    prodneed -= (e - 1); /* led to remarkable idiocy -- Syela */
-  }
-  if (foodneed > pcity->food_stock) {
-    freelog(LOG_DEBUG,
-           "No elvis_pos in %s - would create famine.", pcity->name);
-    return 0; /* Bad time to Elvis */
-  }
-  if (prodneed > 0) {
-    freelog(LOG_DEBUG,
-           "No elvis_pos in %s - would fail-to-upkeep.", pcity->name);
-    return 0; /* Bad time to Elvis */
-  }
-  return(city_tile_value(pcity, *xp, *yp, foodneed, prodneed));
-}
-
-/**************************************************************************
-...
-**************************************************************************/
-int ai_make_elvis(struct city *pcity)
-{
-  int xp, yp, val;
-  if ((val = ai_find_elvis_pos(pcity, &xp, &yp)) != 0) {
-    server_remove_worker_city(pcity, xp, yp);
-    pcity->ppl_elvis++;
-    city_refresh(pcity); /* this lets us call ai_make_elvis in luxury routine 
*/
-    return val; /* much more useful! */
-  } else
-    return 0;
-}
-
-/**************************************************************************
-...
-**************************************************************************/
-static void make_elvises(struct city *pcity)
-{
-  int xp, yp, elviscost;
-  pcity->ppl_elvis += (pcity->ppl_taxman + pcity->ppl_scientist);
-  pcity->ppl_taxman = 0;
-  pcity->ppl_scientist = 0;
-  city_refresh(pcity);
- 
-  while (TRUE) {
-    if ((elviscost = ai_find_elvis_pos(pcity, &xp, &yp)) != 0) {
-      int food = city_get_food_tile(xp, yp, pcity);
-
-      if (food > pcity->food_surplus)
-       break;
-      if (food == pcity->food_surplus && city_happy(pcity))
-       break; /* scientists don't party */
-      if (elviscost >= 24) /* doesn't matter if we wtbb or not! */
-        break; /* no benefit here! */
-      server_remove_worker_city(pcity, xp, yp);
-      pcity->ppl_elvis++;
-      city_refresh(pcity);
-    } else
-      break;
-  }
-    
-}
-
-/**************************************************************************
-...
-**************************************************************************/
-static void make_taxmen(struct city *pcity)
-{
-  while (!city_unhappy(pcity) && pcity->ppl_elvis > 0) {
-    pcity->ppl_taxman++;
-    pcity->ppl_elvis--;
-    city_refresh(pcity);
-  }
-  if (city_unhappy(pcity)) {
-    pcity->ppl_taxman--;
-    pcity->ppl_elvis++;
-    city_refresh(pcity);
-  }
-
-}
-
-/**************************************************************************
-...
-**************************************************************************/
-static void make_scientists(struct city *pcity)
-{
-  make_taxmen(pcity); /* reuse the code */
-  pcity->ppl_scientist = pcity->ppl_taxman;
-  pcity->ppl_taxman = 0;
-}
-
-/**************************************************************************
- we prefer science, but if both is 0 we prefer $ 
- (out of research goal situation)
-**************************************************************************/
-void ai_scientists_taxmen(struct city *pcity)
-{
-  int science_bonus, tax_bonus;
-  make_elvises(pcity);
-  if (pcity->ppl_elvis == 0 || city_unhappy(pcity)) 
-    return;
-  tax_bonus = city_tax_bonus(pcity);
-  science_bonus = city_science_bonus(pcity);
-  
-  if (tax_bonus > science_bonus || ai_wants_no_science(city_owner(pcity))) {
-    make_taxmen(pcity);
-  } else {
-    make_scientists(pcity);
-  }
-
-  sync_cities();
-}
-
-/**************************************************************************
-...
-**************************************************************************/
-bool ai_fix_unhappy(struct city *pcity)
-{
-  if (!city_unhappy(pcity))
-    return TRUE;
-  while (city_unhappy(pcity)) {
-    if(ai_make_elvis(pcity) == 0) break;
-/*     city_refresh(pcity);         moved into ai_make_elvis for utility -- 
Syela */
-  }
-  return (!city_unhappy(pcity));
 }
 
 /**************************************************************************
@@ -896,9 +724,9 @@
   Syela is wrong. It happens quite too often, mostly due to unhappiness.
   Also, most of the time we are unable to resolve the situation. 
 **************************************************************************/
-void emergency_reallocate_workers(struct player *pplayer, struct city *pcity)
+static void resolve_city_emergency(struct player *pplayer, struct city *pcity)
 #define LOG_EMERGENCY LOG_DEBUG
-{ 
+{
   struct city_list minilist;
   char report[500];
 
@@ -916,8 +744,8 @@
 
     if (acity && acity != pcity && acity->owner == pcity->owner)  {
       if (same_pos(acity->x, acity->y, x, y)) {
-       /* can't stop working city center */
-       continue;
+        /* can't stop working city center */
+        continue;
       }
       freelog(LOG_DEBUG, "%s taking over %s's square in (%d, %d)",
               pcity->name, acity->name, x, y);
@@ -931,14 +759,6 @@
     }
   } map_city_radius_iterate_end;
   auto_arrange_workers(pcity);
-  if (ai_fix_unhappy(pcity) && ai_fuzzy(pplayer, TRUE)) {
-    ai_scientists_taxmen(pcity);
-  }
-  if (pcity->shield_surplus >= 0 && !city_unhappy(pcity)
-      && pcity->food_stock + pcity->food_surplus >= 0) {
-    freelog(LOG_EMERGENCY, "%s resolved without disbanding", report);
-    goto cleanup;
-  }
 
   unit_list_iterate(pcity->units_supported, punit) {
     if (city_unhappy(pcity)
@@ -951,25 +771,19 @@
       /* in rare cases the _safe might be needed? --dwp */
       handle_unit_disband_safe(pplayer, &pack, &myiter);
       city_refresh(pcity);
-      (void) ai_fix_unhappy(pcity);
     }
   } unit_list_iterate_end;
 
-  if (pcity->shield_surplus >= 0 && !city_unhappy(pcity)
-      && pcity->food_stock + pcity->food_surplus >= 0) {
-    freelog(LOG_EMERGENCY, "%s resolved by disbanding unit(s)", report);
-  } else {
+  if (CITY_EMERGENCY(pcity)) {
     freelog(LOG_EMERGENCY, "%s remains unresolved", report);
+  } else {
+    freelog(LOG_EMERGENCY, "%s resolved by disbanding unit(s)", report);
   }
 
-  cleanup:
   city_list_iterate(minilist, acity) {
     /* otherwise food total and stuff was wrong. -- Syela */
     city_refresh(acity);
     auto_arrange_workers(pcity);
-    if (ai_fix_unhappy(acity) && ai_fuzzy(pplayer, TRUE)) {
-      ai_scientists_taxmen(acity);
-    }
   } city_list_iterate_end;
 
   sync_cities();
Index: ai/aicity.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aicity.h,v
retrieving revision 1.18
diff -u -r1.18 aicity.h
--- ai/aicity.h 2002/12/21 11:44:00     1.18
+++ ai/aicity.h 2003/05/31 14:23:32
@@ -15,6 +15,10 @@
 
 #include "unit.h"              /* enum unit_move_type */
 
+#define CITY_EMERGENCY(pcity)                        \
+ (pcity->shield_surplus < 0 || city_unhappy(pcity)   \
+  || pcity->food_stock + pcity->food_surplus < 0)    \
+
 struct player;
 struct city;
 struct ai_choice;
@@ -28,10 +32,6 @@
 Unit_Type_id ai_choose_defender_by_type(struct city *pcity,
                                         enum unit_move_type which);
 Unit_Type_id ai_choose_defender(struct city *pcity);
-int ai_make_elvis(struct city *pcity);
-void ai_scientists_taxmen(struct city *pcity);
-bool ai_fix_unhappy(struct city *pcity);
-void emergency_reallocate_workers(struct player *pplayer, struct city *pcity);
 
 enum ai_city_task { AICITY_NONE, AICITY_TECH, AICITY_TAX, AICITY_PROD};
 /* These are not used (well, except AICITY_NONE)  --dwp */
Index: ai/aidata.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidata.c,v
retrieving revision 1.16
diff -u -r1.16 aidata.c
--- ai/aidata.c 2003/05/29 13:44:38     1.16
+++ ai/aidata.c 2003/05/31 14:23:33
@@ -253,8 +253,13 @@
    * are all WAGs. */
   ai->food_priority = FOOD_WEIGHTING;
   ai->shield_priority = SHIELD_WEIGHTING;
-  ai->luxury_priority = 1;
-  ai->science_priority = TRADE_WEIGHTING;
+  if (ai_wants_no_science(pplayer)) {
+    ai->luxury_priority = 1;
+    ai->science_priority = TRADE_WEIGHTING;
+  } else {
+    ai->luxury_priority = TRADE_WEIGHTING;
+    ai->science_priority = 1;
+  }
   ai->gold_priority = TRADE_WEIGHTING;
   ai->happy_priority = 1;
   ai->unhappy_priority = TRADE_WEIGHTING; /* danger */
Index: ai/aihand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aihand.c,v
retrieving revision 1.82
diff -u -r1.82 aihand.c
--- ai/aihand.c 2003/05/15 16:02:00     1.82
+++ ai/aihand.c 2003/05/31 14:23:33
@@ -77,232 +77,53 @@
 }
 
 /**************************************************************************
-.. Set tax/science/luxury rates. Tax Rates > 40 indicates a crisis.
- total rewrite by Syela 
+  Set tax/science/luxury rates.
+
+  TODO: Audit the use of pplayer->ai.maxbuycost in the code elsewhere,
+  then add support for it here.
+
+  TODO: Add support for rapture, needs to be coordinated for entire
+  empire.
 **************************************************************************/
 static void ai_manage_taxes(struct player *pplayer) 
 {
-  struct government *g = get_gov_pplayer(pplayer);
-  int gnow = pplayer->economic.gold;
-  int trade = 0, m, n, i, expense = 0, tot;
-  int waste[40]; /* waste with N elvises */
-  int elvises[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-  int hhjj[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-  int cities = 0;
-  struct packet_unit_request pack;
-  struct city *incity; /* stay a while, until the night is over */
-  struct unit *defender;
-  int maxrate = 10;
-
-  if (ai_handicap(pplayer, H_RATES))
-    maxrate = get_government_max_rate(pplayer->government) / 10;
-
-  pplayer->economic.science += pplayer->economic.luxury;
-/* without the above line, auto_arrange does strange things we must avoid -- 
Syela */
-  pplayer->economic.luxury = 0;
+  int trade = 0, expense = 0;
+  int maxrate = get_government_max_rate(pplayer->government);
+
   city_list_iterate(pplayer->cities, pcity) 
-    cities++;
-    pcity->ppl_elvis = 0; pcity->ppl_taxman = 0; pcity->ppl_scientist = 0;
-    add_adjust_workers(pcity); /* less wasteful than auto_arrange, required */
-    city_refresh(pcity);
     trade += pcity->trade_prod * city_tax_bonus(pcity) / 100;
-    freelog(LOG_DEBUG, "%s has %d trade.", pcity->name, pcity->trade_prod);
     built_impr_iterate(pcity, id) {
       expense += improvement_upkeep(pcity, id);
     } built_impr_iterate_end;
-
   city_list_iterate_end;
-
   pplayer->ai.est_upkeep = expense;
 
-  if (trade == 0) { /* can't return right away - thanks for the evidence, Muzz 
*/
-    city_list_iterate(pplayer->cities, pcity) 
-      if (ai_fix_unhappy(pcity) && ai_fuzzy(pplayer, TRUE))
-        ai_scientists_taxmen(pcity);
-    city_list_iterate_end;
-    return; /* damn division by zero! */
-  }
-
-  pplayer->economic.luxury = 0;
-
-  city_list_iterate(pplayer->cities, pcity) {
-
-    /* this code must be ABOVE the elvises[] if SIMPLISTIC is off */
-    freelog(LOG_DEBUG, "Does %s want to be bigger? %d",
-                 pcity->name, wants_to_be_bigger(pcity));
-    if (government_has_flag(g, G_RAPTURE_CITY_GROWTH)
-       && pcity->size >= g->rapture_size && pcity->food_surplus > 0
-       && pcity->ppl_unhappy[4] == 0 && pcity->ppl_angry[4] == 0
-       && wants_to_be_bigger(pcity) && ai_fuzzy(pplayer, TRUE)) {
-      freelog(LOG_DEBUG, "%d happy people in %s",
-                   pcity->ppl_happy[4], pcity->name);
-      n = ((pcity->size/2) - pcity->ppl_happy[4]) * 20;
-      if (n > pcity->ppl_content[1] * 20) n += (n - pcity->ppl_content[1] * 
20);
-      m = ((((city_got_effect(pcity, B_GRANARY) ? 3 : 2) *
-            city_granary_size(pcity->size))/2) -
-           pcity->food_stock) * food_weighting(pcity->size);
-      freelog(LOG_DEBUG, "Checking HHJJ for %s, m = %d", pcity->name, m);
-      tot = 0;
-      for (i = 0; i <= 10; i++) {
-        if (pcity->trade_prod * i * city_tax_bonus(pcity) >= n * 100) {
-         if (tot == 0) freelog(LOG_DEBUG, "%s celebrates at %d.",
-                           pcity->name, i * 10);
-          hhjj[i] += (pcity->was_happy ? m : m/2);
-          tot++;
-        }
-      }
-    } /* hhjj[i] is (we think) the desirability of partying with lux = 10 * i 
*/
-/* end elevated code block */
-
-    /* need this much lux */
-    n = (2 * pcity->ppl_angry[4] + pcity->ppl_unhappy[4] -
-        pcity->ppl_happy[4]) * 20;
-
-/* this could be an unholy CPU glutton; it's only really useful when we need
-   lots of luxury, like with pcity->size = 12 and only a temple */
-
-    memset(waste, 0, sizeof(waste));
-    tot = pcity->food_prod * food_weighting(pcity->size) +
-            pcity->trade_prod * pcity->ai.trade_want +
-            pcity->shield_prod * SHIELD_WEIGHTING;
-
-    for (i = 1; i <= pcity->size; i++) {
-      m = ai_make_elvis(pcity);
-      if (m != 0) {
-        waste[i] = waste[i-1] + m;
-      } else {
-        while (i <= pcity->size) {
-          waste[i++] = tot;
-        }
-        break;
-      }
-    }
-    for (i = 0; i <= 10; i++) {
-      m = ((n * 100 - pcity->trade_prod * city_tax_bonus(pcity) * i + 1999) / 
2000);
-      if (m >= 0) elvises[i] += waste[m];
-    }
-  }
-  city_list_iterate_end;
-    
-  for (i = 0; i <= 10; i++) elvises[i] += (trade * i) / 10 * TRADE_WEIGHTING;
-  /* elvises[i] is the production + income lost to elvises with lux = i * 10 */
-  n = 0; /* default to 0 lux */
-  for (i = 1; i <= maxrate; i++) if (elvises[i] < elvises[n]) n = i;
-  /* two thousand zero zero party over it's out of time */
-  for (i = 0; i <= 10; i++) {
-    hhjj[i] -= (trade * i) / 10 * TRADE_WEIGHTING; /* hhjj is now our bonus */
-    freelog(LOG_DEBUG, "hhjj[%d] = %d for %s.", i, hhjj[i], pplayer->name);
-  }
-
-  m = n; /* storing the lux we really need */
-  pplayer->economic.luxury = n * 10; /* temporary */
-
-/* Less-intelligent previous versions of the follow equation purged. -- Syela 
*/
-  n = ((expense - gnow + cities + pplayer->ai.maxbuycost) * 10 + trade - 1) / 
trade;
-  if (n < 0) n = 0;
-  while (n > maxrate) n--; /* Better to cheat on lux than on tax -- Syela */
-  if (m > 10 - n) m = 10 - n;
-
-  freelog(LOG_DEBUG, "%s has %d trade and %d expense."
-         "  Min lux = %d, tax = %d", pplayer->name, trade, expense, m, n);
-
-/* Peter Schaefer points out (among other things) that in pathological cases
-(like expense == 468) the AI will try to celebrate with m = 10 and then abort 
*/
-
-  /* want to max the hhjj */
-  for (i = m; i <= maxrate && i <= 10 - n; i++)
-    if (hhjj[i] > hhjj[m] && (trade * (10 - i) >= expense * 10)) m = i;
-/* if the lux rate necessary to celebrate cannot be maintained, don't bother */
-  pplayer->economic.luxury = 10 * m;
-
   if (ai_wants_no_science(pplayer)) {
-    pplayer->economic.tax = 100 - pplayer->economic.luxury;
-    while (pplayer->economic.tax > maxrate * 10) {
-      pplayer->economic.tax -= 10;
-      pplayer->economic.luxury += 10;
+    pplayer->economic.tax = maxrate;
+    pplayer->economic.luxury = MIN(100 - pplayer->economic.tax, maxrate);
+    pplayer->economic.science = 100 - pplayer->economic.tax
+                                - pplayer->economic.luxury;
+  } else {
+    if (pplayer->economic.gold >= ai_gold_reserve(pplayer)) {
+      pplayer->economic.science = maxrate; /* Max research */
+    } else {
+      pplayer->economic.science = maxrate / 2; /* Refill tresury */
     }
-  } else { /* have to balance things logically */
-/* if we need 50 gold and we have trade = 100, need 50 % tax (n = 5) */
-/*  n = ((ai_gold_reserve(pplayer) - gnow - expense) ... I hate typos. -- 
Syela */
-    n = ((ai_gold_reserve(pplayer) - gnow + expense + cities) * 20 + (trade*2) 
- 1) / (trade*2);
-/* same bug here as above, caused us not to afford city walls we needed. -- 
Syela */
-    if (n < 0) n = 0; /* shouldn't allow 0 tax? */
-    while (n > 10 - (pplayer->economic.luxury / 10) || n > maxrate) n--;
-    pplayer->economic.tax = 10 * n;
-  }
-
-/* once we have tech_want established, can compare it to cash want here -- 
Syela */
-  pplayer->economic.science = 100 - pplayer->economic.tax - 
pplayer->economic.luxury;
-  while (pplayer->economic.science > maxrate * 10) {
-    pplayer->economic.tax += 10;
-    pplayer->economic.science -= 10;
+    pplayer->economic.tax = MIN(100 - pplayer->economic.science, maxrate);
+    pplayer->economic.luxury = 100 - pplayer->economic.tax
+                                - pplayer->economic.science;
+    /* Now adjust to get enough money to pay for expenses */
+    while (expense > (trade * pplayer->economic.tax / 100)
+           && pplayer->economic.science > 10
+           && pplayer->economic.tax < maxrate) {
+      pplayer->economic.tax += 10;
+      pplayer->economic.science -= 10;
+    }
   }
-
-  city_list_iterate(pplayer->cities, pcity) 
-    pcity->ppl_elvis = 0;
-    city_refresh(pcity);
-    add_adjust_workers(pcity);
-    city_refresh(pcity);
-    if (ai_fix_unhappy(pcity) && ai_fuzzy(pplayer, TRUE))
-      ai_scientists_taxmen(pcity);
-    if (pcity->shield_surplus < 0 || city_unhappy(pcity) ||
-        pcity->food_stock + pcity->food_surplus < 0) 
-       emergency_reallocate_workers(pplayer, pcity);
-    if (pcity->shield_surplus < 0) {
-      defender = NULL;
-      unit_list_iterate(pcity->units_supported, punit)
-        incity = map_get_city(punit->x, punit->y);
-        if (incity && pcity->shield_surplus < 0) {
-         /* Note that disbanding here is automatically safe (we don't
-          * need to use handle_unit_disband_safe()), because the unit is
-          * in a city, so there are no passengers to get disbanded. --dwp
-          */
-          if (incity == pcity) {
-            if (defender) {
-              if (unit_def_rating_basic(punit) <
-                  unit_def_rating_basic(defender)) {
-               freelog(LOG_VERBOSE, "Disbanding %s in %s",
-                       unit_type(punit)->name, pcity->name);
-                pack.unit_id = punit->id;
-                handle_unit_disband(pplayer, &pack);
-                city_refresh(pcity);
-              } else {
-               freelog(LOG_VERBOSE, "Disbanding %s in %s",
-                       unit_type(defender)->name, pcity->name);
-                pack.unit_id = defender->id;
-                handle_unit_disband(pplayer, &pack);
-                city_refresh(pcity);
-                defender = punit;
-              }
-            } else defender = punit;
-          } else if (incity->shield_surplus > 0) {
-            pack.unit_id = punit->id;
-            pack.city_id = incity->id;
-            handle_unit_change_homecity(pplayer, &pack);
-            city_refresh(pcity);
-           freelog(LOG_VERBOSE, "Reassigning %s from %s to %s",
-                   unit_type(punit)->name, pcity->name, incity->name);
-          }
-        } /* end if */
-      unit_list_iterate_end;
-      if (pcity->shield_surplus < 0) {
-        unit_list_iterate(pcity->units_supported, punit)
-          if (punit != defender && pcity->shield_surplus < 0) {
-           /* the defender MUST NOT be disbanded! -- Syela */
-           freelog(LOG_VERBOSE, "Disbanding %s's %s",
-                   pcity->name, unit_type(punit)->name);
-            pack.unit_id = punit->id;
-            handle_unit_disband_safe(pplayer, &pack, &myiter);
-            city_refresh(pcity);
-          }
-        unit_list_iterate_end;
-      }
-    } /* end if we can't meet payroll */
-    /* FIXME: this shouldn't be here, but in the server... */
-    send_city_info(city_owner(pcity), pcity);
-  city_list_iterate_end;
-
-  sync_cities();
+  freelog(LOG_DEBUG, "%s rates: science%d luxury%d tax%d expenses%d income%d",
+          pplayer->name, pplayer->economic.science,
+          pplayer->economic.luxury, pplayer->economic.tax,
+          expense, trade / 100 * pplayer->economic.tax);
 }
 
 /**************************************************************************
@@ -348,9 +169,6 @@
       city_list_iterate(pplayer->cities, acity) {
         generic_city_refresh(acity, TRUE, NULL);
         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);
@@ -391,9 +209,6 @@
     city_list_iterate(pplayer->cities, acity) {
       generic_city_refresh(acity, FALSE, NULL);
       auto_arrange_workers(acity);
-      if (ai_fix_unhappy(acity)) {
-        ai_scientists_taxmen(acity);
-      }
     } city_list_iterate_end;
     ai->govt_reeval = CLIP(5, city_list_size(&pplayer->cities), 20);
   }
Index: common/aicore/cm.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/cm.c,v
retrieving revision 1.8
diff -u -r1.8 cm.c
--- common/aicore/cm.c  2003/05/13 07:04:28     1.8
+++ common/aicore/cm.c  2003/05/31 14:23:33
@@ -1457,6 +1457,23 @@
     cm_clear_cache(pcity);
   }
   report_stats();
+
+#ifdef NEVER
+  if (result->found_a_valid) {
+    city_map_iterate(x, y) {
+      if (result->worker_positions_used[x][y]) {
+if (!(pcity->city_map[x][y] == C_TILE_EMPTY
+               || pcity->city_map[x][y] == C_TILE_WORKER)) {
+freelog(LOG_ERROR, "%s: using %d,%d", pcity->name, x, y);
+print_city(pcity);
+print_result(pcity, result);
+}
+        assert(pcity->city_map[x][y] == C_TILE_EMPTY
+               || pcity->city_map[x][y] == C_TILE_WORKER);
+      }
+    } city_map_iterate_end;
+  }
+#endif
 }
 
 /****************************************************************************
Index: server/cityhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/cityhand.c,v
retrieving revision 1.122
diff -u -r1.122 cityhand.c
--- server/cityhand.c   2003/05/05 12:11:13     1.122
+++ server/cityhand.c   2003/05/31 14:23:33
@@ -112,6 +112,7 @@
     break;
   }
 
+  sanity_check_city(pcity);
   city_refresh(pcity);
   send_city_info(pplayer, pcity);
 }
@@ -141,6 +142,7 @@
     notify_player_ex(pplayer, pcity->x, pcity->y, E_NOEVENT,
                     _("Game: You don't have a worker here.")); 
   }
+  sanity_check_city(pcity);
 }
 
 /**************************************************************************
@@ -180,6 +182,7 @@
   else 
     pcity->ppl_taxman--;
 
+  sanity_check_city(pcity);
   city_refresh(pcity);
   sync_cities();
 }
@@ -374,6 +377,7 @@
   change_build_target(pplayer, pcity, preq->build_id,
                      preq->is_build_id_unit_id, E_NOEVENT);
 
+  sanity_check_city(pcity);
   city_refresh(pcity);
   send_city_info(pplayer, pcity);
 }
Index: server/citytools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/citytools.c,v
retrieving revision 1.218
diff -u -r1.218 citytools.c
--- server/citytools.c  2003/05/06 05:06:07     1.218
+++ server/citytools.c  2003/05/31 14:23:33
@@ -942,6 +942,7 @@
     build_free_palace(pgiver, pcity->name);
   }
 
+  sanity_check_city(pcity);
   sync_cities();
 }
 
@@ -1034,6 +1035,8 @@
     if (!can_unit_continue_current_activity(punit))
       handle_unit_activity_request(punit, ACTIVITY_IDLE);
   } unit_list_iterate_end;
+
+  sanity_check_city(pcity);
 }
 
 /**************************************************************************
@@ -1945,6 +1948,7 @@
   case C_TILE_WORKER:
     if (!is_available) {
       server_set_tile_city(pcity, city_x, city_y, C_TILE_UNAVAILABLE);
+      pcity->ppl_elvis++; /* keep city sanity */
       add_adjust_workers(pcity); /* will place the displaced */
       city_refresh(pcity);
       send_city_info(NULL, pcity);
Index: server/cityturn.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/cityturn.c,v
retrieving revision 1.214
diff -u -r1.214 cityturn.c
--- server/cityturn.c   2003/05/31 12:45:23     1.214
+++ server/cityturn.c   2003/05/31 14:23:33
@@ -43,8 +43,11 @@
 #include "srv_main.h"
 #include "unittools.h"
 
+#include "cm.h"
+
 #include "advdomestic.h"
 #include "aicity.h"
+#include "ailog.h"
 #include "aitools.h"           /* for ai_advisor_choose_building/ai_choice */
 
 #include "cityturn.h"
@@ -67,17 +70,12 @@
 static void obsolete_building_test(struct city *pcity, int b1, int b2);
 static void pay_for_buildings(struct player *pplayer, struct city *pcity);
 
-static void sanity_check_city(struct city *pcity);
-
 static bool disband_city(struct city *pcity);
 
 static void define_orig_production_values(struct city *pcity);
 static void update_city_activity(struct player *pplayer, struct city *pcity);
 static void nullify_caravan_and_disband_plus(struct city *pcity);
 
-static void worker_loop(struct city *pcity, int *foodneed,
-                       int *prodneed, int *workers);
-
 static void advisor_choose_build(struct player *pplayer, struct city *pcity);
 
 /**************************************************************************
@@ -143,172 +141,103 @@
 }
 
 /**************************************************************************
-You need to call sync_cities for the affected cities to be synced with the
-client.
+  ... TODO: remove
 **************************************************************************/
-static void worker_loop(struct city *pcity, int *foodneed,
-                       int *prodneed, int *workers)
+void add_adjust_workers(struct city *pcity)
 {
-  int bx, by, best, cur;
-  int conflict[5][5];
-  int e, pwr, luxneed = 0; /* I should have thought of this earlier, it is so 
simple */
-
-  city_refresh(pcity);
-  luxneed = 2 * (2 * pcity->ppl_angry[4] + pcity->ppl_unhappy[4] -
-                pcity->ppl_happy[4]);
-  pwr = (2 * city_tax_bonus(pcity)) / 100;
-  luxneed += pwr * pcity->ppl_elvis;
-  if (luxneed < 0) luxneed = 0;
-
-  e = (luxneed + pwr - 1) / pwr;
-  if (e > (*workers - 1)) e = *workers - 1; /* stops the repeated emergencies. 
-- Syela */
-
-/* If I were real clever, I would optimize trade by luxneed and tax_bonus -- 
Syela */
-
-  *foodneed -= 2 * (*workers - 1 - e);
-  *prodneed -= (*workers - 1 - e);
-
-  freelog(LOG_DEBUG, "%s, %d workers, %d luxneed, %d e",
-         pcity->name, *workers, luxneed, e);
-  freelog(LOG_DEBUG, "%s, a4 %d u4 %d h4 %d pwr %d elv %d",
-         pcity->name, pcity->ppl_angry[4], pcity->ppl_unhappy[4],
-         pcity->ppl_happy[4], pwr, pcity->ppl_elvis);
-
-  if (city_happy(pcity) && wants_to_be_bigger(pcity) && pcity->size > 4)
-    *foodneed += 1;
-
-  freelog(LOG_DEBUG, "%s, foodneed %d prodneed %d",
-         pcity->name, *foodneed, *prodneed);
-
-  /* better than nothing, not as good as a global worker allocation -- Syela */
-  memset(conflict, 0, sizeof(conflict));
-  city_map_checked_iterate(pcity->x, pcity->y, cx, cy, mx, my) {
-      conflict[cx][cy] = -1 - minimap[mx][my];
-  } city_map_checked_iterate_end;
-
-  do {
-    /* try to work near the city */
-    bx = 0;
-    by = 0;
-    best = 0;
-
-    city_map_iterate_outwards(x, y) {
-      if (can_place_worker_here(pcity, x, y)
-         && city_can_work_tile(pcity, x, y)) {
-        cur = city_tile_value(pcity,x,y,*foodneed,*prodneed) - conflict[x][y];
-       if (cur > best) {
-         bx = x;
-         by = y;
-         best = cur;
-       }
-      }
-    } city_map_iterate_outwards_end;
-    if (bx != 0 || by != 0) {
-      server_set_worker_city(pcity, bx, by);
-      (*workers)--; /* amazing what this did with no parens! -- Syela */
-      *foodneed -= city_get_food_tile(bx,by,pcity) - 2;
-      *prodneed -= city_get_shields_tile(bx,by,pcity) - 1;
-    }
-  } while(*workers != 0 && (bx != 0 || by != 0));
-  *foodneed += 2 * (*workers - 1 - e);
-  *prodneed += (*workers - 1 - e);
-  if (*prodneed > 0) {
-    freelog(LOG_DEBUG, "Ignored prodneed? in %s (%d)",
-        pcity->name, *prodneed);
-  }
+  auto_arrange_workers(pcity);
 }
 
 /**************************************************************************
-You need to call sync_cities for the affected cities to be synced with the
-client.
+  Hard check
 **************************************************************************/
-void add_adjust_workers(struct city *pcity)
+void sanity_check_city(struct city *pcity)
 {
-  int workers=pcity->size;
-  int iswork=0;
-  int toplace;
-  int foodneed;
-  int prodneed = 0;
+  int worker = 0;
 
   city_map_iterate(x, y) {
-    if (get_worker_city(pcity, x, y)==C_TILE_WORKER) 
-      iswork++;
+    if (get_worker_city(pcity, x, y) == C_TILE_WORKER) {
+      worker++;
+    }
   } city_map_iterate_end;
-  iswork--; /* City center */
-
-  if (iswork+city_specialists(pcity)>workers) {
-    freelog(LOG_ERROR, "Encountered an inconsistency in "
-           "add_adjust_workers() for city %s", pcity->name);
-    auto_arrange_workers(pcity);
-    sync_cities();
-    return;
-  }
-
-  if (iswork + city_specialists(pcity) == workers) {
-    return;
+  if (worker + city_specialists(pcity) != pcity->size + 1) {
+    die("%s is illegal (size%d w%d e%d t%d s%d)",
+        pcity->name, pcity->size, worker, pcity->ppl_elvis,
+        pcity->ppl_taxman, pcity->ppl_scientist);
   }
-
-  toplace = workers-(iswork+city_specialists(pcity));
-  foodneed = -pcity->food_surplus;
-  prodneed = -pcity->shield_surplus;
-
-  worker_loop(pcity, &foodneed, &prodneed, &toplace);
-
-  pcity->ppl_elvis+=toplace;
-  return;
 }
 
 /**************************************************************************
-You need to call sync_cities for the affected cities to be synced with the
-client.
+  You need to call sync_cities for the affected cities to be synced with 
+  the client.
 **************************************************************************/
 void auto_arrange_workers(struct city *pcity)
 {
-  struct government *g = get_gov_pcity(pcity);
-  int workers = pcity->size;
-  int taxwanted,sciwanted;
-  int foodneed, prodneed;
+  struct cm_parameter cmp;
+  struct cm_result cmr;
 
-  city_map_iterate(x, y) {
-    if (get_worker_city(pcity, x, y) == C_TILE_WORKER
-       && !is_city_center(x, y))
+  sanity_check_city(pcity);
+  cm_clear_cache(pcity);
+
+  cmp.require_happy = FALSE;
+  cmp.factor_target = FT_SURPLUS;
+  cmp.factor[FOOD] = 10;
+  cmp.factor[SHIELD] = 10;
+  cmp.factor[TRADE] = 10;
+  cmp.factor[GOLD] = 1;
+  cmp.factor[LUXURY] = 1;
+  cmp.factor[SCIENCE] = 15;
+  cmp.happy_factor = 0;
+
+  cmp.minimal_surplus[FOOD] = 1;
+  cmp.minimal_surplus[SHIELD] = 1;
+  cmp.minimal_surplus[TRADE] = 1;
+  cmp.minimal_surplus[GOLD] = 0;
+  cmp.minimal_surplus[LUXURY] = 0;
+  cmp.minimal_surplus[SCIENCE] = 0;
+
+  cm_query_result(pcity, &cmp, &cmr);
+
+  if (!cmr.found_a_valid) {
+    cmp.minimal_surplus[FOOD] = 0;
+    cmp.minimal_surplus[SHIELD] = 0;
+    cmp.minimal_surplus[TRADE] = 0;
+    cm_query_result(pcity, &cmp, &cmr);
+  }
+
+  if (!cmr.found_a_valid) {
+    CITY_LOG(LOG_DEBUG, pcity, "emergency management");
+    cmp.minimal_surplus[FOOD] = -20;
+    cmp.minimal_surplus[SHIELD] = -20;
+    cmp.minimal_surplus[TRADE] = -20;
+    cmp.minimal_surplus[GOLD] = -20;
+    cmp.minimal_surplus[LUXURY] = -20;
+    cmp.minimal_surplus[SCIENCE] = -20;
+    cmp.require_happy = FALSE;
+
+    cm_query_result(pcity, &cmp, &cmr);
+
+    assert(cmr.found_a_valid);
+  }
+
+  /* Now apply results */
+  city_map_checked_iterate(pcity->x, pcity->y, x, y, mapx, mapy) {
+    if (pcity->city_map[x][y] == C_TILE_WORKER
+        && !cmr.worker_positions_used[x][y]
+        && !is_city_center(x, y)) {
       server_remove_worker_city(pcity, x, y);
-  } city_map_iterate_end;
-  
-  foodneed=(pcity->size *2 -city_get_food_tile(2,2, pcity)) + 
settler_eats(pcity);
-  prodneed = 0;
-  prodneed -= city_get_shields_tile(2,2,pcity);
-  prodneed -= citygov_free_shield(pcity, g);
-
-  unit_list_iterate(pcity->units_supported, this_unit) {
-    int shield_cost = utype_shield_cost(unit_type(this_unit), g);
-    if (shield_cost > 0) {
-      prodneed += shield_cost;
     }
-  }
-  unit_list_iterate_end;
-  
-  taxwanted=pcity->ppl_taxman;
-  sciwanted=pcity->ppl_scientist;
-  pcity->ppl_taxman=0;
-  pcity->ppl_scientist=0;
-  pcity->ppl_elvis=0;
-
-  worker_loop(pcity, &foodneed, &prodneed, &workers);
-
-  while (workers > 0 && (taxwanted > 0 || sciwanted > 0)) {
-    if (taxwanted > 0) {
-      workers--;
-      pcity->ppl_taxman++;
-      taxwanted--;
-    } 
-    if (sciwanted > 0 && workers > 0) {
-      workers--;
-      pcity->ppl_scientist++;
-      sciwanted--;
+    if (pcity->city_map[x][y] != C_TILE_WORKER
+        && cmr.worker_positions_used[x][y]
+        && !is_city_center(x, y)) {
+      server_set_worker_city(pcity, x, y);
     }
-  }
-  pcity->ppl_elvis=workers;
+  } city_map_checked_iterate_end;
+  pcity->ppl_elvis = cmr.entertainers;
+  pcity->ppl_scientist = cmr.scientists;
+  pcity->ppl_taxman = cmr.taxmen;
+
+  sanity_check_city(pcity);
 
   city_refresh(pcity);
 }
@@ -428,6 +357,10 @@
 **************************************************************************/
 void city_reduce_size(struct city *pcity, int pop_loss)
 {
+  if (pop_loss == 0) {
+    return;
+  }
+
   if (pcity->size <= pop_loss) {
     remove_city_from_minimap(pcity->x, pcity->y);
     remove_city(pcity);
@@ -443,9 +376,9 @@
   }
 
   while (pop_loss > 0 && city_specialists(pcity) > 0) {
-    if(pcity->ppl_taxman > 0) {
+    if (pcity->ppl_taxman > 0) {
       pcity->ppl_taxman--;
-    } else if(pcity->ppl_scientist > 0) {
+    } else if (pcity->ppl_scientist > 0) {
       pcity->ppl_scientist--;
     } else {
       assert(pcity->ppl_elvis > 0);
@@ -459,9 +392,20 @@
     city_refresh(pcity);
     send_city_info(city_owner(pcity), pcity);
   } else {
+    /* Take it out on workers */
+    city_map_iterate(x, y) {
+      if (get_worker_city(pcity, x, y) == C_TILE_WORKER
+          && !is_city_center(x, y) && pop_loss > 0) {
+        server_remove_worker_city(pcity, x, y);
+        pop_loss--;
+      }
+    } city_map_iterate_end;
+    /* Then rearrange workers */
+    assert(pop_loss == 0);
     auto_arrange_workers(pcity);
     sync_cities();
   }
+  sanity_check_city(pcity);
 }
 
 /**************************************************************************
@@ -545,18 +489,16 @@
     }
 
   } else {
+    pcity->ppl_taxman++; /* or else city is !sane */
     add_adjust_workers(pcity);
   }
 
   city_refresh(pcity);
 
-  if (powner->ai.control) /* don't know if we need this -- Syela */
-    if (ai_fix_unhappy(pcity))
-      ai_scientists_taxmen(pcity);
-
   notify_player_ex(powner, pcity->x, pcity->y, E_CITY_GROWTH,
                    _("Game: %s grows to size %d."), pcity->name, pcity->size);
 
+  sanity_check_city(pcity);
   sync_cities();
 }
 
@@ -1171,28 +1113,6 @@
 }
 
 /**************************************************************************
-...
-**************************************************************************/
-static void sanity_check_city(struct city *pcity)
-{
-  int size=pcity->size;
-  int iswork=0;
-  city_map_iterate(x, y) {
-    if (get_worker_city(pcity, x, y)==C_TILE_WORKER) 
-       iswork++;
-  } city_map_iterate_end;
-  iswork--;
-  if (iswork+city_specialists(pcity)!=size) {
-    freelog(LOG_ERROR,
-           "%s is bugged: size:%d workers:%d elvis: %d tax:%d sci:%d",
-           pcity->name, size, iswork, pcity->ppl_elvis,
-           pcity->ppl_taxman, pcity->ppl_scientist); 
-    auto_arrange_workers(pcity);
-    sync_cities();
-  }
-}
-
-/**************************************************************************
   Sets the incite_revolt_cost field in the given city.
 **************************************************************************/
 int city_incite_cost(struct player *pplayer, struct city *pcity)
@@ -1278,12 +1198,6 @@
   struct government *g = get_gov_pcity(pcity);
 
   city_refresh(pcity);
-
-  /* the AI often has widespread disorder when the Gardens or Oracle
-     become obsolete.  This is a quick hack to prevent this.  980805 -- Syela 
*/
-  while (pplayer->ai.control && city_unhappy(pcity)) {
-    if (ai_make_elvis(pcity) == 0) break;
-  } /* putting this lower in the routine would basically be cheating. -- Syela 
*/
 
   /* reporting of celebrations rewritten, copying the treatment of disorder 
below,
      with the added rapture rounds count.  991219 -- Jing */
Index: server/cityturn.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/cityturn.h,v
retrieving revision 1.28
diff -u -r1.28 cityturn.h
--- server/cityturn.h   2002/12/18 17:36:20     1.28
+++ server/cityturn.h   2003/05/31 14:23:33
@@ -21,6 +21,8 @@
 struct unit;
 struct conn_list;
 
+void sanity_check_city(struct city *pcity);
+
 void city_refresh(struct city *pcity);          /* call if city has changed */
 void global_city_refresh(struct player *pplayer); /* tax/govt changed */
 
Index: server/srv_main.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/srv_main.c,v
retrieving revision 1.127
diff -u -r1.127 srv_main.c
--- server/srv_main.c   2003/05/15 16:02:00     1.127
+++ server/srv_main.c   2003/05/31 14:23:33
@@ -91,6 +91,8 @@
 #include "unithand.h"
 #include "unittools.h"
 
+#include "cm.h"
+
 #include "advmilitary.h"
 #include "aidata.h"
 #include "aihand.h"
@@ -200,6 +202,48 @@
     return TRUE;
   }
 
+#ifdef AI_MEASUREMENT_MODE
+#ifndef AI_END_GAME
+#define AI_END_GAME A_UNSET
+#endif
+  /* Special AI debug and measurement mode. Compile with 
+   * -DAI_MEASUREMENT_MODE as parameter to activate. Compile
+   * with -DAI_END_GAME=81 to stop the game at 'The Republic'. */
+  players_iterate(pplayer) {
+    int workers = 0, food = 0, shields = 0, trade = 0, settlers = 0;
+
+    if (!ai_handicap(pplayer, H_EXPERIMENTAL)) {
+      continue; /* Not relevant. */
+    }
+    /* Compile statistics */
+    unit_list_iterate(pplayer->units, punit) {
+      if (unit_flag(punit, F_CITIES)) {
+        settlers++; 
+      }
+    } unit_list_iterate_end;
+    city_list_iterate(pplayer->cities, pcity) {
+      workers += pcity->size;
+      shields += pcity->shield_prod;
+      food += pcity->food_prod;
+      trade += pcity->trade_prod;
+    } city_list_iterate_end;
+    if (get_invention(pplayer, AI_END_GAME) == TECH_KNOWN
+        || game.turn % 10 == 0) {
+      freelog(LOG_NORMAL, "%s turn %d: %d cities, %d citizens, %d "
+              "food, %d production, %d trade, %d settlers, %d "
+              "other units.", pplayer->name, game.turn,
+              city_list_size(&pplayer->cities), workers, food,
+              shields, trade, settlers,
+              unit_list_size(&pplayer->units) - settlers);
+    }
+    if (get_invention(pplayer, AI_END_GAME) == TECH_KNOWN) {
+      freelog(LOG_NORMAL, "%s reached %s, game ended in turn %d.",
+              pplayer->name, advances[AI_END_GAME].name, game.turn);
+      return TRUE;
+    }
+  } players_iterate_end;
+#endif
+
   /* count barbarians */
   players_iterate(pplayer) {
     if (is_barbarian(pplayer)) {
@@ -1571,6 +1615,7 @@
 
   /* Run server loop */
   while (TRUE) {
+    cm_init(); /* initialize CM */
     srv_loop();
     if (game.timeout == -1) {
       server_quit();
@@ -1793,6 +1838,7 @@
 **************************************************************************/
 void server_game_free()
 {
+  cm_free();
   players_iterate(pplayer) {
     player_map_free(pplayer);
   } players_iterate_end;

[Prev in Thread] Current Thread [Next in Thread]
  • [Freeciv-Dev] (PR#4335) CM in server, Per I. Mathisen <=