I need a way to Randomly iterate through every tile on the map.

Bobert13

Prince
Joined
Feb 25, 2013
Messages
346
I've been messing around with oasis placement as of late, and I've got a really detailed function that scans a radius around a tile to determine whether or not to place an oasis. The issue is, if there's a large enough desert for them to be placed in, they form a pattern. Currently I'm using a scan method to iterate through the plots and I'd like to replace it with a randomized method to get rid of the pattern.

The scan method:
Code:
for y = 0, gridHeight - 1,1 do
		for x = 0,gridWidth - 1,1 do
			local plot = Map.GetPlot(x,y)

Anybody have any suggestions/examples of how I might be able to do this?
 
Code:
	local W,H = Map.GetGridSize();
	local WH = W*H
	local buf = {}
	local i=1
	for n=1,WH,1 do [COLOR="SeaGreen"]-- Sets up a table with an index and value for every plot on the map.[/COLOR]
		buf[n] = n
	end
	for n=1,WH,1 do
		local r = 1+WH-n [COLOR="SeaGreen"]--Determines the max for my RNG. Simplified, its just an inverse counter counting down (1 at a time) from the number of plots on the map.[/COLOR]
		local randi = PWRandint(1,r) [COLOR="SeaGreen"]--RNG to determine which index to pull.[/COLOR]
		local coord = buf[randi][COLOR="SeaGreen"] --Pulls the random index and sets the variable to the associated value.[/COLOR]
		table.remove(buf,randi) [COLOR="SeaGreen"]--Removes the index and value.[/COLOR]
		local x =coord % W [COLOR="SeaGreen"]--Determines the x coordinate based off the value.[/COLOR]
		local y =(coord-x)/W [COLOR="SeaGreen"]--Determines the y coordinate.[/COLOR]
		print(string.format("coord for index:%d is:(%d,%d). plots remaining:%d.",coord,x,y,r)) [COLOR="SeaGreen"]--A debug printout that's not helping me figure out the issue.[/COLOR] :mad:
		local plot = Map.GetPlot(x,y)

I keep getting "Runtime Error: [string "PerfectWorld3.lua"]:2356: attempt to index local 'plot' (a nil value)" out of the console after anywhere from 300 to 6000 iterations on a huge map (128x80=10,240 plots).

Does removing an index from a table A) delete the value associated with that index and B) realign the indexes?

I'm pretty confident that my arithmetic works as it gets through so many iterations and prints nothing but whole numbers for coordinates.

I got this idea from function GetShuffledCopyOfTable() in MapMakerUtilities.lua where they (read "Firaxis") do this:
Code:
	local left_to_do = table.maxn(copy);
	for loop = 1, len do
		local random_index = 1 + Map.Rand(left_to_do, "Shuffling table entry - Lua");
		table.insert(shuffledVersion, copy[random_index]);
		table.remove(copy, random_index);
		left_to_do = left_to_do - 1;
	end
	return shuffledVersion

If their code works why doesn't mine!:wallbash::wallbash::wallbash::wallbash:
 
*snip*
I keep getting "Runtime Error: [string "PerfectWorld3.lua"]:2356: attempt to index local 'plot' (a nil value)" out of the console after anywhere from 300 to 6000 iterations on a huge map (128x80=10,240 plots).

Does removing an index from a table A) delete the value associated with that index and B) realign the indexes?

I'm pretty confident that my arithmetic works as it gets through so many iterations and prints nothing but whole numbers for coordinates.

I got this idea from function GetShuffledCopyOfTable() in MapMakerUtilities.lua where they (read "Firaxis") do this:
*snip*

If their code works why doesn't mine!:wallbash::wallbash::wallbash::wallbash:

Turns out my arithmetic was off. I needed to subtract 1 from the map width for the limiter on x determination.
I needed to subtract 1 from the value I was using to determine coordinates as the arithmetic is designed for tables that start at 0 and .lua tables start at 1.

Code:
	local W,H = Map.GetGridSize();
	local WH = W*H
	local buf = {}
	for n=0,WH-1,1 do
		buf[n] = n
	end
	for n=1,WH,1 do
		local r = WH-n
		local randi = PWRandint(0,r)
		local coord = buf[randi]
		table.remove(buf,randi)
		local x =coord % W
		local y =(coord-x)/W
		local plot = Map.GetPlot(x,y)

Anybody got any ideas on how I can optimize this? I'd like to use it for a lot more than Oasis determination, but it's a hell of a lot slower than just scanning rows and columns. :cringe:
 
It looks good to me, actually. The only thing I can think of to squeeze a little extra performance is to find a way of removing that modulus operator. It's been a few years since I touched lua so I don't remember if it allows you to fill tables with tables, but if so then I would suggest storing the x and y coordinates in buf[] so that when you're calling .GetPlot you don't have to first convert the array index.
 
It looks good to me, actually. The only thing I can think of to squeeze a little extra performance is to find a way of removing that modulus operator. It's been a few years since I touched lua so I don't remember if it allows you to fill tables with tables, but if so then I would suggest storing the x and y coordinates in buf[] so that when you're calling .GetPlot you don't have to first convert the array index.

I ended up canning this and making a table that stores the coordinates of desert tiles as they're placed. Then calling the shuffle function to get randomization.

It seems to be a lot quicker to only iterate the 600ish (or less) desert tiles than it was to iterate through every tile on the map.

As far as the math bit goes, I was wondering is it faster to store a table of x,y coordinates and then call them all back later, or is it faster to store an index value and then perform the math every iteration.

Is there a console or something where I can set up pseudo-code and run it? The console would need to output a pretty precise time for how long it took to run said code.
 
As far as the math bit goes, I was wondering is it faster to store a table of x,y coordinates and then call them all back later, or is it faster to store an index value and then perform the math every iteration.

Is there a console or something where I can set up pseudo-code and run it? The console would need to output a pretty precise time for how long it took to run said code.

Code:
>[COLOR="RoyalBlue"]lua -e "io.stdout:setvbuf 'no'" "Test.lua" [/COLOR]
Index Method elapsed time: 0.156

Coord Method elapsed time: 0.500

[COLOR="RoyalBlue"]>Exit code: 0[/COLOR]

And I have my answer, it's much faster to do the math every time. In fact, its more than three times as fast... Coincidentally (or maybe not) it's also 3 times more table operations to store coordinates. :goodjob:
 
You should probably post these kinds of questions/discussions on the main C&C forum so folks will see it. Everyone expects finished map scripts here.

Great work on the map scripts by the way!
 
Moderator Action: thread moved to main C&C section.
 
This is how Firaxis did it in MapGenerator.lua:

Code:
-- Iterate through plots
function Shuffle(t)
	local len = #t;
	local random = Map.Rand;
	for i = 1, len, 1 do
		local k = random(len - 1, "Shuffling Values") + 1;
		t[i], t[k] = t[k], t[i];
	end
end

local _plots = {}; --memoize table of plots
function Plots(sort)
	local _indices = {};
	for i = 1, Map.GetNumPlots(), 1 do
		_indices[i] = i - 1;
	end	
	
	if(sort) then
		sort(_indices);
	end
	
	local cur = 0;
	local it = function()
		cur = cur + 1;
		local index = _indices[cur];
		local plot;
		
		if(index) then
			plot = _plots[index] or Map.GetPlotByIndex(index);
			_plots[index] = plot;
		end
		return index, plot;
	end
	
	return it;
end

----------------------------------------
Use like this:

for index, plot in Plots(Shuffle) do
   if ( not plot:IsWater() ) then
      ...

You only need to include "MapGenerator.lua".
:D
 
Back
Top Bottom