Starbeamrainbowlabs

Stardust
Blog


Archive

Mailing List Articles Atom Feed Comments Atom Feed Twitter Reddit Facebook

Tag Cloud

3d account algorithms announcement architecture archives arduino artificial intelligence artix assembly async audio bash batch blog bookmarklet booting c sharp c++ challenge chrome os code codepen coding conundrums coding conundrums evolved command line compilers compiling css dailyprogrammer debugging demystification distributed computing downtime electronics email embedded systems encryption es6 features event experiment external first impressions future game github github gist graphics hardware hardware meetup holiday html html5 html5 canvas infrastructure interfaces internet io.js jabber javascript js bin labs learning library linux low level lua maintenance manjaro network networking node.js operating systems performance photos php pixelbot portable privacy programming problems project projects prolog protocol protocols pseudo 3d python reddit reference release releases resource review rust secrets security series list server software sorting source code control statistics svg technical terminal textures three thing game three.js tool tutorial tutorials twitter ubuntu university update updates upgrade version control virtual reality virtualisation visual web website windows windows 10 xmpp xslt

Language Review: Lua

The Lua logo

I have recently tried writing a bit of Lua. I ported an implementation of a maze generation algorithm I came up with from Python to try it out:

-------------------------------------
-- Maze generation script
-------------------------------------
-- A test by @Starbeamrainbowlabs

---------------------------------------------
-- Intelligent table printing function
---------------------------------------------
-- From http://coronalabs.com/blog/2014/09/02/tutorial-printing-table-contents/

function print_r ( t )
    local print_r_cache={}
    local function sub_print_r(t,indent)
        if (print_r_cache[tostring(t)]) then
            print(indent.."*"..tostring(t))
        else
            print_r_cache[tostring(t)]=true
            if (type(t)=="table") then
                for pos,val in pairs(t) do
                    if (type(val)=="table") then
                        print(indent.."["..pos.."] => "..tostring(t).." {")
                        sub_print_r(val,indent..string.rep(" ",string.len(pos)+8))
                        print(indent..string.rep(" ",string.len(pos)+6).."}")
                    elseif (type(val)=="string") then
                        print(indent.."["..pos..'] => "'..val..'"')
                    else
                        print(indent.."["..pos.."] => "..tostring(val))
                    end
                end
            else
                print(indent..tostring(t))
            end
        end
    end
    if (type(t)=="table") then
        print(tostring(t).." {")
        sub_print_r(t,"  ")
        print("}")
    else
        sub_print_r(t,"  ")
    end
    print()
end

if arg[1] ~= nil then
        width = tonumber(arg[1])
else
        width = 36
end
if arg[2] ~= nil then
        height = tonumber(arg[2])
else
        height = 16
end

----------------------------------
-- function to print out the world
----------------------------------
function printspace(space, w, h)
        for y = 0, h, 1 do
                local line = ""
                for x = 0, w, 1 do
                        line = line .. space[y][x]
                end
                print(line)
        end
end

-- Initialise the world
start_time = os.clock()
math.randomseed(os.time()) -- seed the random number generator with the system clock
world = {}
for y = 0, height, 1 do
        world[y] = {}
        for x = 0, width, 1 do
                world[y][x] = "#"
        end
end

-- do a random walk to create pathways
nodes = {} -- the nodes left that we haven't investigated
curnode = 1 -- the node we are currently operating on
cx, cy = 1, 1 -- our current position
table.insert(nodes, { x = cx, y = cy })

world[cy][cx] = " "
while #nodes > 0 do
        io.write("Nodes left: " .. curnode .. "\r")
        --print("Nodes left: " .. #nodes)
        --print("Currently at (" .. cx .. ", " .. cy .. ")")

        local directions = "" -- the different directions we can move
        if cy - 2 > 0 and world[cy - 2][cx] == "#" then
                directions = directions .. "u"
        end
        if cy + 2 < height and world[cy + 2][cx] == "#" then
                directions = directions .. "d"
        end
        if cx - 2 > 0 and world[cy][cx - 2] == "#" then
                directions = directions .. "l"
        end
        if cx + 2 < width and world[cy][cx + 2] == "#" then
                directions = directions .. "r"
        end
        --print("radar output: '" .. directions .. "' (length: " .. #directions .. "), curnode: " .. curnode)
        if #directions > 0 then
                -- we still have somewhere that we can go
                --print("This node is not a dead end yet.")
                local curdirnum = math.random(0, #directions)
                local curdir = string.sub(directions, curdirnum, curdirnum)
                if curdir == "u" then
                        world[cy - 1][cx] = " "
                        world[cy - 2][cx] = " "
                        cy = cy - 2
                elseif curdir == "d" then
                        world[cy + 1][cx] = " "
                        world[cy + 2][cx] = " "
                        cy = cy + 2
                elseif curdir == "l" then
                        world[cy][cx - 1] = " "
                        world[cy][cx - 2] = " "
                        cx = cx - 2
                elseif curdir == "r" then
                        world[cy][cx + 1] = " "
                        world[cy][cx + 2] = " "
                        cx = cx + 2
                end

                table.insert(nodes, { x = cx, y = cy })
        else
                --print("The node at " .. curnode .. " is a dead end.")
                table.remove(nodes, curnode)
                if #nodes > 0 then
                        --print("performing teleport.");
                        curnode = math.random(1, #nodes)
                        --print("New node: " .. curnode)
                        -- print("Nodes table: ")
                        -- print_r(nodes)
                        cx = nodes[curnode]["x"]
                        cy = nodes[curnode]["y"]
                else
                        --print("Maze generation complete, no teleportation necessary.")
                end
        end
        --printspace(world, width, height)
end

printspace(world, width, height)
end_time = os.clock()
print("Generation completed in " .. (end_time - start_time) .. "s.")

I originally wrote it in Python 3 (I might post about the game it is part of at some point). After trying Lua for a bit I thought it would be a good idea to write up a language review about it.

Firstly, it is really easy to get started with. I was able to compile Lua from source using my MinGW on my Windows 7 machine. Lua is also really lightweight (500kb in total!).

The problems begin when you start looking at Lua's equivalent of arrays: tables. I found that they feel clunky and outdated as there appears to be a lack of manipulation functions. Those that do exist (table.insert() and table.remove() use a lot more characters to use than the equivalent in other languages, such as Javascript (e.g. table.insert(tablename, "somestring") is 40 characters, compared to Javascript's tablename.push("somestring"), which is only 28 characters - a 30% saving!)

Lua's tables also start indexing from 1, not 0 - I found this to be a source of much confusion when I was experimenting with it.

The other thing I found annoying is that strings in Lua are immutable - which means that you can't change them once you have declared them. This can lead to lots of nasty performance issues in programs that do a lot of string manipulation if you are not very careful since every time you re-set a string variable's contents, you are creating more work for the garbage collector.

All in all, Lua looks like an interesting language to learn for fun - you should definitely check it out, if only to see how odd a language it is. I love how lightweight it is. I also managed to build the Lua interpreter from source too, which is always a plus. I can't see myself using it for any real project any time soon though - it just feels too clunky to work with for my purposes, although this is probably more down to my lack of experience and the other languages that I know than the design of the language itself.

Art by Mythdael