Module:LoRUtility
Documentation for this module may be created at Module:LoRUtility/doc
-- <pre>
-- Inherit from https://github.com/SwitchbladeBot/runeterra
local lib = require('Module:Feature')
local MAX_KNOWN_VERSION = 2
local p = {}
local VarInt = {}
local BitOperator = {}
local Utility = {}
local REGIONS = {
{code = "DE", name = "Demacia"},
{code = "FR", name = "Freljord"},
{code = "IO", name = "Ionia"},
{code = "NX", name = "Noxus"},
{code = "PZ", name = "Piltover and Zaun"},
{code = "SI", name = "Shadow Isles"},
{code = "BW", name = "Bilgewater"}
}
function base32_decode(str)
local base32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
local binary =
str:gsub(
".",
function(char)
if char == "=" then
return ""
end
local pos = string.find(base32Alphabet, char)
pos = pos - 1
return string.format("%05u", Utility.dec2bin(pos))
end
)
local bytes = Utility.str_split(binary, 8)
local result = {}
for _, byte in pairs(bytes) do
table.insert(result, tonumber(byte, 2))
end
table.remove(result, #result)
return result
end
function p.deckListFromCode(frame)
local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
local deckCode = args['code'] or nil
local lorData = mw.loadData("Module:LoRData/data")
local bytes = base32_decode(deckCode)
local firstByte = bytes[1]
local result = {}
table.remove(bytes, 1)
local version = BitOperator.AND(tonumber(firstByte), 0xF)
if (version > MAX_KNOWN_VERSION) then
return "The provided code requires a higher version of this library; please update."
end
for i = 3, 1, -1 do
local numGroupOfs = tonumber(VarInt.Pop(bytes)) or 0
for j = 1, numGroupOfs, 1 do
local numOfsInThisGroup = tonumber(VarInt.Pop(bytes)) or 0
local set = VarInt.Pop(bytes)
local faction = VarInt.Pop(bytes)
for k = 1, numOfsInThisGroup, 1 do
local card = VarInt.Pop(bytes)
if card ~= nil then
local setString = Utility.padLeft(tostring(set), 2, "0")
local factionData = REGIONS[faction + 1]
local cardString = Utility.padLeft(tostring(card), 3, "0")
local cardCode = setString .. factionData.code .. cardString
if (lorData[cardCode] == nil) then
return 'Card Code "' .. cardCode .. '" is not exist'
end
table.insert(
result,
{
code = cardCode,
count = tonumber(i),
region = factionData.name
}
)
end
end
end
end
while #bytes > 0 do
local fourPlusCount = VarInt.Pop(bytes)
local fourPlusSet = VarInt.Pop(bytes)
local fourPlusFaction = VarInt.Pop(bytes)
local fourPlusNumber = VarInt.Pop(bytes)
if fourPlusFaction == nil then
return 'Invalid Deck Code'
end
local fourPlusSetString = Utility.padLeft(tostring(fourPlusSet), 2, "0")
local fourPlusFactionData = REGIONS[fourPlusFaction + 1]
local fourPlusNumberString = Utility.padLeft(tostring(fourPlusNumber), 3, "0")
local cardCode = fourPlusSetString .. fourPlusFactionData.code .. fourPlusNumberString
if (lorData[cardCode] == nil) then
return 'Card Code "' .. cardCode .. '" is not exist'
end
table.insert(
result,
{
code = cardCode,
count = tonumber(fourPlusCount),
region = fourPlusFactionData.name
}
)
end
return result
end
function p.deckDecoder(frame)
local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
local deckCode = args['code'] or nil
if deckCode == nil then
return 'Empty Code'
end
local groupBy = args['groupBy'] or 'region'
local groupByValues = {'region', 'type', 'none'}
if Utility.table_contains(groupByValues, groupBy) == false then
return 'Only support Group By "region", "type" or none'
end
local lorData = mw.loadData("Module:LoRData/data")
local result = {}
local deckList = p.deckListFromCode{code=deckCode}
if type(deckList) ~= 'table' then
return deckList
end
for _, deckData in pairs(deckList) do
local cardData = lorData[deckData.code]
local cardType = cardData.type
if cardType == "Unit" then
if cardData.supertype ~= nil and cardData.supertype == "Champion" then
cardType = "Champion"
else
cardType = "Follower"
end
end
local groupByKey = ''
if groupBy == 'region' then
groupByKey = deckData.region
elseif groupBy == 'type' then
groupByKey = cardType .. 's'
else
groupByKey = 'List'
end
if result[groupByKey] == nil then
result[groupByKey] = {}
end
table.insert(
result[groupByKey],
{
code = deckData.code,
count = deckData.count,
name = cardData.name,
type = cardType
}
)
end
local sortedKeys = {}
for k in pairs(result) do
table.insert(sortedKeys, k)
end
table.sort(sortedKeys)
local template = ""
local newline = "\n"
template = template .. "<tabber>" .. newline
template = template .. "Hide=" .. newline .. '|-|' .. newline
for i, groupByKey in ipairs(sortedKeys) do
if i ~= 1 then
template = template .. "|-|" .. newline
end
template = template .. groupByKey .. "=" .. newline
local regionData = result[groupByKey]
table.sort(
regionData,
function(a, b)
if (a.type == b.type) then
return a.name < b.name
end
return a.type < b.type
end
)
local type = ""
for _, cardData in ipairs(regionData) do
if cardData.type ~= type then
if (type ~= "") then
template = template .. "}}" .. newline
end
if groupBy == 'region' or groupBy == 'none' then
template = template .. ";" .. cardData.type .. "s" .. newline
end
template = template .. "{{column|3|" .. newline
type = cardData.type
end
local cardCodeTemplate = "* {{LoR|" .. cardData.name
if cardData.type ~= "Champion" then
cardCodeTemplate = cardCodeTemplate .. "|code=" .. cardData.code
end
cardCodeTemplate = cardCodeTemplate .. "}}"
if cardData.count > 1 then
cardCodeTemplate = cardCodeTemplate .. " (x" .. cardData.count .. ")"
end
template = template .. cardCodeTemplate .. newline
end
template = template .. "}}" .. newline
end
template = template .. "</tabber>"
return frame:preprocess(template)
end
-- VarInit
VarInt.AllButMSB = 0x7f
VarInt.JustMSB = 0x80
function VarInt.Pop(bytes)
local result = 0
local currentShift = 0
local bytesPopped = 0
for i = 1, #bytes do
bytesPopped = bytesPopped + 1
local current = BitOperator.AND(bytes[i], VarInt.AllButMSB)
result = BitOperator.OR(result, BitOperator.RIGHT_SHIFT(current, currentShift))
if BitOperator.AND(bytes[i], VarInt.JustMSB) ~= VarInt.JustMSB then
Utility.table_splice(bytes, 1, bytesPopped)
return result
end
currentShift = currentShift + 7
end
-- error('Invalid Deck Code: Byte array did not contain valid varints.')
return nil
end
-- BitOperator
function BitOperator.OR(m, n)
local tbl_m = Utility.to_bits(m)
local tbl_n = Utility.to_bits(n)
Utility.expand(tbl_m, tbl_n)
local tbl = {}
local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n))
for i = 1, rslt do
if (tbl_m[i] == 0 and tbl_n[i] == 0) then
tbl[i] = 0
else
tbl[i] = 1
end
end
return Utility.table2number(tbl)
end
function BitOperator.AND(m, n)
local tbl_m = Utility.to_bits(m)
local tbl_n = Utility.to_bits(n)
Utility.expand(tbl_m, tbl_n)
local tbl = {}
local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n))
for i = 1, rslt do
if (tbl_m[i] == 0 or tbl_n[i] == 0) then
tbl[i] = 0
else
tbl[i] = 1
end
end
return Utility.table2number(tbl)
end
function BitOperator.RIGHT_SHIFT(n, bits)
Utility.check_int(n)
local high_bit = 0
if (n < 0) then
-- negative
n = BitOperator.NOT(math.abs(n)) + 1
high_bit = 2147483648 -- 0x80000000
end
for i = 1, bits do
n = n / 2
n = BitOperator.OR(math.floor(n), high_bit)
end
return math.floor(n)
end
function BitOperator.NOT(n)
local tbl = Utility.to_bits(n)
local size = math.max(table.getn(tbl), 32)
for i = 1, size do
if (tbl[i] == 1) then
tbl[i] = 0
else
tbl[i] = 1
end
end
return Utility.table2number(tbl)
end
function BitOperator.LEFT_SHIFT(n, bits)
Utility.check_int(n)
if (n < 0) then
-- negative
n = BitOperator.NOT(math.abs(n)) + 1
end
for i = 1, bits do
n = n * 2
end
return BitOperator.AND(n, 4294967295) -- 0xFFFFFFFF
end
-- Utility
function Utility.str_split(str, size)
local result = {}
for i = 1, #str, size do
table.insert(result, str:sub(i, i + size - 1))
end
return result
end
function Utility.dec2bin(num)
local result = ""
repeat
local halved = num / 2
local int, frac = math.modf(halved)
num = int
result = math.ceil(frac) .. result
until num == 0
return result
end
function Utility.padRight(str, length, char)
while #str % length ~= 0 do
str = str .. char
end
return str
end
function Utility.padLeft(str, length, char)
while #str % length ~= 0 do
str = char .. str
end
return str
end
function Utility.table2number(tbl)
local n = table.getn(tbl)
local rslt = 0
local power = 1
for i = 1, n do
rslt = rslt + tbl[i] * power
power = power * 2
end
return rslt
end
function Utility.to_bits(n)
Utility.check_int(n)
if (n < 0) then
-- negative
return Utility.to_bits(bit.bnot(math.abs(n)) + 1)
end
-- to bits table
local tbl = {}
local cnt = 1
while (n > 0) do
local last = math.mod(n, 2)
if (last == 1) then
tbl[cnt] = 1
else
tbl[cnt] = 0
end
n = (n - last) / 2
cnt = cnt + 1
end
return tbl
end
function Utility.check_int(n)
-- checking not float
if (n - math.floor(n) > 0) then
error("trying to use bitwise operation on non-integer!")
end
end
function Utility.expand(tbl_m, tbl_n)
local big = {}
local small = {}
if (table.getn(tbl_m) > table.getn(tbl_n)) then
big = tbl_m
small = tbl_n
else
big = tbl_n
small = tbl_m
end
-- expand small
for i = table.getn(small) + 1, table.getn(big) do
small[i] = 0
end
end
function Utility.table_splice(tab, idx, n, ...) --> removed
local values = {...}
local init_tab_size = #tab
local removed = {}
if n > 0 then
for i = idx, (idx + n - 1) do
table.insert(removed, tab[i])
tab[i] = nil
end
end
local tail = {}
for i = (idx + n), init_tab_size do
table.insert(tail, tab[i])
tab[i] = nil
end
local i = idx
for _, v in ipairs(values) do
tab[i] = v
i = i + 1
end
i = idx + #values
for _, v in ipairs(tail) do
tab[i] = v
i = i + 1
end
return removed
end
function Utility.table_contains(table, val)
for i=1,#table do
if table[i] == val then
return true
end
end
return false
end
return p
-- </pre>
-- [[Category:Lua]]