Let’s clean up those free-hanging functions just for fun.

I’ve left those utility functions for extending, randomizing, and zipping up Decor arrays. They look like this:

function zip(array1, array2)
    assert(#array1==#array2, "arrays don't match "..#array1..","..#array2)
    local result = {}
    for i = 1,#array1 do
        table.insert(result, array1[i]..";"..array2[i]..";avoid(1)")
    end
    return result
end

function randomize(array)
    local copy = {}
    table.move(array,1,#array,1,copy)
    local result = {}
    while #copy > 0 do
        local itemNr = math.random(#copy)
        table.insert(result,copy[itemNr])
        table.remove(copy,itemNr)
    end
    return result
end

function stringFrom(array)
    return ar.reduce(array, "", function(str,item) return str..tostring(item) end)
end

function extendAll(countItemArray)
    local entries, residual
    local result = {}
    residual = countItemArray
    while (#residual) > 0 do
        entries,residual = extend(residual)
        table.move(entries,1,#entries, #result+1, result)
    end
    return result
end

function extend(countItemArray)
    local extended = extendSegment(countItemArray)
    local residual = {}
    table.move(countItemArray,3,#countItemArray,1,residual)
    return extended, residual
end

function extendSegment(countItemArray)
    result = {}
    for i = 1,countItemArray[1] do
        table.insert(result, countItemArray[2])
    end
    return result
end

In that form, they pollute the global namespace, and are likely to collide with other people’s names at some random future date, or would be in a real effort. So we’ll do well to bring them in from the cold.

Since the program’s name is D2, and D2 is short, we could use that. It’s tempting to use dung, as that is my pet name for the program. I think we’ll go with D2, as it is more seemly. Most of the Lua prefixes are lower case, like math and table, and I used lower case for my FP functions, ar, kv, and fp. Let’s follow that convention and go with … ok … du for “dungeon utilities”.

In Main, outside all functions, we’ll define it after checking for an existing definition.

assert(du==nil, "Cannot define `du`")
du = {}

OK now:

du.zip = function(array1, array2)
    assert(#array1==#array2, "arrays don't match "..#array1..","..#array2)
    local result = {}
    for i = 1,#array1 do
        table.insert(result, array1[i]..";"..array2[i]..";avoid(1)")
    end
    return result
end

And change all the references:

function DungeonBuilder:placeZippedDecor(decorIn, lootIn, count)
    local decor = extendAll(decorIn)
    local loot = randomize(extendAll(lootIn))
    return count + #self:fromDefs(du.zip(decor,loot))
end

And so on, I’ll spare you all of them. Tests run. Shall we commit or do them all? Do them all. Am I a bad person? Just takes a moment for each.

Test, commit: Put the decor extend and zip into du namespace.

All functions defined, calls changed. World is just a little better.

There are four other world-level functions in Main:

-- Generally Useful Functions

function clamp(lo,val,hi)
    if lo <= hi then
        return math.max(math.min(val,hi), lo)
    else
        return math.max(math.min(val,lo), hi)
    end
end

function randomValueAvoiding(vLow,vHigh,vMax)
    local numberToSkip = vHigh-vLow + 1
    local v = math.random(1, vMax - numberToSkip)
    if v >= vLow then
        v = v + numberToSkip
    end
    return v
end

function sign(x)
    return (x < 0 and -1) or (x == 0 and 0) or (x > 0 and 1)
end

function splitStringToTable(str)
    local lines = {}
    for s in str:gmatch("[^\r\n]+") do
        table.insert(lines, s)
    end
    return lines
end

Now that we have a place for them, let’s use it. Another few moments and those are done. Commit: move utility functions from Main to du, and all their uses.

Again, the world is just a slightly better place. I wish everything were this easy.