Page 1 of 1

Optimize Code - Tile Replacement

Posted: Sun Aug 21, 2016 11:07 pm
by TheSAguy
Hi,
I wrote a function that changes the terrain when you kill a biter or spawner. I don't think the code is very optimized and can probably be written better.

When terrain is replaced there is a boarder around the replaced terrain also, so this causes landfill when done near water. So when you kill a biter or spwaner, I look at the terrain around the area also and if water, I add the "false" to "surface.set_tiles(New_tiles, false)". This causes no boarder to form, though the replaced terrain is blocky.

So here is my current code and was just wondering if anyone had a better/more efficient way of doing this:

Code: Select all

local replaceableTiles =
{
  ["grass"] = "grass-medium",
  ["grass-medium"] = "grass-dry",
  ["grass-dry"] = "sand",
  ["sand"] = "sand-dark",
  ["sand-dark"] = "dirt",
  ["dirt"] = "dirt-dark"
}

local waterTiles =
{
  ["deepwater"] = true,
  ["deepwater-green"] = true,
  ["water"] = true,
  ["water-green"] = true
}

---------------------------------------------
function Scorched_Earth(surface, pos, size)
	--- Turn the terrain into desert
	local New_tiles = {}
	local Water_Nearby = false
	
	for xxx=-size,size do
		for yyy=-size,size do
			new_position = {x = pos.x + xxx,y = pos.y + yyy}
			currentTilename = surface.get_tile(new_position.x, new_position.y).name
			writeDebug("The current tile is: " .. currentTilename)
			
			if waterTiles[currentTilename] then
				Water_Nearby = true
			end
			
			if replaceableTiles[currentTilename] then
				table.insert(New_tiles, {name=replaceableTiles[currentTilename], position=new_position})	
			end
		end
	end
	
	if Water_Nearby then
		surface.set_tiles(New_tiles, false)
	else
	
		for xxx=-(size+6),(size+6) do
			for yyy=-(size+6),(size+6) do
				new_position = {x = pos.x + xxx,y = pos.y + yyy}
				currentTilename = surface.get_tile(new_position.x, new_position.y).name
				
				if waterTiles[currentTilename] then
					Water_Nearby = true
				end
				
			end
		end
	
		if Water_Nearby then 
			surface.set_tiles(New_tiles, false)
		else
			surface.set_tiles(New_tiles)
		end
		
	end
	
	
end

Re: Optimize Code - Tile Replacement

Posted: Mon Aug 22, 2016 6:59 am
by Adil
Well, that cycle inside of `if` which repeats the logic of the cycle before it doesn't look beautiful, but there's no apparent reason it won't work.
The code is readable, it is called not frequently, you shouldn't worry much about it.

As for playing nice with water, if that is your concern here, I guess the logic would become much more complicated, though I don't exactly understand, what do you want.

Re: Optimize Code - Tile Replacement

Posted: Mon Aug 22, 2016 5:01 pm
by TheSAguy
Okay, so currently I'm checking first a smaller square, if there is water, use the "false" version, if not, then check a larger square. The larger square includes the smaller square.
How can I stop looking at tiles as soon as the first one tests positive for water.
and/or how do I exclude the initial square from the second test.

Re: Optimize Code - Tile Replacement

Posted: Mon Aug 22, 2016 5:20 pm
by keyboardhack
TheSAguy wrote:Okay, so currently I'm checking first a smaller square, if there is water, use the "false" version, if not, then check a larger square. The larger square includes the smaller square.
How can I stop looking at tiles as soon as the first one tests positive for water.
and/or how do I exclude the initial square from the second test.
Below code stop the first two loops when water is found and the second two loops don't check the inner square. Also added local to two fields and switched two ifs to an elseif. Added comments in the code to tell where i did what.

Code: Select all

local replaceableTiles =
{
  ["grass"] = "grass-medium",
  ["grass-medium"] = "grass-dry",
  ["grass-dry"] = "sand",
  ["sand"] = "sand-dark",
  ["sand-dark"] = "dirt",
  ["dirt"] = "dirt-dark"
}

local waterTiles =
{
  ["deepwater"] = true,
  ["deepwater-green"] = true,
  ["water"] = true,
  ["water-green"] = true
}

---------------------------------------------
function Scorched_Earth(surface, pos, size)
	--- Turn the terrain into desert
	local New_tiles = {}
	local Water_Nearby = false
   
	for xxx = -size, size do
		for yyy = -size, size do
			--made local
			local new_position = {x = pos.x + xxx,y = pos.y + yyy}
			local currentTilename = surface.get_tile(new_position.x, new_position.y).name
			writeDebug("The current tile is: " .. currentTilename)

			if waterTiles[currentTilename] then
				Water_Nearby = true
				--added break to stop this loop
				break
			--replaced if with elseif
			elseif replaceableTiles[currentTilename] then
				table.insert(New_tiles, {name=replaceableTiles[currentTilename], position=new_position})   
			end
		end
		--if water nearby then stop loop
		if Water_Nearby then
			break
		end
	end

	if Water_Nearby then
		surface.set_tiles(New_tiles, false)
	else
		for xxx = -(size+6), (size+6) do
			for yyy = -(size+6), (size+6) do
				--no need to go through the tiles you already checked above
				--so only check tiles that are outside the square that was checked in the above for loops
				if xxx < -size or xxx > size or
				   yyy < -size or yyy > size then
					--made local
					local new_position = {x = pos.x + xxx,y = pos.y + yyy}
					local currentTilename = surface.get_tile(new_position.x, new_position.y).name

					if waterTiles[currentTilename] then
					   Water_Nearby = true
					   --added break to stop this loop
					   break
					end
				end
			end
			--if water nearby then stop loop
			if Water_Nearby then
				break
			end
		end

		if Water_Nearby then 
			surface.set_tiles(New_tiles, false)
		else
			surface.set_tiles(New_tiles)
		end
	end
end

Re: Optimize Code - Tile Replacement

Posted: Mon Aug 22, 2016 5:58 pm
by TheSAguy
Cool,
Okay, so the "break" only stops the current look it's in and not the entire function.
Good to know. I'll give this a test. Thanks keyboardhack!

Re: Optimize Code - Tile Replacement

Posted: Mon Aug 22, 2016 10:24 pm
by aubergine18
If you need to break out of multiple loops, you can use the `goto` keyword and label, for example:

Code: Select all

for k,v in pairs(whatever) do
  for a,b in pairs(something) do
    -- ...
    if bleh then goto foo end
  end -- for ab
end -- for kv
::foo::
http://lua-users.org/wiki/GotoStatement

Re: Optimize Code - Tile Replacement

Posted: Wed Aug 24, 2016 7:41 pm
by TheSAguy
Man, that land fill is hard to combat.
I still have cases where small bodies of water get filled, even with the below code:

Code: Select all

function Scorched_Earth(surface, pos, size)
	--- Turn the terrain into desert
	local New_tiles = {}
	local Water_Nearby_near = false
	local Water_Nearby_far = false
	local search_size = size + 1
   
	for xxx = -size, size do
		for yyy = -size, size do
			--made local
			local new_position = {x = pos.x + xxx,y = pos.y + yyy}
			local currentTilename = surface.get_tile(new_position.x, new_position.y).name
			writeDebug("The current tile is: " .. currentTilename)

			if waterTiles[currentTilename] then
				Water_Nearby_near = true
				--added break to stop this loop
				break
			--replaced if with elseif
			elseif replaceableTiles[currentTilename] then
				table.insert(New_tiles, {name=replaceableTiles[currentTilename], position=new_position})   
			end
		end
		--if water nearby then stop loop
		if Water_Nearby_near then
			break
		end
		
		for xxx = -(search_size), (search_size) do
			for yyy = -(search_size), (search_size) do
				--no need to go through the tiles you already checked above
				--so only check tiles that are outside the square that was checked in the above for loops
				if xxx < -size or xxx > size or
					yyy < -size or yyy > size then
					--made local
					local new_position = {x = pos.x + xxx,y = pos.y + yyy}
					local currentTilename = surface.get_tile(new_position.x, new_position.y).name

					if waterTiles[currentTilename] then
						Water_Nearby_near = true
						--added break to stop this loop
						break
					end
				end
			end
			--if water nearby then stop loop
			if Water_Nearby_near then
				break
			end
		end
		
	end

	if Water_Nearby_near then
		-- Water found, so don't replace any tiles.	
		--surface.set_tiles(New_tiles, false)
	else
		for xxx = -(search_size+5), (search_size+5) do
			for yyy = -(search_size+5), (search_size+5) do
				--no need to go through the tiles you already checked above
				--so only check tiles that are outside the square that was checked in the above for loops
				if xxx < -search_size or xxx > search_size or
					yyy < -search_size or yyy > search_size then
					--made local
					local new_position = {x = pos.x + xxx,y = pos.y + yyy}
					local currentTilename = surface.get_tile(new_position.x, new_position.y).name

					if waterTiles[currentTilename] then
						Water_Nearby_far = true
						--added break to stop this loop
						break
					end
				end
			end
			--if water nearby then stop loop
			if Water_Nearby_far then
				break
			end
		end

		if Water_Nearby_far then 
			surface.set_tiles(New_tiles, false)
		else
			surface.set_tiles(New_tiles)
		end
	end
end