• Welcome to the Kancolle Wiki!
  • If you have any questions regarding site content, account registration, etc., please visit the KanColle Wiki Discord

Difference between revisions of "Module:Iterator"

From Kancolle Wiki
Jump to navigation Jump to search
m
 
(16 intermediate revisions by 4 users not shown)
Line 6: Line 6:
  
 
local U = require('Module:Core')
 
local U = require('Module:Core')
local EquipmentData = require('Module:EquipmentData')
+
local Equipment = require('Module:Equipment')
local CollectionEquipment = require('Module:Collection/Equipment')
+
local Ship = require('Module:Ship')
 +
local CollectionShips = require('Module:Collection/Ships')
 +
local CollectionEquipment = require('Module:Data/Equipment')
 
local EnemyShip = require('Module:EnemyShip')
 
local EnemyShip = require('Module:EnemyShip')
 
local CollectionEnemy = require('Module:Collection/EnemyShips')
 
local CollectionEnemy = require('Module:Collection/EnemyShips')
Line 82: Line 84:
 
         end,
 
         end,
 
     }
 
     }
 +
end
 +
 +
function Iterator.shipsBy(context, n, pred, pre, nItems)
 +
    local predKey = stringKey('pred', context, n)
 +
    local pred2
 +
    if predKey then
 +
        pred2 = function(e)
 +
            local obj = Ship(e._name)
 +
            return obj[predKey](obj)
 +
        end
 +
    end
 +
    local preCollection
 +
    local collectionKey = stringKey('collection', context, n)
 +
    if collectionKey then
 +
        local _, CollectionData = U.requireModule(string.format("Collection/%s", collectionKey))
 +
        preCollection = U.icopy(CollectionData)
 +
    else
 +
        preCollection = U.icopy(CollectionShips)
 +
    end
 +
   
 +
    local allowedRemodels = context['allowedRemodels']
 +
    local listBase = (context['listBase'] == 'true')
 +
    local collection = {}
 +
    local index = 1
 +
    for i = 1, #preCollection do
 +
    local _, CollectionData = U.requireModule(string.format("Data/Ship/%s", preCollection[i]))
 +
    if _ and CollectionData then
 +
    for j,v in pairs(CollectionData) do
 +
    if v._suffix then
 +
    if (allowedRemodels == nil or allowedRemodels[v._suffix]) then
 +
    local fullName = string.format("%s/%s", v._name, v._suffix)
 +
    collection[index] = v
 +
    collection[index]._fullName = fullName
 +
    index = index + 1
 +
end
 +
    elseif v._id then
 +
if v._remodel_from or (v._name and listBase) then
 +
    collection[index] = v
 +
    collection[index]._fullName = v._name
 +
    index = index + 1
 +
    end
 +
    end
 +
end
 +
    end
 +
end
 +
   
 +
    local sortKey = stringKey('sort', context, n)
 +
    if sortKey then
 +
        table.sort(collection, function(a, b)
 +
            if a[sortKey] and b[sortKey] and a[sortKey] ~= b[sortKey] then
 +
                return a[sortKey] < b[sortKey]
 +
            elseif a._id == b._id then
 +
            return a._api_id < b._api_id
 +
            else
 +
                return a._id < b._id
 +
            end
 +
        end)
 +
    end
 +
 +
    local i = 1
 +
    local current = nil
 +
    local preFlag = true
 +
    local nCollection = nItems == true and #collection or nItems or #collection
 +
    return {
 +
        next = function()
 +
            for _ = i, nCollection do
 +
                local e
 +
                if nItems then
 +
                    e = U.ifindBy(collection, function(e) return e._id == i end) or { _id = i }
 +
                else
 +
                    e = collection[i]
 +
                end
 +
                if (pred and not pred(e, i)) or (pred2 and not pred2(e, i)) then
 +
                    i = i + 1
 +
                else
 +
                    if pre and preFlag then
 +
                        local value = pre(e, i)
 +
                        if value then
 +
                            current = value
 +
                            preFlag = false
 +
                            return true
 +
                        end
 +
                    end
 +
                    current = e._fullName
 +
                    if nItems and not current then
 +
                        current = '-'
 +
                    end
 +
                    i = i + 1
 +
                    preFlag = true
 +
                    return true
 +
                end
 +
            end
 +
            current = nil
 +
            return false
 +
        end,
 +
        current = function()
 +
            return current
 +
        end,
 +
    }
 +
end
 +
 +
function Iterator.shipsByType(context, n)
 +
    local type = numberKey('type', context, n)
 +
    return Iterator.shipsBy(context, n, function(e)
 +
        return type == nil or e._type == type
 +
    end)
 +
end
 +
 +
function Iterator.shipsByTrueId(context, n)
 +
    local from = numberKey('from', context, n, 1)
 +
    local to = numberKey('to', context, n, 1500)
 +
    return Iterator.shipsBy(context, n, function(e)
 +
        if e._true_id then
 +
            return e._true_id >= from and e._true_id <= to
 +
        else
 +
            return e._id >= from and e._id <= to
 +
        end
 +
    end)
 
end
 
end
  
Line 91: Line 211:
 
     if predKey then
 
     if predKey then
 
         pred2 = function(e)
 
         pred2 = function(e)
             local obj = EquipmentData(e)
+
             local obj = Equipment(e._name)
 
             return obj[predKey](obj)
 
             return obj[predKey](obj)
 
         end
 
         end
 
     end
 
     end
    mw.log(predKey)
 
    mw.log(pred2)
 
    mw.log(pred)
 
 
     local collection
 
     local collection
 
     local collectionKey = stringKey('collection', context, n)
 
     local collectionKey = stringKey('collection', context, n)
 
     if collectionKey then
 
     if collectionKey then
         local _, CollectionData = U.requireModule(string.format("Collection/%s", collectionKey))
+
         local _, CollectionData = U.requireModule(collectionKey == 'Equipment' and 'Data/Equipment' or string.format("Collection/%s", collectionKey))
 
         collection = U.icopy(CollectionData)
 
         collection = U.icopy(CollectionData)
 
     else
 
     else
Line 129: Line 246:
 
                     e = collection[i]
 
                     e = collection[i]
 
                 end
 
                 end
                mw.log(e._name)
 
                mw.log(pred and pred(e, i))
 
                mw.log(pred2 and pred2(e, i))
 
 
                 if pred and not pred(e, i) or pred2 and not pred2(e, i) then
 
                 if pred and not pred(e, i) or pred2 and not pred2(e, i) then
 
                     i = i + 1
 
                     i = i + 1
Line 163: Line 277:
 
function Iterator.equipmentById(context, n)
 
function Iterator.equipmentById(context, n)
 
     local from = numberKey('from', context, n, 1)
 
     local from = numberKey('from', context, n, 1)
     local to = numberKey('to', context, n, 500)
+
     local to = numberKey('to', context, n, 1500)
 
     return Iterator.equipmentBy(context, n, function(e)
 
     return Iterator.equipmentBy(context, n, function(e)
 
         return e._id >= from and e._id <= to
 
         return e._id >= from and e._id <= to
Line 171: Line 285:
 
function Iterator.equipmentByIdWithHeaders(context, n)
 
function Iterator.equipmentByIdWithHeaders(context, n)
 
     local from = numberKey('from', context, n, 1)
 
     local from = numberKey('from', context, n, 1)
     local to = numberKey('to', context, n, 500)
+
     local to = numberKey('to', context, n, 1500)
 
     local prevMod = 0
 
     local prevMod = 0
 
     return Iterator.equipmentBy(
 
     return Iterator.equipmentBy(
Line 194: Line 308:
 
function Iterator.equipmentByIdWithEmptyWithHeaders(context, n)
 
function Iterator.equipmentByIdWithEmptyWithHeaders(context, n)
 
     local from = numberKey('from', context, n, 1)
 
     local from = numberKey('from', context, n, 1)
     local to = numberKey('to', context, n, 500)
+
     local to = numberKey('to', context, n, 1500)
 
     local prevMod = 0
 
     local prevMod = 0
 
     local nItems = (math.floor(U.ilast(CollectionEquipment)._id / 10) + 1) * 10
 
     local nItems = (math.floor(U.ilast(CollectionEquipment)._id / 10) + 1) * 10
Line 249: Line 363:
 
         if ai and bi and ai ~= bi then
 
         if ai and bi and ai ~= bi then
 
             return ai < bi
 
             return ai < bi
 +
        elseif a._id and b._id and a._id ~= b._id then
 +
            return a._id < b._id
 
         else
 
         else
            return a._id < b._id
+
        return false
 
         end
 
         end
 
     end)
 
     end)
Line 304: Line 420:
 
      
 
      
 
     local selectBoss = stringKey('boss', context, n, ''):lower()
 
     local selectBoss = stringKey('boss', context, n, ''):lower()
 +
    -- treat unknwon _back as bosses? why it is unknwon?
 
     local predBoss = selectBoss == 'yes' and
 
     local predBoss = selectBoss == 'yes' and
         function(e) return e._back <= -11 end
+
         function(e) return (e._back or -11) <= -11 end
 
         or selectBoss == 'no' and
 
         or selectBoss == 'no' and
         function(e) return e._back >= -10 end
+
         function(e) return (e._back or -11) >= -10 end
 
         or
 
         or
 
         function(e) return true end
 
         function(e) return true end
Line 332: Line 449:
 
         mw.log()
 
         mw.log()
 
     end
 
     end
 +
   
 +
    testIterator('shipsByType', { type = '11', sort = '_class' , listBase = 'true'})
 +
    testIterator('shipsByType', { sort = '_name'})
 +
    testIterator('shipsByTrueId', { sort = '_id', from = '1', to = '20', listBase = 'true'})
 +
    testIterator('shipsBy', { pred = 'is_auxiliary' , sort = '_name'})
 +
   
 
     testIterator('equipmentById', { from = '11', to = '20' })
 
     testIterator('equipmentById', { from = '11', to = '20' })
 
     testIterator('equipmentByIdWithHeaders', { from = '1', to = '30' })
 
     testIterator('equipmentByIdWithHeaders', { from = '1', to = '30' })
Line 338: Line 461:
 
     testIterator('equipmentByTypeAndIcon', { type = '1', icon = '16' })
 
     testIterator('equipmentByTypeAndIcon', { type = '1', icon = '16' })
 
     testIterator('equipmentBy', { pred = 'is_large_caliber_main_gun', sort = '_type' })
 
     testIterator('equipmentBy', { pred = 'is_large_caliber_main_gun', sort = '_type' })
 +
   
 
     testIterator('enemiesByType', { type = '2' })
 
     testIterator('enemiesByType', { type = '2' })
 
end
 
end

Latest revision as of 15:02, 30 April 2023

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:Core')
local Equipment = require('Module:Equipment')
local Ship = require('Module:Ship')
local CollectionShips = require('Module:Collection/Ships')
local CollectionEquipment = require('Module:Data/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

-- * Ship iterators.

function Iterator.shipsByNo(context)
    local extra = stringKey('extra', context)
    local function prefix(i)
        if extra then
            i = i + 1300
        end
        if (i - 1) % 10 == 0 then
            local key = 'key' .. i
            context['custom_row_' .. key .. '_content'] = string.format('==No.%d-%d==', i, i - 1 + 10)
            return '!' .. key
        end
    end
    local collection = U.ifilter(require('Module:Collection/ShipsByNo'), function(e)
        if extra then
            return e.no >= 1301
        else
            return e.no < 1301
        end
    end)
    local i = 0
    local current
    local prefixFlag = true
    return {
        next = function()
            i = i + 1
            local e = collection[i]
            if e then
                local prefixValue = prefix(i)
                if prefixFlag and prefixValue then
                    i = i - 1
                    current = prefixValue
                    prefixFlag = false
                else
                    current = e.name or '-'
                    prefixFlag = true
                end
                return true
            end
            return false
        end,
        current = function()
            return current
        end,
    }
end

function Iterator.shipsBy(context, n, pred, pre, nItems)
    local predKey = stringKey('pred', context, n)
    local pred2
    if predKey then
        pred2 = function(e)
            local obj = Ship(e._name)
            return obj[predKey](obj)
        end
    end
    local preCollection
    local collectionKey = stringKey('collection', context, n)
    if collectionKey then
        local _, CollectionData = U.requireModule(string.format("Collection/%s", collectionKey))
        preCollection = U.icopy(CollectionData)
    else
        preCollection = U.icopy(CollectionShips)
    end
    
    local allowedRemodels = context['allowedRemodels']
    local listBase = (context['listBase'] == 'true')
    local collection = {}
    local index = 1
    for i = 1, #preCollection do
    	local _, CollectionData = U.requireModule(string.format("Data/Ship/%s", preCollection[i]))
    	if _ and CollectionData then
    		for j,v in pairs(CollectionData) do
    			if v._suffix then
    				if (allowedRemodels == nil or allowedRemodels[v._suffix]) then
    					local fullName = string.format("%s/%s", v._name, v._suffix)
    					collection[index] = v
    					collection[index]._fullName = fullName
    					index = index + 1
					end
    			elseif v._id then
					if v._remodel_from or (v._name and listBase) then
    					collection[index] = v
    					collection[index]._fullName = v._name
    					index = index + 1
    				end
    			end
			end
    	end
	end
    
    local sortKey = stringKey('sort', context, n)
    if sortKey then
        table.sort(collection, function(a, b)
            if a[sortKey] and b[sortKey] and a[sortKey] ~= b[sortKey] then
                return a[sortKey] < b[sortKey]
            elseif a._id == b._id then
            	return a._api_id < b._api_id
            else
                return a._id < b._id
            end
        end)
    end
		
    local i = 1
    local current = nil
    local preFlag = true
    local nCollection = nItems == true and #collection or nItems or #collection
    return {
        next = function()
            for _ = i, nCollection do
                local e
                if nItems then
                    e = U.ifindBy(collection, function(e) return e._id == i end) or { _id = i }
                else
                    e = collection[i]
                end
                if (pred and not pred(e, i)) or (pred2 and not pred2(e, i)) then
                    i = i + 1
                else
                    if pre and preFlag then
                        local value = pre(e, i)
                        if value then
                            current = value
                            preFlag = false
                            return true
                        end
                    end
                    current = e._fullName
                    if nItems and not current then
                        current = '-'
                    end
                    i = i + 1
                    preFlag = true
                    return true
                end
            end
            current = nil
            return false
        end,
        current = function()
            return current
        end,
    }
end

function Iterator.shipsByType(context, n)
    local type = numberKey('type', context, n)
    return Iterator.shipsBy(context, n, function(e)
        return type == nil or e._type == type
    end)
end

function Iterator.shipsByTrueId(context, n)
    local from = numberKey('from', context, n, 1)
    local to = numberKey('to', context, n, 1500)
    return Iterator.shipsBy(context, n, function(e)
        if e._true_id then
            return e._true_id >= from and e._true_id <= to
        else
            return e._id >= from and e._id <= to
        end
    end)
end

-- * Equipment iterators.

function Iterator.equipmentBy(context, n, pred, pre, nItems)
    local predKey = stringKey('pred', context, n)
    local pred2
    if predKey then
        pred2 = function(e)
            local obj = Equipment(e._name)
            return obj[predKey](obj)
        end
    end
    local collection
    local collectionKey = stringKey('collection', context, n)
    if collectionKey then
        local _, CollectionData = U.requireModule(collectionKey == 'Equipment' and 'Data/Equipment' or 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
    local nCollection = nItems == true and #collection or nItems or #collection
    return {
        next = function()
            for _ = i, nCollection do
                local e
                if nItems then
                    e = U.ifindBy(collection, function(e) return e._id == i end) or { _id = i }
                else
                    e = collection[i]
                end
                if pred and not pred(e, i) or pred2 and not pred2(e, i) then
                    i = i + 1
                else
                    if pre and preFlag then
                        local value = pre(e, i)
                        if value then
                            current = value
                            preFlag = false
                            return true
                        end
                    end
                    current = e._name
                    if nItems and not current then
                        current = '-'
                    end
                    i = i + 1
                    preFlag = true
                    return true
                end
            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, 1500)
    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, 1500)
    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
                local title = string.format("No. %s - %s", U.pad(e._id - currentMod, 3, "0"), U.pad(e._id - currentMod + 9, 3, "0"))
                return string.format("!#[[Equipment#%s|%s]]", title, title)
            else
                prevMod = currentMod
                return false
            end
        end
    )
end

function Iterator.equipmentByIdWithEmptyWithHeaders(context, n)
    local from = numberKey('from', context, n, 1)
    local to = numberKey('to', context, n, 1500)
    local prevMod = 0
    local nItems = (math.floor(U.ilast(CollectionEquipment)._id / 10) + 1) * 10
    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
                local title = string.format("No. %s - %s", U.pad(e._id - currentMod, 3, "0"), U.pad(e._id - currentMod + 9, 3, "0"))
                return string.format("!#[[Equipment#%s|%s]]", title, title)
            else
                prevMod = currentMod
                return false
            end
        end,
        nItems
    )
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.equipmentByIcon(context, n)
    return Iterator.equipmentBy(context, n, function(e)
        return e._icon == numberKey('icon', context, n)
    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.enemiesBy(context, n, pred, pre)
    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
        elseif a._id and b._id and a._id ~= b._id then
            return a._id < b._id
        else
        	return false
        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: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

function Iterator.enemiesByType(context, n)
    local type = numberKey('type', context, n)
    return Iterator.enemiesBy(context, n, function(e)
        return e:type() == type
    end)
end

function Iterator.enemiesByTypeAndInstallationAndBoss(context, n)
    local type = numberKey('type', context, n, 0)
    
    local selectInstallation = stringKey('installation', context, n, ''):lower()
    local predInstallation = selectInstallation == 'yes' and
        function(e) return e._speed == 0 end
        or selectInstallation == 'no' and
        function(e) return e._speed ~=0 end
        or
        function(e) return true end
    
    local selectBoss = stringKey('boss', context, n, ''):lower()
    -- treat unknwon _back as bosses? why it is unknwon?
    local predBoss = selectBoss == 'yes' and
        function(e) return (e._back or -11) <= -11 end
        or selectBoss == 'no' and
        function(e) return (e._back or -11) >= -10 end
        or
        function(e) return true end
    
    return Iterator.enemiesBy(context, n, function(e)
        return e._hp -- skip unimplemented units
        and (e:type() == type
            or type == 0
            or type < 0 and e:type() ~= -type) -- skip SS-typed bosses
        and predInstallation(e)
        and predBoss(e)
    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('shipsByType', { type = '11', sort = '_class' , listBase = 'true'})
    testIterator('shipsByType', { sort = '_name'})
    testIterator('shipsByTrueId', { sort = '_id', from = '1', to = '20', listBase = 'true'})
    testIterator('shipsBy', { pred = 'is_auxiliary' , sort = '_name'})
    
    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