a performant way of picking random entities

Place to get help with not working mods / modding interface.
Post Reply
User avatar
adamwong246
Fast Inserter
Fast Inserter
Posts: 148
Joined: Tue Dec 01, 2020 5:01 am
Contact:

a performant way of picking random entities

Post by adamwong246 »

I'm making a mod that causes small, random damage to every entity on nauvis. Something like, every 1 minutes, pick 1 random entity from the entire map and cause 1% damage. But I'm having issues doing it performantly.

My (laggy) code ATM.

Code: Select all

function thingFallsApartMaybe()
	local nauvisSurface = game.get_surface("nauvis")
	local chunkIterator = nauvisSurface.get_chunks

	for chunk in chunkIterator() do
	  --log("x: " .. chunk.x .. ", y: " .. chunk.y)
	  --log("area: " .. serpent.line(chunk.area))

		local x = chunk.x + math.random(chunk.area.left_top.x, chunk.area.right_bottom.x)
		local y = chunk.y + math.random(chunk.area.left_top.y, chunk.area.right_bottom.y)

		local area = chunk.area
		local entities = nauvisSurface.find_entities(area)

		if entities then
			if next(entities) ~= nil then
			  log(entities[math.random(#entities)].name)
			end
		end
	end
end

script.on_nth_tick(60 * 1, thingFallsApartMaybe) --1 seconds

Is there a better way of doing this?

Choumiko
Smart Inserter
Smart Inserter
Posts: 1352
Joined: Fri Mar 21, 2014 10:51 pm
Contact:

Re: a performant way of picking random entities

Post by Choumiko »

The biggest issue with your code right now is that you don't break/return after logging the random entity, causing it to go over every chunk in the map and log the name every tick.
I added a break after the log and comparing it to my version showed that is still maybe a little bit slower but it didn't lag on my save

Code: Select all

local function thingFallsApartMaybe2(e)
    local nauvisSurface = game.get_surface("nauvis")
    local get_random_chunk = nauvisSurface.get_random_chunk
    local find_entities = nauvisSurface.find_entities
    while true do --potential for infinite loop, if there are no entities on the map (for freeplay there should always be the character?)
        local chunk = get_random_chunk()
        local left_top = {x = chunk.x * 32, y = chunk.y * 32}
        local entities = find_entities({left_top = left_top, right_bottom = {x = left_top.x + 32, y = left_top.y + 32}})
        if next(entities) then
            local random_entity = entities[math.random(#entities)]
            --game.players[1].teleport(random_entity.position)
            --game.print(tries)
            log(random_entity.name)
            --do the damage
            return
        end
    end
end

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: a performant way of picking random entities

Post by eradicator »

Choumiko wrote:
Wed Dec 02, 2020 7:50 am

Code: Select all

    while true do --potential for infinite loop, if there are no entities on the map (for freeplay there should always be the character?)
        local chunk = get_random_chunk()
That looks like a seriously bad idea. You might be getting the same empty chunk over and over again, even on an only half-empty map. If @OP wants to guarantee that at least one entity is found i'd recommend shoving the coordinates of all known chunks into a table and randomly selecting from that, remembering which entires you've already tried during each pass. Or even remembering them permanently so that the randomness is equally distributed over the map. Using on_chunk_generated you also don't have to re-build the table every time.

But...statistically speaking you've got a really good chance to either hit a tree, biter or resource entity. So...what would even be the point? The effect on gameplay would literally be zero.
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: 日本語, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

User avatar
adamwong246
Fast Inserter
Fast Inserter
Posts: 148
Joined: Tue Dec 01, 2020 5:01 am
Contact:

Re: a performant way of picking random entities

Post by adamwong246 »

I'd rather not access this data through a surface for that very reason. But I could not find anything in the api that could return "one random friendly entity with health", other than loading _every_ entity, then choosing a random one, but that can't possible be performant. So this led me to believe I could choose a random surface and then a random point on that surface and just let the RNG eventually hit a friendly entity.

The idea is to model a kind of random damage. Something like a lightening strike or a meteor impact.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: a performant way of picking random entities

Post by eradicator »

Hm. I guess you could use the on_built/destroy_* based events to track what chunks on a surface do contain player-built entities at all. Then the selection is easier. I doubt that even the engine itself has a "list of all entities of this force" so there's no shortcuts. It's either try to keep track (error prone) or in each cycle probe around until you find something (costly).
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: 日本語, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

Post Reply

Return to “Modding help”