There is shurly a better way to code these few lines.

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
Fuzzlix
Citizen
Posts: 53
Joined: Thu Oct 13, 2016 5:36 pm

There is shurly a better way to code these few lines.

Post by Fuzzlix » Mon Nov 13, 2017 4:40 pm

Hi all.
In my game i use a tilemap to create the worldmap. The tile choosen depends on the terrain type and the 8 neighbour terrains. The only interesting question was: The neighbour is the same terrain type as the center? I created a Mask of 8 bits for each neighbour. This was easy and later i figured out, i have to test for rivers in neighbour squares. I changed the mask so string type and used "0|1|R" as flags in the mask string. All working fine.

Now i try to improve speed by going back to integer keys. To handle the rivers on top/left/right/bottom i need 4 more bits in the integer key... All fine.

Now the task: I come in with 8 char strings like "0R0111R0". I need to copy the positions 2,4,5,7 to the left side of the string ( a 12 char string now). The 4 leftmost chars become "R"->"1" or 0. Finally the 4 orginal "R"s need to become replaced by "0".
Now i have a nice string with a binary number, i can give to tonumber().

My working solution is:
local function idx2i(key)
return tonumber(
key:gsub("([01])([01R])([01])([01R])([01R])([01])([01R])([01])", "%2%4%5%7%1%2%3%4%5%6%7%8")
:gsub("(.)", {["0"] = "0",["1"]="0",R="1"}, 4)
:gsub("R", "0"),
2
);
end;
It works but i am shure, it is not the best solution( and completely unoptimized: eg. replacement list should be predefined outside the function). This function may become called 2400 times while game starts up.

Any better solutions will make me very happy :)

davisdude
Party member
Posts: 1143
Joined: Sun Apr 28, 2013 3:29 am
Location: North Carolina

Re: There is shurly a better way to code these few lines.

Post by davisdude » Mon Nov 13, 2017 7:44 pm

I'd recommend looking in to this writeup here
GitHub | MLib - Math and shape intersections library | Walt - Animation library | Brady - Camera library with parallax scrolling | Vim-love-docs - Help files and syntax coloring for Vim

Nelvin
Prole
Posts: 7
Joined: Mon Sep 12, 2016 7:52 am

Re: There is shurly a better way to code these few lines.

Post by Nelvin » Mon Nov 13, 2017 11:04 pm

Whatever you do 2400 times during game startup should really not matter (well as long as we're talking about a few lines of code that don't do huge amounts of work).

Still if this would be something you'd have to optimize, you could skip the whole string modifications and just generate the final integer bitmask like so

Code: Select all

local lshift = require("bit").lshift

local byte1 = string.byte("1", 1)
local byteR = string.byte("R", 1)

local function idx2i( src )
    local bitmask = 0

    for i = 1, 8 do
        if src:byte(i) == byte1 then
            bitmask = bitmask + lshift(1,8-i)
        end
    end

    bitmask = bitmask + (src:byte(2) == byteR and lshift(1,12) or 0)
    bitmask = bitmask + (src:byte(4) == byteR and lshift(1,11) or 0)
    bitmask = bitmask + (src:byte(5) == byteR and lshift(1,10) or 0)
    bitmask = bitmask + (src:byte(7) == byteR and lshift(1,9) or 0)

    return bitmask
end

print( idx2i( "0R000001" ) )
=>4097

Fuzzlix
Citizen
Posts: 53
Joined: Thu Oct 13, 2016 5:36 pm

Re: There is shurly a better way to code these few lines.

Post by Fuzzlix » Tue Nov 14, 2017 12:24 pm

Thank you, Nelvin.
The logic, i want to implement is a little bit more complex, your tip is very inspiring for building up the neighbour mask though.

I try to do a worldmap like in the old dos game master of magic. A water square tile depends on where are landmass neighbour and just for making the possible choise amount 16 times bigger, the land neighbours may have a river/estuary. The old dos game had tiles for each possibility. this means i would need 256x16 different tiles. thats a lot and blows up the tilemap with rarly choosen tiles. (old MoM avoided some situations to reduse the tile count.) I went a different way. I divide a tile into 4 sub-tiles, one for each corner. i need only 32 sub-tiles and in case one tile is required, i create a spritebatch of 4 subtiles, i cache the spritebatch for later use. this works well.

While still in development, some tiles change from time to time. Each (sub)tile is a png file in my source folder. I count them at program start and create a canvas to create a tilemap at runtime. the png file names include the 8-char-mask i am talking about. I calculate the interger mask from those filenames using the code in the OP.

Nelvin
Prole
Posts: 7
Joined: Mon Sep 12, 2016 7:52 am

Re: There is shurly a better way to code these few lines.

Post by Nelvin » Tue Nov 14, 2017 3:10 pm

That's exactly what the code does, well, was meant tod :)I noticed I had an off by 1 error with regards to the "R" bits - I shifted them by the amount of their final position.
This fixed version should match the output of your function.

Code: Select all

local lshift = require("bit").lshift

local byte1 = string.byte("1", 1)
local byteR = string.byte("R", 1)

local function idx2i( src )
    local bitmask = 0

    for i = 1, 8 do
        if src:byte(i) == byte1 then
            bitmask = bitmask + lshift(1,8-i)
        end
    end

    bitmask = bitmask + (src:byte(2) == byteR and lshift(1,11) or 0)
    bitmask = bitmask + (src:byte(4) == byteR and lshift(1,10) or 0)
    bitmask = bitmask + (src:byte(5) == byteR and lshift(1,9) or 0)
    bitmask = bitmask + (src:byte(7) == byteR and lshift(1,8) or 0)

    return bitmask
end

Post Reply

Who is online

Users browsing this forum: Gibs and 6 guests