Optimize Code - Tile Replacement

Place to get help with not working mods / modding interface.
TheSAguy
Smart Inserter
Smart Inserter
Posts: 1456
Joined: Mon Jan 13, 2014 6:17 pm
Contact:

Optimize Code - Tile Replacement

Post 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
User avatar
Adil
Filter Inserter
Filter Inserter
Posts: 945
Joined: Fri Aug 15, 2014 8:36 pm
Contact:

Re: Optimize Code - Tile Replacement

Post 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.
I do mods. Modding wiki is friend, it teaches how to mod. Api docs is friend too...
I also update mods, some of them even work.
Recently I did a mod tutorial.
TheSAguy
Smart Inserter
Smart Inserter
Posts: 1456
Joined: Mon Jan 13, 2014 6:17 pm
Contact:

Re: Optimize Code - Tile Replacement

Post 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.
keyboardhack
Filter Inserter
Filter Inserter
Posts: 478
Joined: Sat Aug 23, 2014 11:43 pm
Contact:

Re: Optimize Code - Tile Replacement

Post 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
Waste of bytes : P
TheSAguy
Smart Inserter
Smart Inserter
Posts: 1456
Joined: Mon Jan 13, 2014 6:17 pm
Contact:

Re: Optimize Code - Tile Replacement

Post 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!
User avatar
aubergine18
Smart Inserter
Smart Inserter
Posts: 1264
Joined: Fri Jul 22, 2016 8:51 pm
Contact:

Re: Optimize Code - Tile Replacement

Post 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
Better forum search for modders: Enclose your search term in quotes, eg. "font_color" or "custom-input" - it prevents the forum search from splitting on hypens and underscores, resulting in much more accurate results.
TheSAguy
Smart Inserter
Smart Inserter
Posts: 1456
Joined: Mon Jan 13, 2014 6:17 pm
Contact:

Re: Optimize Code - Tile Replacement

Post 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
Post Reply

Return to “Modding help”