Complete.Org: Mailing Lists: Archives: freeciv-dev: July 2004:
[Freeciv-Dev] Re: (PR#9128) tax code changes
Home

[Freeciv-Dev] Re: (PR#9128) tax code changes

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [Freeciv-Dev] Re: (PR#9128) tax code changes
From: "Per Inge Mathisen" <per@xxxxxxxxxxx>
Date: Tue, 6 Jul 2004 13:14:40 -0700
Reply-to: rt@xxxxxxxxxxx

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

New version. We now use CM in a much smarter way to figure out how which
cities can celebrate. Also removed some unnecessary usage of CM in the AI,
that were causing problems with keeping cities celebrating. Depends on
PR#9127.

Intend to commit soon.

  - Per

Index: ai/aicity.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aicity.c,v
retrieving revision 1.156
diff -u -r1.156 aicity.c
--- ai/aicity.c 25 Jun 2004 23:43:00 -0000      1.156
+++ ai/aicity.c 6 Jul 2004 20:08:03 -0000
@@ -566,10 +566,12 @@
 
   city_list_iterate(pplayer->cities, pcity) {
     if (CITY_EMERGENCY(pcity)) {
+      auto_arrange_workers(pcity); /* this usually helps */
+    }
+    if (CITY_EMERGENCY(pcity)) {
       /* Fix critical shortages or unhappiness */
       resolve_city_emergency(pplayer, pcity);
     }
-    auto_arrange_workers(pcity);
     ai_sell_obsolete_buildings(pcity);
     sync_cities();
   } city_list_iterate_end;
Index: ai/aihand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aihand.c,v
retrieving revision 1.88
diff -u -r1.88 aihand.c
--- ai/aihand.c 25 Jun 2004 23:43:01 -0000      1.88
+++ ai/aihand.c 6 Jul 2004 20:08:04 -0000
@@ -27,6 +27,8 @@
 #include "shared.h"
 #include "unit.h"
 
+#include "cm.h"
+
 #include "citytools.h"
 #include "cityturn.h"
 #include "plrhand.h"
@@ -38,6 +40,7 @@
 #include "advspace.h"
 #include "aicity.h"
 #include "aidata.h"
+#include "ailog.h"
 #include "aitech.h"
 #include "aitools.h"
 #include "aiunit.h"
@@ -58,6 +61,8 @@
   /U2 Lemon.
 ******************************************************************************/
 
+#define LOGLEVEL_TAX LOG_DEBUG
+
 /**************************************************************************
  handle spaceship related stuff
 **************************************************************************/
@@ -76,86 +81,147 @@
 }
 
 /**************************************************************************
-  Refresh all cities of the given player.  This function is only used by 
-  AI so we keep it here. 
-
-  Do not send_unit_info because it causes client to blink.
-**************************************************************************/
-static void ai_player_cities_refresh(struct player *pplayer)
-{
-  city_list_iterate(pplayer->cities, pcity) {
-    generic_city_refresh(pcity, TRUE, NULL);
-  } city_list_iterate_end;
-}
-
-/**************************************************************************
   Set tax/science/luxury rates.
 
-  TODO: Add support for luxuries: select the luxury rate at which all 
-  cities are content and the trade output (minus what is consumed by 
-  luxuries) is maximal.
+  TODO: Add general support for luxuries: select the luxury rate at which 
+  all cities are content and the trade output (minus what is consumed by 
+  luxuries) is maximal.  For this we need some more information from the 
+  city management code.
 
   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) 
 {
   int maxrate = (ai_handicap(pplayer, H_RATES) 
                  ? get_government_max_rate(pplayer->government) : 100);
-
-  /* Otherwise stupid problems arise */
-  assert(maxrate >= 50);
-
-  /* Add proper support for luxury here */
-  pplayer->economic.luxury = 0;
-  /* After this moment don't touch luxury, it's optimal! */
-
-  if (ai_wants_no_science(pplayer)) {
-    /* Maximum tax, leftovers into science */
-    pplayer->economic.tax = MIN(maxrate, 100 - pplayer->economic.luxury);
-    pplayer->economic.science = (100 - pplayer->economic.tax
-                                 - pplayer->economic.luxury);
-    ai_player_cities_refresh(pplayer);
-  } else {
-    /* Set tax to the bare minimum which allows positive balance */
-
-    /* First set tax to the minimal available number */
-    pplayer->economic.science = MIN(maxrate, 100 - pplayer->economic.luxury);
-    pplayer->economic.tax = (100 - pplayer->economic.science
-                             - pplayer->economic.luxury);
-    ai_player_cities_refresh(pplayer);
-
-    /* Now find the minimum tax with positive balance */
-    while(pplayer->economic.tax < maxrate 
-          && pplayer->economic.science > 10) {
-
-      if (player_get_expected_income(pplayer) < 0) {
-        pplayer->economic.tax += 10;
-        pplayer->economic.science -= 10;
-        ai_player_cities_refresh(pplayer);
+  bool celebrate = TRUE;
+  int can_celebrate = 0, total_cities = 0;
+  struct government *g = get_gov_pplayer(pplayer);
+
+  /* Find minimum tax rate which gives us a positive balance. We assume
+   * that we want science most and luxuries least here, and reverse or 
+   * modify this assumption later. on */
+
+  /* First set tax to the minimal available number */
+  pplayer->economic.science = maxrate; /* Assume we want science here */
+  pplayer->economic.tax = MAX(0, 100 - maxrate * 2); /* If maxrate < 50% */
+  pplayer->economic.luxury = (100 - pplayer->economic.science
+                             - pplayer->economic.tax); /* Spillover */
+
+  /* Now find the minimum tax with positive balance */
+  while(pplayer->economic.tax < maxrate
+        && (pplayer->economic.science > 0
+            || pplayer->economic.luxury > 0)) {
+
+    if (player_get_expected_income(pplayer) < 0) {
+      pplayer->economic.tax += 10;
+      if (pplayer->economic.luxury > 0) {
+        pplayer->economic.luxury -= 10;
       } else {
-        /* Ok, got positive balance */
-        if (pplayer->economic.gold < ai_gold_reserve(pplayer)) {
-          /* Need to refill coffers, increase tax a bit */
-          pplayer->economic.tax += 10;
+        pplayer->economic.science -= 10;
+      }
+    } else {
+      /* Ok, got positive balance */
+      if (pplayer->economic.gold < ai_gold_reserve(pplayer)) {
+        /* Need to refill coffers, increase tax a bit */
+        pplayer->economic.tax += 10;
+        if (pplayer->economic.luxury > 0) {
+          pplayer->economic.luxury -= 10;
+        } else {
           pplayer->economic.science -= 10;
-          ai_player_cities_refresh(pplayer);
         }
-        /* Done! Break the while loop */
-        break;
       }
+      /* Done! Break the while loop */
+      break;
+    }
+  }
 
+  /* Should we celebrate? */
+  /* TODO: In the future, we should check if we should 
+   * celebrate for other reasons than growth. Currently 
+   * this is ignored. Maybe we need ruleset AI hints. */
+  if (government_has_flag(g, G_RAPTURE_CITY_GROWTH)) {
+    int luxrate = pplayer->economic.luxury;
+    int scirate = pplayer->economic.science;
+    struct cm_parameter cmp;
+    struct cm_result cmr;
+
+    while (pplayer->economic.luxury < maxrate
+           && pplayer->economic.science > 0) {
+      pplayer->economic.luxury += 10;
+      pplayer->economic.science -= 10;
     }
 
+    memset(&cmp, 0, sizeof(struct cm_parameter));
+    cmp.require_happy = TRUE;    /* note this one */
+    cmp.allow_disorder = FALSE;
+    cmp.allow_specialists = TRUE;
+    cmp.factor_target = FT_SURPLUS;
+    cmp.factor[FOOD] = 20;
+    cmp.minimal_surplus[GOLD] = -FC_INFINITY;
+
+    city_list_iterate(pplayer->cities, pcity) {
+      cm_clear_cache(pcity);
+      cm_query_result(pcity, &cmp, &cmr); /* burn some CPU */
+
+      total_cities++;
+
+      if (cmr.found_a_valid
+          && pcity->food_surplus > 0
+          && pcity->size >= g->rapture_size
+          && (pcity->size < game.aqueduct_size
+              || city_got_building(pcity, B_AQUEDUCT))
+          && (pcity->size < game.sewer_size
+              || city_got_building(pcity, B_SEWER))) {
+        pcity->ai.celebrate = TRUE;
+        can_celebrate++;
+      } else {
+        pcity->ai.celebrate = FALSE;
+      }
+    } city_list_iterate_end;
+    /* If more than half our cities can celebrate, go for it! */
+    celebrate = (can_celebrate * 2 > total_cities);
+    if (celebrate) {
+      freelog(LOGLEVEL_TAX, "*** %s CELEBRATES! ***", pplayer->name);
+      city_list_iterate(pplayer->cities, pcity) {
+        if (pcity->ai.celebrate == TRUE) {
+          freelog(LOGLEVEL_TAX, "setting %s to celebrate", pcity->name);
+          cm_query_result(pcity, &cmp, &cmr);
+          apply_cmresult_to_city(pcity, &cmr);
+          generic_city_refresh(pcity, TRUE, NULL);
+          if (cmr.found_a_valid && !city_happy(pcity)) {
+            CITY_LOG(LOG_ERROR, pcity, "is NOT happy when it should be!");
+          }
+        }
+      } city_list_iterate_end;
+    } else {
+      pplayer->economic.luxury = luxrate;
+      pplayer->economic.science = scirate;
+    }
+  }
+
+  if (!celebrate && pplayer->economic.luxury < maxrate) {
+    /* TODO: Add general luxury code here. */
   }
 
-  freelog(LOG_DEBUG, "%s rates: Sci %d Lux%d Tax %d NetIncome %d",
-          pplayer->name, pplayer->economic.science,
+  /* Ok, we now have the desired tax and luxury rates. Do we really want
+   * science? If not, swap it with tax if it is bigger. */
+  if (ai_wants_no_science(pplayer)
+      && pplayer->economic.science > pplayer->economic.tax) {
+    int science = pplayer->economic.science;
+    /* Swap science and tax */
+    pplayer->economic.science = pplayer->economic.tax;
+    pplayer->economic.tax = science;
+  }
+
+  assert(pplayer->economic.tax + pplayer->economic.luxury 
+         + pplayer->economic.science == 100);
+  freelog(LOGLEVEL_TAX, "%s rates: Sci %d Lux%d Tax %d NetIncome %d "
+          "celeb=(%d/%d)", pplayer->name, pplayer->economic.science,
           pplayer->economic.luxury, pplayer->economic.tax,
-          player_get_expected_income(pplayer));
+          player_get_expected_income(pplayer), can_celebrate, total_cities);
+  send_player_info(pplayer, pplayer);
 }
 
 /**************************************************************************
@@ -167,6 +233,8 @@
   necessary tech more.  The best of the available governments is recorded 
   in goal.revolution.  We record the want of each government, and only
   recalculate this data every ai->govt_reeval_turns turns.
+
+  Note: Call this _before_ doing taxes!
 **************************************************************************/
 void ai_best_government(struct player *pplayer)
 {
@@ -197,6 +265,7 @@
        * this is a rather big CPU operation, we'd rather not. */
       check_player_government_rates(pplayer);
       city_list_iterate(pplayer->cities, acity) {
+        acity->ai.celebrate = FALSE;
         generic_city_refresh(acity, TRUE, NULL);
         auto_arrange_workers(acity);
       } city_list_iterate_end;
Index: ai/aitools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitools.c,v
retrieving revision 1.113
diff -u -r1.113 aitools.c
--- ai/aitools.c        25 Jun 2004 23:43:01 -0000      1.113
+++ ai/aitools.c        6 Jul 2004 20:08:04 -0000
@@ -635,6 +635,9 @@
   pplayer->government = game.government_when_anarchy;
   handle_player_government(pplayer, gov);
   pplayer->revolution = -1; /* yes, I really mean this. -- Syela */
+  city_list_iterate(pplayer->cities, pcity) {
+    auto_arrange_workers(pcity); /* update cities */
+  } city_list_iterate_end;
 }
 
 /**************************************************************************
Index: common/player.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/player.c,v
retrieving revision 1.139
diff -u -r1.139 player.c
--- common/player.c     25 Jun 2004 23:43:01 -0000      1.139
+++ common/player.c     6 Jul 2004 20:08:05 -0000
@@ -402,7 +402,8 @@
 
 /**************************************************************************
   Return the expected net income of the player this turn.  This includes
-  tax revenue and upkeep, but not one-time purchases or found gold.
+  tax revenue and upkeep, but not one-time purchases or found gold.  Does
+  not depend on pcity->total_tax being set correctly.
 **************************************************************************/
 int player_get_expected_income(struct player *pplayer)
 {
@@ -410,8 +411,13 @@
 
   /* City income/expenses. */
   city_list_iterate(pplayer->cities, pcity) {
-    /* Tax income. */
-    income += pcity->tax_total;
+    int lux, tax, sci, trade = pcity->trade_prod;
+
+    get_tax_income(pplayer, trade, &sci, &lux, &tax);
+    income += tax;
+    income += pcity->specialists[SP_TAXMAN]
+            * game.rgame.specialists[SP_TAXMAN].bonus;
+    income += get_city_tithes_bonus(pcity);
 
     /* Improvement upkeep. */
     impr_type_iterate(impr_id) {
Index: server/cityturn.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/cityturn.c,v
retrieving revision 1.249
diff -u -r1.249 cityturn.c
--- server/cityturn.c   29 May 2004 20:34:31 -0000      1.249
+++ server/cityturn.c   6 Jul 2004 20:08:06 -0000
@@ -143,6 +143,31 @@
 }
 
 /**************************************************************************
+  Rearrange workers according to a cm_result struct.
+**************************************************************************/
+void apply_cmresult_to_city(struct city *pcity, struct cm_result *cmr)
+{
+  int i;
+
+  /* Now apply results */
+  city_map_checked_iterate(pcity->x, pcity->y, x, y, mapx, mapy) {
+    if (pcity->city_map[x][y] == C_TILE_WORKER
+        && !is_city_center(x, y)
+        && !cmr->worker_positions_used[x][y]) {
+      server_remove_worker_city(pcity, x, y);
+    }
+    if (pcity->city_map[x][y] != C_TILE_WORKER
+        && !is_city_center(x, y)
+        && cmr->worker_positions_used[x][y]) {
+      server_set_worker_city(pcity, x, y);
+    }
+  } city_map_checked_iterate_end;
+  for (i = 0; i < SP_COUNT; i++) {
+    pcity->specialists[i] = cmr->specialists[i];
+  }
+}
+
+/**************************************************************************
   You need to call sync_cities so that the affected cities are synced with 
   the client.
 **************************************************************************/
@@ -151,7 +176,6 @@
   struct cm_parameter cmp;
   struct cm_result cmr;
   struct player *pplayer = city_owner(pcity);
-  int i;
 
   /* HACK: make sure everything is up-to-date before continuing.  This may
    * result in recursive calls to auto_arrange_workers, but it's better
@@ -244,22 +268,7 @@
   }
   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
-       && !is_city_center(x, y)
-       && !cmr.worker_positions_used[x][y]) {
-      server_remove_worker_city(pcity, x, y);
-    }
-    if (pcity->city_map[x][y] != C_TILE_WORKER
-       && !is_city_center(x, y)
-       && cmr.worker_positions_used[x][y]) {
-      server_set_worker_city(pcity, x, y);
-    }
-  } city_map_checked_iterate_end;
-  for (i = 0; i < SP_COUNT; i++) {
-    pcity->specialists[i] = cmr.specialists[i];
-  }
+  apply_cmresult_to_city(pcity, &cmr);
 
   sanity_check_city(pcity);
 
Index: server/cityturn.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/cityturn.h,v
retrieving revision 1.34
diff -u -r1.34 cityturn.h
--- server/cityturn.h   28 May 2004 06:52:30 -0000      1.34
+++ server/cityturn.h   6 Jul 2004 20:08:06 -0000
@@ -20,11 +20,13 @@
 struct player;
 struct unit;
 struct conn_list;
+struct cm_result;
 
 void city_refresh(struct city *pcity);          /* call if city has changed */
 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);
 
 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] Re: (PR#9128) tax code changes, Per Inge Mathisen <=