changeset 19243:3944b709b9bb draft

(svn r24132) -Change [FS#4713]: Improve randomness of tile order in the tile loop. (monoid)
author michi_cc <michi_cc@openttd.org>
date Tue, 17 Apr 2012 19:43:43 +0000
parents be6130c27640
children a5850617d857
files src/genworld.cpp src/landscape.cpp src/misc.cpp src/saveload/afterload.cpp
diffstat 4 files changed, 36 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/src/genworld.cpp
+++ b/src/genworld.cpp
@@ -159,6 +159,7 @@
 			SetGeneratingWorldProgress(GWP_RUNTILELOOP, 0x500);
 			for (i = 0; i < 0x500; i++) {
 				RunTileLoop();
+				_tick_counter++;
 				IncreaseGeneratingWorldProgress(GWP_RUNTILELOOP);
 			}
 
--- a/src/landscape.cpp
+++ b/src/landscape.cpp
@@ -712,32 +712,43 @@
 
 
 TileIndex _cur_tileloop_tile;
-#define TILELOOP_BITS 4
-#define TILELOOP_SIZE (1 << TILELOOP_BITS)
-#define TILELOOP_ASSERTMASK ((TILELOOP_SIZE - 1) + ((TILELOOP_SIZE - 1) << MapLogX()))
-#define TILELOOP_CHKMASK (((1 << (MapLogX() - TILELOOP_BITS))-1) << TILELOOP_BITS)
 
+/**
+ * Gradually iterate over all tiles on the map, calling their TileLoopProcs once every 256 ticks.
+ */
 void RunTileLoop()
 {
-	TileIndex tile = _cur_tileloop_tile;
+	/* The pseudorandom sequence of tiles is generated using a Galois linear feedback
+	 * shift register (LFSR). This allows a deterministic pseudorandom ordering, but
+	 * still with minimal state and fast iteration. */
+
+	/* Maximal length LFSR feedback terms, from 12-bit (for 64x64 maps) to 22-bit (for 2048x2048 maps).
+	 * Extracted from http://www.ece.cmu.edu/~koopman/lfsr/ */
+	static const uint32 feedbacks[] = {
+		0xD8F, 0x1296, 0x2496, 0x4357, 0x8679, 0x1030E, 0x206CD, 0x403FE, 0x807B8, 0x1004B2, 0x2006A8
+	};
+	const uint32 feedback = feedbacks[MapLogX() + MapLogY() - 12];
 
-	assert((tile & ~TILELOOP_ASSERTMASK) == 0);
-	uint count = (MapSizeX() / TILELOOP_SIZE) * (MapSizeY() / TILELOOP_SIZE);
-	do {
+	/* We update every tile every 256 ticks, so divide the map size by 2^8 = 256 */
+	uint count = 1 << (MapLogX() + MapLogY() - 8);
+
+	TileIndex tile = _cur_tileloop_tile;
+	/* The LFSR cannot have a zeroed state. */
+	assert(tile != 0);
+
+	/* Manually update tile 0 every 256 ticks - the LFSR never iterates over it itself.  */
+	if (_tick_counter % 256 == 0) {
+		_tile_type_procs[GetTileType(0)]->tile_loop_proc(0);
+		count--;
+	}
+
+	while (count--) {
 		_tile_type_procs[GetTileType(tile)]->tile_loop_proc(tile);
 
-		if (TileX(tile) < MapSizeX() - TILELOOP_SIZE) {
-			tile += TILELOOP_SIZE; // no overflow
-		} else {
-			tile = TILE_MASK(tile - TILELOOP_SIZE * (MapSizeX() / TILELOOP_SIZE - 1) + TileDiffXY(0, TILELOOP_SIZE)); // x would overflow, also increase y
-		}
-	} while (--count != 0);
-	assert((tile & ~TILELOOP_ASSERTMASK) == 0);
+		/* Get the next tile in sequence using a Galois LFSR. */
+		tile = (tile >> 1) ^ (-(int32)(tile & 1) & feedback);
+	}
 
-	tile += 9;
-	if (tile & TILELOOP_CHKMASK) {
-		tile = (tile + MapSizeX()) & TILELOOP_ASSERTMASK;
-	}
 	_cur_tileloop_tile = tile;
 }
 
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -59,7 +59,7 @@
 	_pause_mode = PM_UNPAUSED;
 	_fast_forward = 0;
 	_tick_counter = 0;
-	_cur_tileloop_tile = 0;
+	_cur_tileloop_tile = 1;
 	_thd.redsq = INVALID_TILE;
 	if (reset_settings) MakeNewgameSettingsLive();
 
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -483,6 +483,10 @@
 
 	TileIndex map_size = MapSize();
 
+	extern TileIndex _cur_tileloop_tile; // From landscape.cpp.
+	/* The LFSR used in RunTileLoop iteration cannot have a zeroed state, make it non-zeroed. */
+	if (_cur_tileloop_tile == 0) _cur_tileloop_tile = 1;
+
 	if (IsSavegameVersionBefore(98)) GamelogOldver();
 
 	GamelogTestRevision();