Testing whether an _area_ has been charted

Place to post guides, observations, things related to modding that are not mods themselves.
Pi-C
Smart Inserter
Smart Inserter
Posts: 1742
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Testing whether an _area_ has been charted

Post by Pi-C »

We have force.is_chunk_charted(surface, position) to check if a specific point is in a charted chunk. Is there something similar that would do the same for the complete area (rectangle or circle) around a position?

In this particular case, I want to check if a square area ABCD is fully charted before doing anything. The sides AB, BC, CD, and DA have a maximum length of 600 tiles. The naive way would be to check that the 4 corners A, B, C, and D are all in charted chunks. However, due to the sides being considerably longer than one chunk (600 vs. 32 tiles), it could happen that A and B are in charted chunks, but the line AB goes through uncharted area. Therefore, I'd have to check not only the corner points, but also the lines (in 32 tile intervals) between them, i.e. 4 + 4 * int(side_length/32) times, which amounts to 76 positions for my 600x600 area. EDIT: Thinking again, it's even more! In an area of that size, I should make sure that not only the borders have been charted, but everything within the area, so I should check all positions in a grid, thus 4 + int(side_length/32) * int(side_length/32) positions -- 328 for a 600x600 area.

The maps that I want to be found are loot of trees. Loot is given with a probability of 0.005 for the 600x600 maps, and with a probability of 0.015 for the small 200x200 maps. That's not much per se, but if a player kills a forest with poison, grenades, fire, or nukes, many trees will die at once, so it's not unlikely that several maps are given as loot in a short period. I already make sure that each tree will give at most one map (the biggest one if several are in the loot), and if the tree dies from fire or explosive damage caused by a player (if caused by wild fires and similar mod events, all maps will be removed from the loot) I will even remove small maps and reduce big maps to something between 25% and 90% of their original size, so there's already some optimization. (On second thought, 600 tiles is a lot, perhaps I should use 300 tiles not as radius, but as diameter.) Still, I'm concerned that checking up to 76 positions for each map would take quite a dent out of performance. Is that really so, or can I use force.is_chunk_charted() that often without giving it a second thought?
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!
User avatar
darkfrei
Smart Inserter
Smart Inserter
Posts: 2905
Joined: Thu Nov 20, 2014 11:11 pm
Contact:

Re: Testing whether an _area_ has been charted

Post by darkfrei »

Save this 4 positions as top_left (tl) and bottom_right (br) positions to the global.
Check the chunk on chunk charted
https://lua-api.factorio.com/latest/eve ... nk_charted

if tl.x ≤ x ≤ br.x and tl.y ≤ y ≤ br.y then it's your area
Pi-C
Smart Inserter
Smart Inserter
Posts: 1742
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: Testing whether an _area_ has been charted

Post by Pi-C »

darkfrei wrote: Thu Oct 01, 2020 10:32 am Save this 4 positions as top_left (tl) and bottom_right (br) positions to the global.
Check the chunk on chunk charted
This would be easy if it was only about one chunk. But even an area of 100 x 100 tiles is larger than 9 chunks (3x32 tiles per x and y).
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!
User avatar
darkfrei
Smart Inserter
Smart Inserter
Posts: 2905
Joined: Thu Nov 20, 2014 11:11 pm
Contact:

Re: Testing whether an _area_ has been charted

Post by darkfrei »

Pi-C wrote: Thu Oct 01, 2020 10:41 am
darkfrei wrote: Thu Oct 01, 2020 10:32 am Save this 4 positions as top_left (tl) and bottom_right (br) positions to the global.
Check the chunk on chunk charted
This would be easy if it was only about one chunk. But even an area of 100 x 100 tiles is larger than 9 chunks (3x32 tiles per x and y).
You are need to save your coordinates as chunk coordinates, the whole chunk [32, 32] to (64, 64) tiles is a chunk with coordinate {x=1, y=1}.
Pi-C
Smart Inserter
Smart Inserter
Posts: 1742
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: Testing whether an _area_ has been charted

Post by Pi-C »

darkfrei wrote: Thu Oct 01, 2020 1:57 pm
Pi-C wrote: Thu Oct 01, 2020 10:41 am
darkfrei wrote: Thu Oct 01, 2020 10:32 am Save this 4 positions as top_left (tl) and bottom_right (br) positions to the global.
Check the chunk on chunk charted
This would be easy if it was only about one chunk. But even an area of 100 x 100 tiles is larger than 9 chunks (3x32 tiles per x and y).
You are need to save your coordinates as chunk coordinates, the whole chunk [32, 32] to (64, 64) tiles is a chunk with coordinate {x=1, y=1}.
Ah, thanks! I've missed the on_chunk_charted part, now it makes sense! So, for an easy look-up, I guess I should use something like this structure: global.force.chunks_x = {x = {y=true}}, and on testing all chunks in the map area for position {x, y}, I'd just check if global.force.chunks_x[x][y] exists. But now I've got to use global again, with all kinds of events like on_player_joined_force or _on_force_created …Geez, I thought I could make a simple mod at least once! :mrgreen:
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!
User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5211
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Testing whether an _area_ has been charted

Post by eradicator »

"Never guess about performance"®☮™. You should always measure. Below function takes ~120 MICROseconds to scan 100 chunks for chartedness. That's 0.1 milliseconds, or less than a hundreths of a single tick. For partially uncharted areas it will stop checking on the first uncharted chunk, making it even faster.

Totally not worth it trying to cache the chartedness, especially considering that chunks can become uncharted without raising any events.

Code: Select all

  local is_fully_charted = function()
    local s = game.player.surface
    local f = game.player.force.is_chunk_charted --[[~20% faster to cache the function]]
    for x=1,10 do
      for y=1,10 do
        if not f(s,{x,y}) then --[[ 2~3 microseconds faster than {x=x,y=y} ]]
          return false
          end
        end
      end
    return true
    end
    
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.
Pi-C
Smart Inserter
Smart Inserter
Posts: 1742
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: Testing whether an _area_ has been charted

Post by Pi-C »

eradicator wrote: Thu Oct 01, 2020 5:10 pm "Never guess about performance"®☮™. You should always measure. Below function takes ~120 MICROseconds to scan 100 chunks for chartedness. [...]

Totally not worth it trying to cache the chartedness, especially considering that chunks can become uncharted without raising any events.
Convincing argument. So, the Maps mod doesn't store charted chunks when checking maps for now. It has a look-up list just like the one darkfrei suggested for chunks, but for keeping track of map markers. The list will be cleaned when a map is picked up, so unlike chunks that will have to be stored forever (unless they are deleted/uncharted again) they won't increase the size of saved games forever.

Code: Select all

    local f = game.player.force.is_chunk_charted --[[~20% faster to cache the function]]
Simple but elegant. :-)
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!
Post Reply

Return to “Modding discussion”