Module:Iterator

Revision as of 19:44, 27 April 2018 by がか (talk | contribs)

Documentation for this module may be created at Module:Iterator/doc

-- [[Category:Todo]]:
-- more generic interface and compositions (filtering, grouping, mapping, sorting, etc.)
-- prevent clients from infinite loops
-- load modules more lazily?
-- generalize equipment/enemy iterators

local U = require('Module:Utils')
local EquipmentData = require('Module:EquipmentData')
local CollectionEquipment = require('Module:Collection/Equipment')
local EnemyShip = require('Module:EnemyShip')
local CollectionEnemy = require('Module:Collection/EnemyShips')

local Iterator = {}

-- legacy, not a proper iterator
function Iterator.array(arr, i, n)
    i = i or 0
    n = n and math.min(#arr, n) or #arr
    function step(n, i)
        if i < n then
            i = i + 1
            return i, arr[i]
        end
    end
    return function()
        return step, n, i
    end
end

function stringKey(name, context, n, default)
    return context and context[name .. (n and tostring(n) or '')] or default
end

function numberKey(name, context, n, default)
    return context and tonumber(context[name .. (n and tostring(n) or '')]) or default
end

-- * Equipment iterators.

function Iterator.equipmentBy(context, n, pred, pre)
    local predKey = stringKey('pred', context, n)
    if predKey then
        pred = function(e)
            local obj = EquipmentData(e)
            return obj[predKey](obj)
        end
    end
    local collection
    local collectionKey = stringKey('collection', context, n)
    if collectionKey then
        local _, CollectionData = U.requireModule(string.format("Collection/%s", collectionKey))
        collection = U.icopy(CollectionData)
    else
        collection = U.icopy(CollectionEquipment)
    end
    local sortKey = stringKey('sort', context, n)
    if sortKey then
        table.sort(collection, function(a, b)
            if a[sortKey] ~= b[sortKey] then
                return a[sortKey] < b[sortKey]
            else
                return a._id < b._id
            end
        end)
    end
    local i = 1
    local current = nil
    local preFlag = true
    return {
        next = function()
            for _ = i, #collection do
                local e = collection[i]
                if pred(e, i) then
                    if pre and preFlag then
                        local value = pre(e, i)
                        if value then
                            current = value
                            preFlag = false
                            return true
                        end
                    end
                    current = e._name
                    i = i + 1
                    preFlag = true
                    return true
                end
                i = i + 1
            end
            current = nil
            return false
        end,
        current = function()
            return current
        end,
    }
end

function Iterator.equipmentById(context, n)
    local from = numberKey('from', context, n, 1)
    local to = numberKey('to', context, n, 500)
    return Iterator.equipmentBy(context, n, function(e)
        return e._id >= from and e._id <= to
    end)
end

function Iterator.equipmentByIdWithHeaders(context, n)
    local from = numberKey('from', context, n, 1)
    local to = numberKey('to', context, n, 500)
    local prevMod = 0
    return Iterator.equipmentBy(
        context, n,
        function(e)
            return e._id >= from and e._id <= to
        end,
        function(e)
            local currentMod = (e._id - 1) % 10
            if currentMod <= prevMod then
                prevMod = currentMod
                return "!#No " .. U.pad(e._id - currentMod, 3, "0") .. " - " .. U.pad(e._id - currentMod + 9, 3, "0")
            else
                prevMod = currentMod
                return false
            end
        end
    )
end

function Iterator.equipmentByType(context, n)
    local type = numberKey('type', context, n)
    return Iterator.equipmentBy(context, n, function(e)
        return e._type == type
    end)
end

function Iterator.equipmentByTypeAndIcon(context, n)
    local type = numberKey('type', context, n)
    local icon = numberKey('icon', context, n)
    return Iterator.equipmentBy(context, n, function(e)
        return e._type == type and e._icon == icon
    end)
end

-- * Enemy iterators.

function Iterator.enemiesByType(context, n)
    local collection = U.imap(CollectionEnemy, function(name)
        return EnemyShip(name)
    end)
    table.sort(collection, function(a, b)
        local ai = U.ifind(CollectionEnemy, a:base_name())
        local bi = U.ifind(CollectionEnemy, b:base_name())
        if ai and bi and ai ~= bi then
            return ai < bi
        else
            return a._id < b._id
        end
    end)
    local type = numberKey('type', context, n)
    local pred = function(e, i)
        return e:type() == type
    end
    local pre
    local i = 1
    local current = nil
    local preFlag = true
    return {
        next = function()
            for _ = i, #collection do
                local e = collection[i]
                if pred(e, i) then
                    if pre and preFlag then
                        local value = pre(e, i)
                        if value then
                            current = value
                            preFlag = false
                            return true
                        end
                    end
                    current = e:lua_name()
                    i = i + 1
                    preFlag = true
                    return true
                end
                i = i + 1
            end
            current = nil
            return false
        end,
        current = function()
            return current
        end,
    }
end

-- * Tests.

function Iterator.test()
    function testIterator(name, args)
        local iterator = Iterator[name](args)
        mw.log(name)
        while iterator.next() do
            mw.log('  ' .. (iterator.current() or '?'))
        end
        mw.log()
    end
    testIterator('equipmentById', { from = '11', to = '20' })
    testIterator('equipmentByIdWithHeaders', { from = '1', to = '30' })
    testIterator('equipmentByType', { type = '2' })
    testIterator('equipmentByType', { type = '1', sort = '_icon' })
    testIterator('equipmentByTypeAndIcon', { type = '1', icon = '16' })
    testIterator('equipmentBy', { pred = 'is_large_caliber_main_gun', sort = '_type' })
    testIterator('enemiesByType', { type = '2' })
end
-- p.test()

return Iterator