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

There's a problem with reproducing reports for 2.2 savegames.  Over the past
9 months, I've spent considerable time improving the reproducibility of 2.1
(PR#39365, PR#39572, etc.)

Searching for myrand() differences between 2.1 and 2.2, found only 1 more
variable, added by PR#12682.  It is used as both an interval *and* a "weight"
multiplier for calculating AI wants.

Renamed it to ai.building_wait, and save/restore it.  (Yes, that's punny.)

Renamed the related AI "recalc" variables to have the same prefix_* as the
corresponding *_want counterparts (matching the names in the savegames).

Moved the pseudo-random equation from tilespec into rand, making it easier
to find, trace, and use elsewhere.

Other trivial cleanup.

Index: utility/rand.c
===================================================================
--- utility/rand.c      (revision 14377)
+++ utility/rand.c      (working copy)
@@ -78,8 +78,8 @@
   directly representable in type RANDOM_TYPE, so we do instead:
          divisor = MAX_UINT32/size
 *************************************************************************/
-RANDOM_TYPE myrand_debug(RANDOM_TYPE size, const char *called_as, int line,
-                         const char *file)
+RANDOM_TYPE myrand_debug(RANDOM_TYPE size, const char *called_as,
+                        int line, const char *file) 
 {
   RANDOM_TYPE new_rand, divisor, max;
   int bailout = 0;
@@ -110,8 +110,8 @@
     rand_state.v[rand_state.x] = new_rand;
 
     if (++bailout > 10000) {
-      freelog(LOG_ERROR, "%s(%lu) = %lu bailout at line %d of %s", 
-           called_as, (unsigned long)size, (unsigned long)new_rand, line, 
file);
+      freelog(LOG_ERROR, "%s(%lu) = %lu bailout at %s:%d", 
+           called_as, (unsigned long)size, (unsigned long)new_rand, file, 
line);
       new_rand = 0;
       break;
     }
@@ -124,8 +124,8 @@
     new_rand = 0;
   }
 
-  freelog(LOG_RAND, "%s(%lu) = %lu at line %d of %s",
-           called_as, (unsigned long)size, (unsigned long)new_rand, line, 
file);
+  freelog(LOG_RAND, "%s(%lu) = %lu at %s:%d",
+           called_as, (unsigned long)size, (unsigned long)new_rand, file, 
line);
 
   return new_rand;
 } 
@@ -248,3 +248,31 @@
   /* restore state: */
   set_myrand_state(saved_state);
 }
+
+/*************************************************************************
+  Local pseudo-random function for repeatedly reaching the same result,
+  instead of myrand().  Primarily needed for tiles.
+
+  Use an invariant equation for seed.
+  Result is 0 to (size - 1).
+*************************************************************************/
+RANDOM_TYPE myrandomly_debug(RANDOM_TYPE seed, RANDOM_TYPE size,
+                            const char *called_as, int line, const char *file)
+{
+  RANDOM_TYPE result;
+
+#define LARGE_PRIME (10007)
+#define SMALL_PRIME (1009)
+
+  /* Check for overflow and underflow */
+  assert((int)(seed * LARGE_PRIME) > 0);
+  assert(size < SMALL_PRIME);
+  assert(size > 0);
+  result = ((seed * LARGE_PRIME) % SMALL_PRIME) % size;
+
+  freelog(LOG_RAND, "%s(%lu,%lu) = %lu at %s:%d",
+         called_as, (unsigned long)seed, (unsigned long)size,
+         (unsigned long)result, file, line);
+
+  return result;
+}
Index: utility/rand.h
===================================================================
--- utility/rand.h      (revision 14377)
+++ utility/rand.h      (working copy)
@@ -26,8 +26,8 @@
   bool is_init;                        /* initially 0 for static storage */
 } RANDOM_STATE;
 
-#define myrand(vsize)      myrand_debug((vsize), "myrand", \
-                                        __LINE__, __FILE__)
+#define myrand(_size) \
+  myrand_debug((_size), "myrand", __LINE__, __FILE__)
 
 RANDOM_TYPE myrand_debug(RANDOM_TYPE size, const char *called_as, 
                          int line, const char *file);
@@ -40,4 +40,12 @@
 
 void test_random1(int n);
 
+/*===*/
+
+#define myrandomly(_seed, _size) \
+  myrandomly_debug((_seed), (_size), "myrandomly", __LINE__, __FILE__)
+
+RANDOM_TYPE myrandomly_debug(RANDOM_TYPE seed, RANDOM_TYPE size,
+                            const char *called_as, int line, const char *file);
+
 #endif  /* FC__RAND_H */
Index: server/stdinhand.c
===================================================================
--- server/stdinhand.c  (revision 14377)
+++ server/stdinhand.c  (working copy)
@@ -2357,7 +2357,6 @@
     } else {
       pcity->debug = TRUE;
       CITY_LOG(LOG_TEST, pcity, "debugged");
-      pcity->ai.next_recalc = 0; /* force recalc of city next turn */
     }
   } else if (ntokens > 0 && strcmp(arg[0], "units") == 0) {
     int x, y;
Index: server/savegame.c
===================================================================
--- server/savegame.c   (revision 14377)
+++ server/savegame.c   (working copy)
@@ -2533,21 +2533,30 @@
     worklist_load(file, &pcity->worklist, "player%d.c%d", plrno, i);
 
     /* FIXME: remove this when the urgency is properly recalculated. */
-    pcity->ai.urgency = secfile_lookup_int_default(file, 0, 
-                               "player%d.c%d.ai.urgency", plrno, i);
+    pcity->ai.urgency =
+      secfile_lookup_int_default(file, 0, "player%d.c%d.ai.urgency",
+                                 plrno, i);
 
     /* avoid myrand recalculations on subsequent reload. */
-    pcity->ai.next_recalc = secfile_lookup_int_default(file, 0, 
-                               "player%d.c%d.ai.building_turn", plrno, i);
+    pcity->ai.building_turn =
+      secfile_lookup_int_default(file, 0, "player%d.c%d.ai.building_turn",
+                                 plrno, i);
+    pcity->ai.building_wait =
+      secfile_lookup_int_default(file, BUILDING_WAIT_MINIMUM,
+                                 "player%d.c%d.ai.building_wait",
+                                 plrno, i);
 
     /* avoid myrand and expensive recalculations on subsequent reload. */
-    pcity->ai.next_founder_want_recalc = secfile_lookup_int_default(file, 0, 
-                               "player%d.c%d.ai.founder_turn", plrno, i);
-    pcity->ai.founder_want = secfile_lookup_int_default(file, 0, 
-                               "player%d.c%d.ai.founder_want", plrno, i);
-    pcity->ai.founder_boat = secfile_lookup_bool_default(file,
-                               (pcity->ai.founder_want < 0), 
-                               "player%d.c%d.ai.founder_boat", plrno, i);
+    pcity->ai.founder_turn =
+      secfile_lookup_int_default(file, 0, "player%d.c%d.ai.founder_turn",
+                                 plrno, i);
+    pcity->ai.founder_want =
+      secfile_lookup_int_default(file, 0, "player%d.c%d.ai.founder_want",
+                                 plrno, i);
+    pcity->ai.founder_boat =
+      secfile_lookup_bool_default(file, (pcity->ai.founder_want < 0),
+                                  "player%d.c%d.ai.founder_boat",
+                                  plrno, i);
 
     /* After all the set_worker_city() are done. */
     tile_set_city(pcity->tile, pcity);
@@ -3287,11 +3296,13 @@
                       "player%d.c%d.ai.urgency", plrno, i);
 
     /* avoid myrand recalculations on subsequent reload. */
-    secfile_insert_int(file, pcity->ai.next_recalc,
+    secfile_insert_int(file, pcity->ai.building_turn,
                       "player%d.c%d.ai.building_turn", plrno, i);
+    secfile_insert_int(file, pcity->ai.building_wait,
+                      "player%d.c%d.ai.building_wait", plrno, i);
 
     /* avoid myrand and expensive recalculations on subsequent reload. */
-    secfile_insert_int(file, pcity->ai.next_founder_want_recalc,
+    secfile_insert_int(file, pcity->ai.founder_turn,
                       "player%d.c%d.ai.founder_turn", plrno, i);
     secfile_insert_int(file, pcity->ai.founder_want,
                       "player%d.c%d.ai.founder_want", plrno, i);
Index: common/city.c
===================================================================
--- common/city.c       (revision 14377)
+++ common/city.c       (working copy)
@@ -2438,8 +2438,7 @@
   pcity->owner = pplayer;
   pcity->tile = ptile;
   sz_strlcpy(pcity->name, name);
-#if 0
-  /* some zero variables not compiled, but left for searching */
+#ifdef ZERO_VARIABLES_FOR_SEARCHING
   memset(pcity->feel, 0, sizeof(pcity->feel));
   memset(pcity->specialists, 0, sizeof(pcity->specialists));
   memset(pcity->tile_output, 0, sizeof(pcity->tile_output));
@@ -2509,6 +2508,7 @@
 
   pcity->turn_last_built = game.info.turn;
   pcity->changed_from = pcity->production;
+#ifdef ZERO_VARIABLES_FOR_SEARCHING
   pcity->before_change_shields = 0;
   pcity->disbanded_shields = 0;
   pcity->caravan_shields = 0;
@@ -2521,13 +2521,21 @@
   pcity->server.needs_arrange = FALSE;
   pcity->server.vision = NULL; /* No vision. */
 
+  pcity->ai.building_turn = 0;
+#endif
+  pcity->ai.building_wait = BUILDING_WAIT_MINIMUM;
+#ifdef ZERO_VARIABLES_FOR_SEARCHING
+  memset(pcity->ai.building_want, 0, sizeof(pcity->ai.building_want));
+
+  /* pcity->ai.choice; placeholder for searching */
   pcity->ai.founder_boat = FALSE;
+  pcity->ai.founder_turn = 0;
   pcity->ai.founder_want = 0; /* calculating this is really expensive */
-  pcity->ai.next_founder_want_recalc = 0; /* turns to recalc found_want */
+  pcity->ai.settler_want = 0;
+#endif
   pcity->ai.trade_want = 1; /* we always want some */
-#if 0
-  /* some zero variables not compiled, but left for searching */
-  memset(pcity->ai.building_want, 0, sizeof(pcity->ai.building_want));
+
+#ifdef ZERO_VARIABLES_FOR_SEARCHING
   pcity->ai.danger = 0;
   pcity->ai.urgency = 0;
   pcity->ai.grave_danger = 0;
@@ -2536,8 +2544,6 @@
   pcity->ai.invasion = 0;
   pcity->ai.bcost = 0;
   pcity->ai.attack = 0;
-  pcity->ai.recalc_interval = 1;
-  pcity->ai.next_recalc = 0;
 
   memset(pcity->surplus, 0, O_COUNT * sizeof(*pcity->surplus));
   memset(pcity->waste, 0, O_COUNT * sizeof(*pcity->waste));
Index: common/city.h
===================================================================
--- common/city.h       (revision 14377)
+++ common/city.h       (working copy)
@@ -186,6 +186,10 @@
 };
 
 struct ai_city {
+  int building_turn;            /* only recalculate every Nth turn */
+  int building_wait;            /* for weighting values */
+#define BUILDING_WAIT_MINIMUM (1)
+
   /* building desirabilities - easiest to handle them here -- Syela */
   /* The units of building_want are output
    * (shields/gold/luxuries) multiplied by a priority
@@ -193,6 +197,8 @@
    */
   int building_want[B_LAST];
 
+  struct ai_choice choice;      /* to spend gold in the right place only */
+
   unsigned int danger;          /* danger to be compared to assess_defense */
   bool diplomat_threat;         /* enemy diplomat or spy is near the city */
   bool has_diplomat;            /* this city has diplomat or spy defender */
@@ -201,8 +207,6 @@
   unsigned int grave_danger;    /* danger, should show positive feedback */
   int wallvalue;                /* how much it helps for defenders to be 
                                    ground units */
-  int trade_want;               /* saves a zillion calculations */
-  struct ai_choice choice;      /* to spend gold in the right place only */
   int downtown;                 /* distance from neighbours, for locating 
                                    wonders wisely */
   int distance_to_wonder_city;  /* wondercity will set this for us, 
@@ -217,17 +221,17 @@
   /* These values are for builder (F_SETTLERS) and founder (F_CITIES) units.
    * Negative values indicate that the city needs a boat first;
    * -value is the degree of want in that case. */
-  int settler_want, founder_want;
-  int next_founder_want_recalc; /* do not recalc founder_want every turn */
-  bool founder_boat; /* if the city founder will need a boat */
+  bool founder_boat;            /* city founder will need a boat */
+  int founder_turn;             /* only recalculate every Nth turn */
+  int founder_want;
+  int settler_want;
+  int trade_want;               /* saves a zillion calculations */
+
   int invasion; /* who's coming to kill us, for attack co-ordination */
   int attack, bcost; /* This is also for invasion - total power and value of
                       * all units coming to kill us. */
 
   int worth; /* Cache city worth here, sum of all weighted incomes */
-  /* Only recalc every Nth turn: */
-  int recalc_interval; /* Use for weighting values calculated every Nth turn */
-  int next_recalc;
 };
 
 enum citizen_category {
Index: ai/aicity.c
===================================================================
--- ai/aicity.c (revision 14377)
+++ ai/aicity.c (working copy)
@@ -276,7 +276,7 @@
   /* The conversion factor was determined by experiment,
    * and might need adjustment.
    */
-  const int tech_want = building_want * pcity->ai.recalc_interval * 14 / 8;
+  const int tech_want = building_want * pcity->ai.building_wait * 14 / 8;
 #if 0
   /* This logging is relatively expensive,
    * so activate it only while necessary. */
@@ -1118,15 +1118,15 @@
       improvement_iterate(pimprove) {
         pcity->ai.building_want[improvement_index(pimprove)] = 1;
       } improvement_iterate_end;
-    } else if (pcity->ai.next_recalc <= game.info.turn) {
+    } else if (pcity->ai.building_turn <= game.info.turn) {
       /* Do a scheduled recalculation this turn */
       improvement_iterate(pimprove) {
         pcity->ai.building_want[improvement_index(pimprove)] = 0;
       } improvement_iterate_end;
     } else if (should_force_recalc(pcity)) {
       /* Do an emergency recalculation this turn. */
-      pcity->ai.recalc_interval = pcity->ai.next_recalc - game.info.turn;
-      pcity->ai.next_recalc = game.info.turn;
+      pcity->ai.building_wait = pcity->ai.building_turn - game.info.turn;
+      pcity->ai.building_turn = game.info.turn;
 
       improvement_iterate(pimprove) {
         pcity->ai.building_want[improvement_index(pimprove)] = 0;
@@ -1150,7 +1150,7 @@
           /* Don't consider impossible or redundant buildings */
           pcity->ai.building_want[improvement_index(pimprove)] = 0;
         } else if (pplayer->ai.control
-                   && pcity->ai.next_recalc <= game.info.turn) {
+                   && pcity->ai.building_turn <= game.info.turn) {
           /* Building wants vary relatively slowly, so not worthwhile
            * recalculating them every turn.
            * We DO want to calculate (tech) wants because of buildings
@@ -1275,11 +1275,11 @@
 
   /* Reset recalc counter */
   city_list_iterate(pplayer->cities, pcity) {
-    if (pcity->ai.next_recalc <= game.info.turn) {
+    if (pcity->ai.building_turn <= game.info.turn) {
       /* This will spread recalcs out so that no one turn end is 
        * much longer than others */
-      pcity->ai.recalc_interval = myrand(RECALC_SPEED) + RECALC_SPEED;
-      pcity->ai.next_recalc = game.info.turn + pcity->ai.recalc_interval;
+      pcity->ai.building_wait = myrand(RECALC_SPEED) + RECALC_SPEED;
+      pcity->ai.building_turn = game.info.turn + pcity->ai.building_wait;
     }
   } city_list_iterate_end;
 }
@@ -1701,12 +1701,15 @@
     TIMING_LOG(AIT_CITY_TERRAIN, TIMER_STOP);
 
     TIMING_LOG(AIT_CITY_SETTLERS, TIMER_START);
-    if (pcity->ai.next_founder_want_recalc <= game.info.turn) {
+    if (pcity->ai.founder_turn <= game.info.turn) {
       /* Will record its findings in pcity->founder_want */ 
       contemplate_new_city(pcity);
       /* Avoid recalculating all the time.. */
-      pcity->ai.next_founder_want_recalc = 
+      pcity->ai.founder_turn = 
         game.info.turn + myrand(RECALC_SPEED) + RECALC_SPEED;
+    } else if (pcity->debug) {
+      /* recalculate every turn */
+      contemplate_new_city(pcity);
     }
     TIMING_LOG(AIT_CITY_SETTLERS, TIMER_STOP);
     ASSERT_CHOICE(pcity->ai.choice);
Index: client/tilespec.c
===================================================================
--- client/tilespec.c   (revision 14377)
+++ client/tilespec.c   (working copy)
@@ -38,6 +38,7 @@
 #include "mem.h"
 #include "nation.h"
 #include "player.h"
+#include "rand.h"
 #include "registry.h"
 #include "shared.h"
 #include "specialist.h"
@@ -3787,13 +3788,8 @@
 
          if (count > 0) {
            /* Pseudo-random reproducable algorithm to pick a sprite. */
-           const int LARGE_PRIME = 10007;
-           const int SMALL_PRIME = 1009;
+           count = myrandomly(tile_index(ptile), count);
 
-           assert(count < SMALL_PRIME);
-           assert((int)(LARGE_PRIME * MAP_INDEX_SIZE) > 0);
-           count = ((tile_index(ptile) * LARGE_PRIME) % SMALL_PRIME) % count;
-
            if (dlp->is_tall) {
              ox += FULL_TILE_X_OFFSET;
              oy += FULL_TILE_Y_OFFSET;
_______________________________________________
Freeciv-dev mailing list
Freeciv-dev@gna.org
https://mail.gna.org/listinfo/freeciv-dev

Reply via email to