LuaSurface.count_tiles_filtered{} return {name=count} mapping per tile type.
Posted: Wed Nov 21, 2018 11:50 am
by eradicator
What?
A new option for LuaSurface.count_tiles_filtered{} that returns a list of counts by tile type in addition to the total count.
Details
For example LuaSurface.count_tiles_filtered{area=area,get_statistics=true} should return the total count as it does now but have a table as second return value:
Usecases
a) Adapting newly placed tiles to their surroundings.
b) Modding machines that do different things depending on where they are placed. E.g. a ground extractor that extracts dirt, sand, rock etc depending on where it is placed. (i.e. an reskinned assembler with forced recipe.) or wind-turbines that work better near water, etc...
Why?
Currently the only way to get these numbers is to either call find_tiles_filtered and do the count myself, or to loop game.tile_prototypes and call count_tiles_filtered for each name. If count_tiles_filtered could directly return the numbers there would be less overhead.
Re: LuaSurface.count_tiles_filtered{} return {name=count} mapping per tile type.
Posted: Wed Nov 21, 2018 1:44 pm
by Klonan
eradicator wrote: Wed Nov 21, 2018 11:50 am Why?
Currently the only way to get these numbers is to ... or to loop game.tile_prototypes and call count_tiles_filtered for each name.
Do you have some statistic on how long this takes?
Having a second return value that may not be used will slow it down for all usages
Re: LuaSurface.count_tiles_filtered{} return {name=count} mapping per tile type.
Posted: Thu Nov 22, 2018 4:35 pm
by eradicator
Klonan wrote: Wed Nov 21, 2018 1:44 pm
Having a second return value that may not be used will slow it down for all usages
I was hoping that if the second return value is only transfered to lua when requested it would be reasonably cheap. I've made some statistics and thought about it a bit more.
What i actually want/need:
a) A method to get per-type counts of all tiles in an area.
b) Same as a) but if a tile has a hidden_tile then the hidden_tile is counted instead of the surface tile, tiles without hidden_tile count normally.
The more i think about it the less sure i am what a good implementation would be (or if it's worth it to bother you :/). A completely new api function *cough*? Count_tiles with names={} instead of name=""?
Also for small radius scans manually counting find_tiles might be fast enough after all, and for large radius scans i only have one usecase so far (windpower) which could be tick-distributed (or ignore some tile types) to get it into "fast enough" regions.
Statistics Conclusion:
looping count_tiles:
+ fast with very large areas
- very slow with lots of modded tiles
manual counting find_tiles:
+ stable speed regardless of modded tiles
- slow for large areas in unmodded games
/c
--[[pregen chunks]]
s.request_to_generate_chunks({0,0},20)
s.force_generate_chunk_requests()
--[[trial functions]]
function manual_count_pairs (surface,radius)
local counts = {}
for _,tile in pairs(surface.find_tiles_filtered{
area={left_top={x=-1*radius,y=-1*radius},right_bottom={x=radius,y=radius}}
}) do
counts[tile.name] = (counts[tile.name] or 0) + 1
end
return counts
end
function manual_count_forx (surface,radius)
local counts = {}
local tiles = surface.find_tiles_filtered{
area={left_top={x=-1*radius,y=-1*radius},right_bottom={x=radius,y=radius}}
}
for i=1,#tiles do
counts[tiles[i].name] = (counts[tiles[i].name] or 0) + 1
end
return counts
end
function looped_count_pairs (surface,radius)
local counts = {}
for _,tprot in pairs(game.tile_prototypes) do
local c = surface.count_tiles_filtered{
area={left_top={x=-1*radius,y=-1*radius},right_bottom={x=radius,y=radius}},
name = tprot.name
}
counts[tprot.name] = (c ~= 0) and c or nil
end
return counts
end
--[[Sanity check]]
--[[
print('\nMANUAL COUNT PAIRS:')
print(serpent.block(manual_count_pairs(s,100)))
print('\nMANUAL COUNT FORX:')
print(serpent.block(manual_count_forx(s,100)))
print('\nLOOPED COUNT PAIRS:')
print(serpent.block(looped_count_pairs(s,100)))
--]]
/measured-command local f=manual_count_pairs local s=game.player.surface for i=1,1000 do f(s,6) end
/measured-command local f=manual_count_forx local s=game.player.surface for i=1,1000 do f(s,6) end
/measured-command local f=looped_count_pairs local s=game.player.surface for i=1,1000 do f(s,6) end
/measured-command local f=manual_count_pairs local s=game.player.surface for i=1,1000 do f(s,16) end
/measured-command local f=manual_count_forx local s=game.player.surface for i=1,1000 do f(s,16) end
/measured-command local f=looped_count_pairs local s=game.player.surface for i=1,1000 do f(s,16) end
/measured-command local f=manual_count_pairs local s=game.player.surface for i=1,500 do f(s,32) end
/measured-command local f=manual_count_forx local s=game.player.surface for i=1,500 do f(s,32) end
/measured-command local f=looped_count_pairs local s=game.player.surface for i=1,500 do f(s,32) end
/measured-command local f=manual_count_pairs local s=game.player.surface for i=1,100 do f(s,64) end
/measured-command local f=manual_count_forx local s=game.player.surface for i=1,100 do f(s,64) end
/measured-command local f=looped_count_pairs local s=game.player.surface for i=1,100 do f(s,64) end