- 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"
Jump to navigation
Jump to search
m (50 revisions imported) |
|||
(13 intermediate revisions by 3 users not shown) | |||
Line 7: | Line 7: | ||
local U = require('Module:Core') | local U = require('Module:Core') | ||
local Equipment = require('Module:Equipment') | local Equipment = require('Module:Equipment') | ||
− | local CollectionEquipment = require('Module: | + | 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 98: | Line 218: | ||
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 157: | 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, | + | 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 165: | 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, | + | local to = numberKey('to', context, n, 1500) |
local prevMod = 0 | local prevMod = 0 | ||
return Iterator.equipmentBy( | return Iterator.equipmentBy( | ||
Line 188: | 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, | + | 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 243: | 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 false | |
end | end | ||
end) | end) | ||
Line 298: | 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 326: | 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 332: | 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