mirror of
https://github.com/ComfyFactory/ComfyFactorio.git
synced 2025-01-08 00:39:30 +02:00
671 lines
19 KiB
Lua
671 lines
19 KiB
Lua
-- This file is part of thesixthroc's Pirate Ship softmod, licensed under GPLv3 and stored at https://github.com/ComfyFactory/ComfyFactorio and https://github.com/danielmartin0/ComfyFactorio-Pirates.
|
|
|
|
|
|
local Math = require 'maps.pirates.math'
|
|
-- local _inspect = require 'utils.inspect'.inspect
|
|
local simplex_noise = require 'utils.simplex_noise'.d2 --rms ~ 0.1925
|
|
-- local perlin_noise = require 'utils.perlin_noise'
|
|
-- local Memory = require 'maps.pirates.memory'
|
|
-- local CoreData = require 'maps.pirates.coredata'
|
|
local NoisePregen = require 'maps.pirates.noise_pregen.noise_pregen'
|
|
|
|
local Public = {}
|
|
|
|
-- Lua 5.2 compatibility
|
|
-- local unpack = unpack or table.unpack
|
|
|
|
|
|
function Public.rgb_from_hsv(h, s, v)
|
|
-- 0 ≤ H < 360, 0 ≤ S ≤ 1 and 0 ≤ V ≤ 1
|
|
local r, g, b
|
|
local c = v * s
|
|
local x = c * (1 - Math.abs(((h / 60) % 2) - 1))
|
|
local m = v - c
|
|
if h < 60 then
|
|
r = c + m
|
|
g = x + m
|
|
b = m
|
|
elseif h < 120 then
|
|
r = x + m
|
|
g = c + m
|
|
b = m
|
|
elseif h < 180 then
|
|
r = m
|
|
g = c + m
|
|
b = x + m
|
|
elseif h < 240 then
|
|
r = m
|
|
g = x + m
|
|
b = c + m
|
|
elseif h < 300 then
|
|
r = x + m
|
|
g = m
|
|
b = c + m
|
|
else
|
|
r = c + m
|
|
g = m
|
|
b = x + m
|
|
end
|
|
return { r = 255 * r, g = 255 * g, b = 255 * b }
|
|
end
|
|
|
|
function Public.stable_sort(list, comp) --sorts but preserves ordering of equals
|
|
comp = comp or function (a, b) return a < b end
|
|
|
|
local num = 0
|
|
for k, _ in ipairs(list) do
|
|
num = num + 1
|
|
end
|
|
|
|
if num <= 1 then
|
|
return
|
|
end
|
|
|
|
local sorted = false
|
|
local n = num
|
|
while not sorted do
|
|
sorted = true
|
|
for i = 1, n - 1 do
|
|
if comp(list[i + 1], list[i]) then
|
|
local tmp = list[i]
|
|
list[i] = list[i + 1]
|
|
list[i + 1] = tmp
|
|
|
|
sorted = false
|
|
end
|
|
end
|
|
n = n - 1
|
|
end
|
|
end
|
|
|
|
function Public.psum(plist)
|
|
local totalx, totaly = 0, 0
|
|
for i = 1, #plist do
|
|
if plist[i][1] then --multiplier
|
|
if plist[i][2] and plist[i][2].x and plist[i][2].y then
|
|
totalx = totalx + plist[i][1] * plist[i][2].x
|
|
totaly = totaly + plist[i][1] * plist[i][2].y
|
|
end
|
|
elseif plist[i].x and plist[i].y then
|
|
totalx = totalx + plist[i].x
|
|
totaly = totaly + plist[i].y
|
|
end
|
|
end
|
|
return { x = totalx, y = totaly }
|
|
end
|
|
|
|
function Public.interpolate(vector1, vector2, param)
|
|
return { x = vector1.x * (1 - param) + vector2.x * param, y = vector1.y * (1 - param) + vector2.y * param }
|
|
end
|
|
|
|
function Public.contains(table, element)
|
|
if not table then return false end
|
|
for _, value in pairs(table) do
|
|
if value == element then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
function Public.snap_coordinates_for_rails(p)
|
|
return { x = p.x + (p.x % 2) - 1, y = p.y + (p.y % 2) }
|
|
end
|
|
|
|
function Public.spritepath_to_richtext(spritepath)
|
|
return '[' .. spritepath:gsub("/", "=") .. ']'
|
|
end
|
|
|
|
function Public.nonrepeating_join_dict(t1, t2)
|
|
t1 = t1 or {}
|
|
t2 = t2 or {}
|
|
local t3 = {}
|
|
for k, i1 in pairs(t1) do
|
|
t3[k] = i1
|
|
end
|
|
for k, i2 in pairs(t2) do
|
|
t3[k] = i2
|
|
end
|
|
return t3
|
|
end
|
|
|
|
function Public.nonrepeating_join(t1, t2)
|
|
t1 = t1 or {}
|
|
t2 = t2 or {}
|
|
local t3 = {}
|
|
for _, i1 in pairs(t1) do
|
|
t3[#t3 + 1] = i1
|
|
end
|
|
for _, i2 in pairs(t2) do
|
|
if not Public.contains(t3, i2) then
|
|
t3[#t3 + 1] = i2
|
|
end
|
|
end
|
|
return t3
|
|
end
|
|
|
|
function Public.exclude(t, t_exclude)
|
|
t_exclude = t_exclude or {}
|
|
local t2 = {}
|
|
for _, i in pairs(t) do
|
|
if not Public.contains(t_exclude, i) then
|
|
t2[#t2 + 1] = i
|
|
end
|
|
end
|
|
return t2
|
|
end
|
|
|
|
function Public.exclude_position_arrays(a, b_exclude)
|
|
b_exclude = b_exclude or {}
|
|
local a2 = {}
|
|
for x, xtab in pairs(a) do
|
|
for y, _ in pairs(xtab) do
|
|
if not (b_exclude[x] and b_exclude[x][y]) then
|
|
if not a2[x] then a2[x] = {} end
|
|
a2[x][y] = true
|
|
end
|
|
end
|
|
end
|
|
return a2
|
|
end
|
|
|
|
function Public.unordered_table_with_values_removed(tbl, val)
|
|
local to_keep = {}
|
|
for k, v in pairs(tbl) do
|
|
if v ~= val then to_keep[k] = v end
|
|
end
|
|
return to_keep
|
|
end
|
|
|
|
function Public.ordered_table_with_values_removed(tbl, val)
|
|
local to_keep = {}
|
|
local j = 1
|
|
for i = 1, #tbl do
|
|
if tbl[i] ~= val then
|
|
to_keep[j] = tbl[i]
|
|
j = j + 1
|
|
end
|
|
end
|
|
return to_keep
|
|
end
|
|
|
|
function Public.ordered_table_with_single_value_removed(tbl, val)
|
|
local to_keep = {}
|
|
local j = 1
|
|
local taken_one = false
|
|
for i = 1, #tbl do
|
|
if (tbl[i] ~= val) or taken_one then
|
|
to_keep[j] = tbl[i]
|
|
j = j + 1
|
|
else
|
|
taken_one = true
|
|
end
|
|
end
|
|
return to_keep
|
|
end
|
|
|
|
function Public.ordered_table_with_index_removed(tbl, index)
|
|
local to_keep = {}
|
|
local j = 1
|
|
for i = 1, #tbl do
|
|
if i ~= index then
|
|
to_keep[j] = tbl[i]
|
|
j = j + 1
|
|
end
|
|
end
|
|
return to_keep
|
|
end
|
|
|
|
-- Implementation of table.fast_remove
|
|
function Public.fast_remove(tbl, index)
|
|
local count = #tbl
|
|
if index > count then
|
|
return
|
|
elseif index < count then
|
|
tbl[index] = tbl[count]
|
|
end
|
|
|
|
tbl[count] = nil
|
|
end
|
|
|
|
function Public.length(tbl)
|
|
local count = 0
|
|
for k, _ in pairs(tbl) do
|
|
count = count + 1
|
|
end
|
|
return count
|
|
end
|
|
|
|
function Public.standard_string_form_of_time_in_seconds(time)
|
|
local time2, str1
|
|
if time >= 0 then
|
|
time2 = time
|
|
str1 = ''
|
|
else
|
|
time2 = -time
|
|
str1 = '-'
|
|
end
|
|
local str2
|
|
local hours = Math.floor(Math.ceil(time2) / 3600)
|
|
local minutes = Math.floor(Math.ceil(time2) / 60)
|
|
local seconds = Math.ceil(time2)
|
|
if hours >= 1 then
|
|
str2 = string.format('%.0fh %.0fm %.0fs', hours, minutes % 60, seconds % 60)
|
|
elseif minutes >= 1 then
|
|
str2 = string.format('%.0fm %.0fs', minutes, seconds % 60)
|
|
else
|
|
str2 = string.format('%.0fs', seconds)
|
|
end
|
|
return str1 .. str2
|
|
end
|
|
|
|
function Public.time_longform(seconds)
|
|
local seconds2, str1
|
|
if seconds >= 0 then
|
|
seconds2 = seconds
|
|
str1 = ''
|
|
else
|
|
seconds2 = -seconds
|
|
str1 = '-'
|
|
end
|
|
local str2
|
|
if seconds2 < 60 - 1 then
|
|
str2 = string.format('%.0f seconds', Math.ceil(seconds2))
|
|
elseif seconds2 < 60 * 60 - 1 then
|
|
str2 = string.format('%.0f mins, %.0f seconds', Math.floor(Math.ceil(seconds2) / 60), Math.ceil(seconds2) % 60)
|
|
elseif seconds2 < 60 * 60 * 24 - 1 then
|
|
str2 = string.format('%.0f hours, %.0f mins, %.0f seconds', Math.floor(Math.ceil(seconds2) / (60 * 60)), Math.floor(Math.ceil(seconds2) / 60) % 60, Math.ceil(seconds2) % 60)
|
|
else
|
|
str2 = string.format('%.0f days, %.0f hours, %.0f mins, %.0f seconds', Math.floor(Math.ceil(seconds2) / (24 * 60 * 60)), Math.floor(Math.ceil(seconds2) / (60 * 60)) % 24, Math.floor(Math.ceil(seconds2) / 60) % 60, Math.ceil(seconds2) % 60)
|
|
end
|
|
return str1 .. str2
|
|
end
|
|
|
|
function Public.time_mediumform(seconds)
|
|
local seconds2, str1
|
|
if seconds >= 0 then
|
|
seconds2 = seconds
|
|
str1 = ''
|
|
else
|
|
seconds2 = -seconds
|
|
str1 = '-'
|
|
end
|
|
local str2
|
|
local seconds3 = Math.floor(seconds2)
|
|
if seconds3 < 60 - 1 then
|
|
str2 = string.format('%.0fs', seconds3)
|
|
elseif seconds3 < 60 * 60 - 1 then
|
|
str2 = string.format('%.0fm%.0fs', Math.floor(seconds3 / 60), seconds3 % 60)
|
|
elseif seconds3 < 60 * 60 * 24 - 1 then
|
|
str2 = string.format('%.0fh%.0fm%.0fs', Math.floor(seconds3 / (60 * 60)), Math.floor(seconds3 / 60) % 60, seconds3 % 60)
|
|
else
|
|
str2 = string.format('%.0fd%.0fh%.0fm%.0fs', Math.floor(seconds3 / (24 * 60 * 60)), Math.floor(seconds3 / (60 * 60)) % 24, Math.floor(seconds3 / 60) % 60, seconds3 % 60)
|
|
end
|
|
return str1 .. str2
|
|
end
|
|
|
|
function Public.deepcopy(obj) --doesn't copy metatables
|
|
if type(obj) ~= 'table' then return obj end
|
|
local res = {}
|
|
for k, v in pairs(obj) do res[Public.deepcopy(k)] = Public.deepcopy(v) end
|
|
return res
|
|
end
|
|
|
|
function Public.bignumber_abbrevform(number) --e.g. 516, 1.2k, 21.4k, 137k
|
|
local str1, str2, number2
|
|
if number >= 0 then
|
|
number2 = number
|
|
str1 = ''
|
|
else
|
|
number2 = -number
|
|
str1 = '-'
|
|
end
|
|
|
|
if number2 == 0 then
|
|
str2 = '0'
|
|
elseif number2 < 1000 then
|
|
str2 = string.format('%.0d', Math.floor(number2))
|
|
elseif number2 < 100000 then
|
|
str2 = string.format('%.1fk', Math.floor(number2 / 100) / 10)
|
|
else
|
|
str2 = string.format('%.0dk', Math.floor(number2 / 1000))
|
|
end
|
|
|
|
return str1 .. str2
|
|
end
|
|
|
|
function Public.bignumber_abbrevform2(number) --e.g. 516, 1.2k, 21k, 137k
|
|
local str1, str2, number2
|
|
if number >= 0 then
|
|
number2 = number
|
|
str1 = ''
|
|
else
|
|
number2 = -number
|
|
str1 = '-'
|
|
end
|
|
|
|
if number2 == 0 then
|
|
str2 = '0'
|
|
elseif number2 < 1000 then
|
|
str2 = string.format('%.0d', Math.floor(number2))
|
|
elseif number2 < 10000 then
|
|
str2 = string.format('%.1fk', Math.floor(number2 / 100) / 10)
|
|
else
|
|
str2 = string.format('%.0dk', Math.floor(number2 / 1000))
|
|
end
|
|
|
|
return str1 .. str2
|
|
end
|
|
|
|
function Public.negative_rate_abbrevform(number)
|
|
local str1, str2, number2
|
|
if number > 0 then
|
|
number2 = number
|
|
str1 = ''
|
|
else
|
|
number2 = -number
|
|
str1 = '-'
|
|
end
|
|
if number2 == 0 then
|
|
str2 = '0'
|
|
elseif number2 < 10 then
|
|
str2 = string.format('%.1f', Math.ceil(number2 * 10) / 10)
|
|
else
|
|
str2 = string.format('%.0d', Math.ceil(number2))
|
|
end
|
|
|
|
return str1 .. str2 .. '/s'
|
|
end
|
|
|
|
function Public.noise_field_simplex_2d(noise_data, seed, normalised)
|
|
normalised = normalised or false
|
|
|
|
local f = function (position)
|
|
local noise, _seed, weight_sum = 0, seed, 0
|
|
for i = 1, #noise_data do
|
|
local n = noise_data[i]
|
|
local toadd = n.amplitude
|
|
if n.wavelength > 0 then --=0 codes for infinite
|
|
toadd = toadd * simplex_noise(position.x / n.wavelength, position.y / n.wavelength, _seed)
|
|
_seed = _seed + 12345 --some deficiencies
|
|
end
|
|
|
|
if normalised then weight_sum = weight_sum + n.amplitude end
|
|
noise = noise + toadd
|
|
end
|
|
if normalised then noise = noise / weight_sum end
|
|
return noise
|
|
end
|
|
return f
|
|
end
|
|
|
|
function Public.hardcoded_noise_field_decompress(fieldtype, noise_data, seed, normalised)
|
|
normalised = normalised or false
|
|
|
|
-- local hardcoded_upperscale = NoisePregen[fieldtype].upperscale
|
|
-- local hardcoded_boxsize = NoisePregen[fieldtype].boxsize
|
|
local hardcoded_wordlength = NoisePregen[fieldtype].wordlength
|
|
local factor = NoisePregen[fieldtype].factor
|
|
|
|
local f = function (position)
|
|
local noise, weight_sum, _seed = 0, 0, seed
|
|
for i = 1, #noise_data do
|
|
local n = noise_data[i]
|
|
local toadd = n.amplitude
|
|
local seedfactor = n.seedfactor or 1
|
|
|
|
if n.upperscale > 0 then --=0 codes for infinite
|
|
local scale = n.upperscale / 100
|
|
|
|
local seed2 = seed * seedfactor
|
|
|
|
local x2 = position.x / scale
|
|
local y2 = position.y / scale
|
|
|
|
local x2remainder = x2 % 1
|
|
local y2remainder = y2 % 1
|
|
|
|
local x2floor = x2 - x2remainder
|
|
local y2floor = y2 - y2remainder
|
|
|
|
local topleftnoiseindex = seed2 % (1000 * 1000)
|
|
|
|
local relativeindex00 = x2floor + y2floor * 1000
|
|
|
|
local totalindex00 = (topleftnoiseindex + relativeindex00) % (1000 * 1000)
|
|
local totalindex10 = (1 + topleftnoiseindex + relativeindex00) % (1000 * 1000)
|
|
local totalindex01 = (1000 + topleftnoiseindex + relativeindex00) % (1000 * 1000)
|
|
|
|
local strindex00 = 1 + totalindex00 * hardcoded_wordlength
|
|
local strindex10 = 1 + totalindex10 * hardcoded_wordlength
|
|
local strindex01 = 1 + totalindex01 * hardcoded_wordlength
|
|
|
|
local str00 = NoisePregen[fieldtype].Data:sub(strindex00, strindex00 + (hardcoded_wordlength - 1))
|
|
local str10 = NoisePregen[fieldtype].Data:sub(strindex10, strindex10 + (hardcoded_wordlength - 1))
|
|
local str01 = NoisePregen[fieldtype].Data:sub(strindex01, strindex01 + (hardcoded_wordlength - 1))
|
|
local noise00, noise10, noise01 = 0, 0, 0
|
|
for j = 0, hardcoded_wordlength - 1 do
|
|
noise00 = noise00 + NoisePregen.dec[str00:sub(hardcoded_wordlength - j, hardcoded_wordlength - j)] * (NoisePregen.encoding_length ^ j)
|
|
noise10 = noise10 + NoisePregen.dec[str10:sub(hardcoded_wordlength - j, hardcoded_wordlength - j)] * (NoisePregen.encoding_length ^ j)
|
|
noise01 = noise01 + NoisePregen.dec[str01:sub(hardcoded_wordlength - j, hardcoded_wordlength - j)] * (NoisePregen.encoding_length ^ j)
|
|
end
|
|
|
|
if noise00 % 2 == 1 then noise00 = -noise00 end
|
|
noise00 = noise00 / (NoisePregen.encoding_length ^ (hardcoded_wordlength - 1))
|
|
|
|
if noise10 % 2 == 1 then noise10 = -noise10 end
|
|
noise10 = noise10 / (NoisePregen.encoding_length ^ (hardcoded_wordlength - 1))
|
|
|
|
if noise01 % 2 == 1 then noise01 = -noise01 end
|
|
noise01 = noise01 / (NoisePregen.encoding_length ^ (hardcoded_wordlength - 1))
|
|
|
|
-- local hardnoise00 = tonumber(strsub00:sub(2,6))/10000
|
|
-- if strsub00:sub(1,1) == '-' then hardnoise00 = -hardnoise00 end
|
|
-- local hardnoise10 = tonumber(strsub10:sub(2,6))/10000
|
|
-- if strsub10:sub(1,1) == '-' then hardnoise10 = -hardnoise10 end
|
|
-- local hardnoise01 = tonumber(strsub01:sub(2,6))/10000
|
|
-- if strsub01:sub(1,1) == '-' then hardnoise01 = -hardnoise01 end
|
|
|
|
-- log(_inspect{topleftnoiseindex, topleftnoiseindex2, relativeindex00, relativeindex10, relativeindex01})
|
|
-- log(_inspect{strindex00, strindex10, strindex01, hardnoise1, hardnoise2, hardnoise3})
|
|
|
|
local interpolatedhardnoise = noise00 + x2remainder * (noise10 - noise00) + y2remainder * (noise01 - noise00)
|
|
|
|
toadd = toadd * factor * tonumber(interpolatedhardnoise)
|
|
_seed = _seed + 12345
|
|
end
|
|
|
|
if normalised then weight_sum = weight_sum + n.amplitude end
|
|
noise = noise + toadd
|
|
end
|
|
if normalised then noise = noise / weight_sum end
|
|
return noise
|
|
end
|
|
return f
|
|
end
|
|
|
|
function Public.hardcoded_noise_field(fieldtype, noise_data, seed, normalised)
|
|
normalised = normalised or false
|
|
|
|
-- local hardcoded_upperscale = NoisePregen[fieldtype].upperscale --100
|
|
-- local hardcoded_boxsize = NoisePregen[fieldtype].boxsize --1000
|
|
local hardcoded_wordlength = NoisePregen[fieldtype].wordlength
|
|
local factor = NoisePregen[fieldtype].factor
|
|
|
|
local f = function (position)
|
|
local noise, weight_sum, _seed = 0, 0, seed
|
|
for i = 1, #noise_data do
|
|
local n = noise_data[i]
|
|
local toadd = n.amplitude
|
|
local seedfactor = n.seedfactor or 1
|
|
|
|
if n.upperscale > 0 then --=0 codes for infinite
|
|
local scale = n.upperscale / 100
|
|
|
|
local seed2 = seed * seedfactor
|
|
|
|
local x2 = position.x / scale
|
|
local y2 = position.y / scale
|
|
|
|
local x2remainder = x2 % 1
|
|
local y2remainder = y2 % 1
|
|
|
|
local x2floor = x2 - x2remainder
|
|
local y2floor = y2 - y2remainder
|
|
|
|
local seedindex = seed2 % (1000 * 1000)
|
|
|
|
local relativeindex00 = x2floor + y2floor * 1000
|
|
|
|
local noiseindex1 = seedindex + relativeindex00
|
|
|
|
local totalindex00 = noiseindex1 % (1000 * 1000)
|
|
local totalindex10 = (1 + noiseindex1) % (1000 * 1000)
|
|
local totalindex01 = (1000 + noiseindex1) % (1000 * 1000)
|
|
|
|
local strindex00 = 1 + totalindex00 * hardcoded_wordlength
|
|
local strindex10 = 1 + totalindex10 * hardcoded_wordlength
|
|
local strindex01 = 1 + totalindex01 * hardcoded_wordlength
|
|
|
|
local str00 = NoisePregen[fieldtype].Data:sub(strindex00, strindex00 + (hardcoded_wordlength - 1))
|
|
local str10 = NoisePregen[fieldtype].Data:sub(strindex10, strindex10 + (hardcoded_wordlength - 1))
|
|
local str01 = NoisePregen[fieldtype].Data:sub(strindex01, strindex01 + (hardcoded_wordlength - 1))
|
|
|
|
local noise00 = tonumber(str00:sub(2, 6)) / 10000
|
|
if str00:sub(1, 1) == '-' then noise00 = -noise00 end
|
|
local noise10 = tonumber(str10:sub(2, 6)) / 10000
|
|
if str10:sub(1, 1) == '-' then noise10 = -noise10 end
|
|
local noise01 = tonumber(str01:sub(2, 6)) / 10000
|
|
if str01:sub(1, 1) == '-' then noise01 = -noise01 end
|
|
|
|
-- log(_inspect{topleftnoiseindex, topleftnoiseindex2, relativeindex00, relativeindex10, relativeindex01})
|
|
-- log(_inspect{strindex00, strindex10, strindex01, hardnoise1, hardnoise2, hardnoise3})
|
|
|
|
local interpolatedhardnoise = noise00 + x2remainder * (noise10 - noise00) + y2remainder * (noise01 - noise00)
|
|
|
|
toadd = toadd * factor * tonumber(interpolatedhardnoise)
|
|
_seed = _seed + 12345 --some deficiencies
|
|
end
|
|
|
|
if normalised then weight_sum = weight_sum + n.amplitude end
|
|
noise = noise + toadd
|
|
end
|
|
if normalised then noise = noise / weight_sum end
|
|
return noise
|
|
end
|
|
return f
|
|
end
|
|
|
|
function Public.noise_generator(noiseparams, seed)
|
|
--memoizes locally
|
|
noiseparams = noiseparams or {}
|
|
seed = seed or 0
|
|
|
|
local ret = {}
|
|
for k, v in pairs(noiseparams) do
|
|
local fn
|
|
if v.type == 'simplex_2d' then
|
|
fn = Public.noise_field_simplex_2d(v.params, seed, v.normalised)
|
|
elseif v.type == 'perlin_1d_circle' then
|
|
fn = Public.noise_field_perlin_1d_circle(v.params, seed, v.normalised)
|
|
else
|
|
fn = Public.hardcoded_noise_field(v.type, v.params, seed, v.normalised)
|
|
end
|
|
ret[k] = Public.memoize(fn)
|
|
end
|
|
|
|
function ret:addNoise(key, new_noise_function)
|
|
if self[key] then
|
|
return
|
|
else
|
|
self[key] = Public.memoize(new_noise_function)
|
|
end
|
|
end
|
|
|
|
ret.seed = seed
|
|
-- ret.noiseparams = noiseparams
|
|
return ret
|
|
end
|
|
|
|
local function cache_get(cache, params)
|
|
local node = cache
|
|
for i = 1, #params do
|
|
node = node.children and node.children[params[i]]
|
|
if not node then return nil end
|
|
end
|
|
return node.results
|
|
end
|
|
|
|
local function cache_put(cache, params, results)
|
|
local node = cache
|
|
local param
|
|
for i = 1, #params do
|
|
param = params[i]
|
|
node.children = node.children or {}
|
|
node.children[param] = node.children[param] or {}
|
|
node = node.children[param]
|
|
end
|
|
node.results = results
|
|
end
|
|
|
|
|
|
|
|
-- The following functions were adapted from https://github.com/kikito/memoize.lua/blob/master/memoize.lua, under the MIT License:
|
|
-- [[
|
|
-- MIT LICENSE
|
|
-- Copyright (c) 2018 Enrique García Cota
|
|
-- Permission is hereby granted, free of charge, to any person obtaining a
|
|
-- copy of this software and associated documentation files (the
|
|
-- "Software"), to deal in the Software without restriction, including
|
|
-- without limitation the rights to use, copy, modify, merge, publish,
|
|
-- distribute, sublicense, and/or sell copies of the Software, and to
|
|
-- permit persons to whom the Software is furnished to do so, subject to
|
|
-- the following conditions:
|
|
-- The above copyright notice and this permission notice shall be included
|
|
-- in all copies or substantial portions of the Software.
|
|
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
-- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
-- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
-- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
-- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
-- ]]
|
|
|
|
local function is_callable(f)
|
|
local tf = type(f)
|
|
if tf == 'function' then return true end
|
|
if tf == 'table' then
|
|
local mt = getmetatable(f)
|
|
return type(mt) == 'table' and is_callable(mt.__call)
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- == memoization
|
|
-- memoize takes in a function and outputs an auto-memoizing version of the same
|
|
-- e.g. local memoized_f = memoize(f, <cache>), explicit cache is optional
|
|
function Public.memoize(f, cache)
|
|
cache = cache or {}
|
|
|
|
if not is_callable(f) then
|
|
log(string.format(
|
|
"Only functions and callable tables are memoizable. Received %s (a %s)",
|
|
tostring(f), type(f)))
|
|
end
|
|
|
|
return function (...)
|
|
local params = { ... }
|
|
|
|
local results = cache_get(cache, params)
|
|
if not results then
|
|
results = { f(...) }
|
|
cache_put(cache, params, results)
|
|
end
|
|
|
|
return table.unpack(results)
|
|
end
|
|
end
|
|
|
|
return Public
|