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