Complete.Org: Mailing Lists: Archives: freeciv-dev: September 2003:
[Freeciv-Dev] barbarians on the poles (PR#6183)
Home

[Freeciv-Dev] barbarians on the poles (PR#6183)

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [Freeciv-Dev] barbarians on the poles (PR#6183)
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Mon, 29 Sep 2003 15:00:14 -0700
Reply-to: rt@xxxxxxxxxxxxxx

Here's a full patch to fix this.

- A new function, rand_map_pos_filtered, is used.

- This function is used to find a _valid_ spot for barbarians.

- Barbarians are now summoned a fixed 1/10 of the time when 
summon_barbarians() is called.

- Barbarians will never be summoned on arctic or tundra.

- If there are no valid positions for summoning, the server won't fail.

This does change the behavior.  Barbarian summoning is still random, but 
with a different distribution.  In my tests the average # summoned was 
about the same.

The only advantage of the new method is that it is robust.  If this is 
not a concern we can use a much simpler fix (also attached).  Note that 
we have the exact same dilemma in several other places, however.

Comments?

jason

? core.19462
Index: common/map.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/map.c,v
retrieving revision 1.146
diff -u -r1.146 map.c
--- common/map.c        2003/09/29 02:42:15     1.146
+++ common/map.c        2003/09/29 21:54:45
@@ -1497,6 +1497,51 @@
 }
 
 /**************************************************************************
+ Random square anywhere on the map for which the 'filter' function
+ returns true.  return FALSE if none can be found.
+**************************************************************************/
+bool rand_map_pos_filtered(int *x, int *y, bool (*filter)(int x, int y))
+{
+  int tries = 0, count = 0;
+  const int max_tries = map.xsize * map.ysize / 10;
+
+  /* First do a few quick checks to find a spot. */
+  do {
+    rand_map_pos(x, y);
+    if (filter(*x, *y)) {
+      return TRUE;
+    }
+  } while (++tries < max_tries);
+
+  /* If that fails, count all available spots and pick one.
+   * Slow but reliable. */
+  whole_map_iterate(x1, y1) {
+    if (filter(x1, y1)) {
+      count++;
+    }
+  } whole_map_iterate_end;
+
+  if (count == 0) {
+    return FALSE;
+  }
+
+  count = myrand(count);
+  whole_map_iterate(x1, y1) {
+    if (filter(x1, y1)) {
+      if (count == 0) {
+       *x = x1;
+       *y = y1;
+       return TRUE;
+      }
+      count--;
+    }
+  } whole_map_iterate_end;
+
+  assert(0);
+  return FALSE;
+}
+
+/**************************************************************************
 Return the debugging name of the direction.
 **************************************************************************/
 const char *dir_get_name(enum direction8 dir)
Index: common/map.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/map.h,v
retrieving revision 1.156
diff -u -r1.156 map.h
--- common/map.h        2003/09/29 17:42:37     1.156
+++ common/map.h        2003/09/29 21:54:45
@@ -335,6 +335,7 @@
 
 void rand_neighbour(int x0, int y0, int *x, int *y);
 void rand_map_pos(int *x, int *y);
+bool rand_map_pos_filtered(int *x, int *y, bool (*filter)(int x, int y));
 
 bool is_water_adjacent_to_tile(int x, int y);
 bool is_tiles_adjacent(int x0, int y0, int x1, int y1);
Index: server/barbarian.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/barbarian.c,v
retrieving revision 1.68
diff -u -r1.68 barbarian.c
--- server/barbarian.c  2003/09/19 14:14:45     1.68
+++ server/barbarian.c  2003/09/29 21:54:45
@@ -302,8 +302,57 @@
   return FALSE;
 }
 
+/* Variables used in checking for barbarian uprising positions. */
+static int uprise_civ_check, uprise_gov_check;
+
+/****************************************************************************
+  Return TRUE iff this is a valid position for barbarians.  See
+  try_summon_barbarian().  This function is fairly slow, given that it may
+  have to be called many times.
+****************************************************************************/
+static bool is_valid_barbarian_pos(int x, int y)
+{
+  struct city *pcity;
+  int xu, yu, dist;
+
+  /* FIXME: replace with appropriate terrain flag. */
+  if (map_get_terrain(x, y) == T_TUNDRA
+      || map_get_terrain(x, y) == T_ARCTIC) {
+    return FALSE;
+  }
+
+  if (!(pcity = dist_nearest_city(NULL, x, y, TRUE, FALSE))) {
+    /* any city */
+    return FALSE;
+  }
+
+  dist = real_map_distance(x, y, pcity->x, pcity->y);
+  if (dist > MAX_UNREST_DIST || dist < MIN_UNREST_DIST) {
+    return FALSE;
+  }
+
+  /* Sea Raiders can come out of unknown sea territory. */
+  if (!find_empty_tile_nearby(x, y, &xu, &yu)
+      || (!map_is_known(xu, yu, city_owner(pcity))
+         && !is_ocean(map_get_terrain(xu, yu)))
+      || !is_near_land(xu, yu)) {
+    return FALSE;
+  }
+
+  /* Do not harass small civs (or too early in the game). */
+  if (uprise_civ_check > (city_list_size(&city_owner(pcity)->cities) -
+                        UPRISE_CIV_SIZE / (game.barbarianrate - 1))
+      || uprise_gov_check > get_gov_pcity(pcity)->civil_war) {
+    return FALSE;
+  }
+
+  /* Looks like this is a good place for barbarians! */
+  return TRUE;
+}
+
 /**************************************************************************
   The barbarians are summoned at a randomly chosen place if:
+  0. The terrain is not tundra or arctic.
   1. It's not closer than MIN_UNREST_DIST and not further than 
      MAX_UNREST_DIST from the nearest city. City owner is called 'victim' 
      here.
@@ -323,46 +372,25 @@
 static void try_summon_barbarians(void)
 {
   int x, y, xu, yu;
-  int i, boat, cap, dist, unit;
+  int i, boat, cap, unit;
   int uprise = 1;
+  struct player *victim, *barbarians;
   struct city *pc;
-  struct player *barbarians, *victim;
+  static int uprisings = 0;
 
-  /* No uprising on North or South Pole */
-  do {
-    rand_map_pos(&x, &y);
-  } while (y == 0 || y == map.ysize - 1);
-
-  if (!(pc = dist_nearest_city(NULL, x, y, TRUE, FALSE))) {
-    /* any city */
-    return;
-  }
-
-  victim = city_owner(pc);
-
-  dist = real_map_distance(x, y, pc->x, pc->y);
-  freelog(LOG_DEBUG,"Closest city to %d %d is %s at %d %d which is %d far", 
-          x, y, pc->name, pc->x, pc->y, dist);
-  if (dist > MAX_UNREST_DIST || dist < MIN_UNREST_DIST) {
-    return;
+  /* Pick random values for this barbarian check. */
+  uprise_civ_check = myrand(UPRISE_CIV_MORE);
+  uprise_gov_check = myrand(100);
+  if (!rand_map_pos_filtered(&x, &y, is_valid_barbarian_pos)) {
+    return; /* No barbarians this time. */
   }
 
-  /* I think Sea Raiders can come out of unknown sea territory */
-  if (!find_empty_tile_nearby(x,y,&xu,&yu)
-      || (!map_is_known(xu, yu, victim)
-         && !is_ocean(map_get_terrain(xu, yu)))
-      || !is_near_land(xu, yu)) {
-    return;
-  }
+  freelog(LOG_NORMAL, "%d: uprising %d at %d,%d",
+         game.year, ++uprisings, x, y);
 
-  /* do not harass small civs - in practice: do not uprise at the beginning */
-  if ((int)myrand(UPRISE_CIV_MORE) >
-           (int)city_list_size(&victim->cities) -
-                UPRISE_CIV_SIZE/(game.barbarianrate-1)
-      || myrand(100) > get_gov_pcity(pc)->civil_war) {
-    return;
-  }
-  freelog(LOG_DEBUG, "Barbarians are willing to fight");
+  pc = dist_nearest_city(NULL, x, y, TRUE, FALSE);
+  victim = city_owner(pc);
+  find_empty_tile_nearby(x, y, &xu, &yu);
 
   if (map_has_special(x, y, S_HUT)) { /* remove the hut in place of uprising */
     map_clear_special(x,y,S_HUT);
@@ -425,23 +453,15 @@
 **************************************************************************/
 void summon_barbarians(void)
 {
-  int i, n;
-
-  if (game.barbarianrate == 0) {
-    return;
-  }
+  int i;
 
   if (game.year < game.onsetbarbarian) {
     return;
   }
 
-  n = map_num_tiles() / MAP_FACTOR;
-  if (n == 0) {
-    /* Allow barbarians on maps smaller than MAP_FACTOR */
-    n = 1;
-  }
-
-  for (i = 0; i < n * (game.barbarianrate - 1); i++) {
-    try_summon_barbarians();
+  for (i = 0; i < game.barbarianrate; i++) {
+    if (myrand(BARB_FACTOR) == 0) {
+      try_summon_barbarians();
+    }
   }
 }
Index: server/barbarian.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/barbarian.h,v
retrieving revision 1.9
diff -u -r1.9 barbarian.h
--- server/barbarian.h  2003/05/05 12:41:39     1.9
+++ server/barbarian.h  2003/09/29 21:54:45
@@ -25,7 +25,9 @@
 #define UPRISE_CIV_MORE  30
 #define UPRISE_CIV_MOST  50
 
-#define MAP_FACTOR     2000  /* adjust this to get a good uprising frequency */
+/* Adjust this to get a good uprising frequency.  Barbarians are summoned
+ * every 1/BARB_FACTOR attempts. */
+#define BARB_FACTOR 10
 
 bool unleash_barbarians(int x, int y);
 void summon_barbarians(void);
Index: server/barbarian.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/barbarian.c,v
retrieving revision 1.68
diff -u -r1.68 barbarian.c
--- server/barbarian.c  2003/09/19 14:14:45     1.68
+++ server/barbarian.c  2003/09/29 21:59:28
@@ -331,7 +331,8 @@
   /* No uprising on North or South Pole */
   do {
     rand_map_pos(&x, &y);
-  } while (y == 0 || y == map.ysize - 1);
+  } while (map_get_terrain(x, y) == T_TUNDRA
+          || map_get_terrain(x, y) == T_ARCTIC);
 
   if (!(pc = dist_nearest_city(NULL, x, y, TRUE, FALSE))) {
     /* any city */

[Prev in Thread] Current Thread [Next in Thread]
  • [Freeciv-Dev] barbarians on the poles (PR#6183), Jason Short <=