Module:BassaridEmployment
From MicrasWiki
Documentation for this module may be created at Module:BassaridEmployment/doc
local p = {}
---------------------------------------------------------
-- Module:BassaridEmployment (Optimized)
--
-- What this module does
-- • Computes a daily "previous vs current" employment estimate per company
-- • Uses deterministic per-company variation so:
-- - results are stable for a given day
-- - results do NOT depend on loop order / table order
-- • Builds wiki output using buffers (table.concat) for speed
--
-- Usage:
-- {{#invoke:BassaridEmployment|generateTable}}
-- Optional args:
-- |population=55499301
-- |unemployment=0.04
-- |maxChange=0.04
---------------------------------------------------------
---------------------------------------------------------
-- 1) getCurrentDate
-- Returns a date string in PSSC format, plus day/month/year/dayOfYear.
---------------------------------------------------------
local function getCurrentDate()
local startDate = os.time({ year = 1999, month = 8, day = 6 })
local secondsInDay = 86400
local daysPerYear = 183
local currentDate = os.time()
local totalDaysElapsed = math.floor((currentDate - startDate) / secondsInDay)
local yearFraction = totalDaysElapsed / daysPerYear
local psscYear = math.floor(yearFraction)
local dayOfYear = math.floor((yearFraction - psscYear) * daysPerYear) + 1
local month, dayOfMonth
if dayOfYear <= 61 then
month = 1; dayOfMonth = dayOfYear
elseif dayOfYear <= 122 then
month = 2; dayOfMonth = dayOfYear - 61
else
month = 3; dayOfMonth = dayOfYear - 122
end
return string.format("%d/%d/%d [[PSSC]]", dayOfMonth, month, psscYear), dayOfMonth, month, psscYear, dayOfYear
end
---------------------------------------------------------
-- 2) Company table (same data as current module)
---------------------------------------------------------
local companies = {
{name = "Lake Morovia Blockade Fund", employmentPercentage = 0.0300},
{name = "Gladeseed Farmers' Union", employmentPercentage = 0.1205},
{name = "Atterian Cattle Drivers", employmentPercentage = 0.0502},
{name = "Roving Wind Farm Corporation", employmentPercentage = 0.0210},
{name = "Baratar Corporation", employmentPercentage = 0.0401},
{name = "Noctic Fleet", employmentPercentage = 0.0603},
{name = "Keepers of the Bloodbaths of Laprind", employmentPercentage = 0.0105},
{name = "Jogiani Merchants of Manwine", employmentPercentage = 0.0208},
{name = "Keepers of the Grove of Zoe Elm", employmentPercentage = 0.0112},
{name = "Plains of Jogi", employmentPercentage = 0.0807},
{name = "Ale of the Night Central Brewery", employmentPercentage = 0.0201},
{name = "Camel Herders of the Jangsong Province", employmentPercentage = 0.0306},
{name = "Thalassian Temple Granite Export Cooperative", employmentPercentage = 0.0310},
{name = "East Keltian Iron Company", employmentPercentage = 0.0415},
{name = "Mylecian Coal Ports", employmentPercentage = 0.0317},
{name = "Merchants of the Valley of Diamonds", employmentPercentage = 0.0102},
{name = "Maritime Guild of the Cult of Maskmakers", employmentPercentage = 0.0109},
{name = "Hatch Ministry", employmentPercentage = 0.0203},
{name = "Anterran Imports and Services", employmentPercentage = 0.0113},
{name = "East Keltian Timber Company", employmentPercentage = 0.0312},
{name = "Grand Cave Bee Meadery", employmentPercentage = 0.0215},
{name = "Merchant Guild of the Poppy Goddess", employmentPercentage = 0.0111},
{name = "Cinnamon Plains of Rouge", employmentPercentage = 0.0218},
{name = "Seamstresses of Rouge", employmentPercentage = 0.0114},
{name = "Salurian Temple of Sacred Horehound", employmentPercentage = 0.0212},
{name = "Suncliff Fisheries", employmentPercentage = 0.0309},
{name = "Bird Keepers of Saluria", employmentPercentage = 0.0108},
{name = "Velvet Homestead", employmentPercentage = 0.0110},
{name = "Hunters of the Giant Fanged Penguin", employmentPercentage = 0.0116},
{name = "Professional Pillarion League", employmentPercentage = 0.0052},
{name = "La Sái Ebile", employmentPercentage = 0.0104},
{name = "Temple Bank of the Reformed Stripping Path", employmentPercentage = 0.0118},
{name = "Market Hanavusu", employmentPercentage = 0.0157},
{name = "Couriers of the Lizard Queen", employmentPercentage = 0.0117},
{name = "Temple University of Delphica", employmentPercentage = 0.0209},
{name = "Trans-Morovian Railway Company", employmentPercentage = 0.0155},
}
---------------------------------------------------------
-- 2.5) Normalize employment percentages into weights
---------------------------------------------------------
local totalEmploymentWeight = 0
for _, company in ipairs(companies) do
totalEmploymentWeight = totalEmploymentWeight + company.employmentPercentage
end
---------------------------------------------------------
-- 3) Deterministic RNG helpers (fast; no global math.random)
---------------------------------------------------------
local MOD = 2147483647 -- 2^31 - 1
local A = 1103515245
local C = 12345
local function hashString(s)
s = tostring(s or "")
local h = 0
for i = 1, #s do
h = (h * 31 + s:byte(i)) % MOD
end
return h
end
local function lcg(seed)
seed = seed % MOD
return function()
seed = (A * seed + C) % MOD
return seed
end
end
local function randIntInclusive(nextInt, lo, hi)
if hi < lo then lo, hi = hi, lo end
local span = (hi - lo) + 1
local r = (nextInt() + nextInt()) % MOD
return lo + (r % span)
end
---------------------------------------------------------
-- 4) Per-company calculation
---------------------------------------------------------
local function calculateEmployees(company, employedPopulation, maxChangeFrac, dailySeed)
local normalizedShare = company.employmentPercentage / totalEmploymentWeight
local base = math.floor(employedPopulation * normalizedShare)
local h = hashString(company.name)
local nextInt = lcg(dailySeed + h)
local maxChange = math.floor(base * maxChangeFrac)
if maxChange < 0 then maxChange = 0 end
local changeAmount = 0
if maxChange > 0 then
changeAmount = randIntInclusive(nextInt, -maxChange, maxChange)
end
local prevEmployees = base
local currEmployees = base + changeAmount
if currEmployees < 1 then currEmployees = 1 end
return prevEmployees, currEmployees, normalizedShare
end
---------------------------------------------------------
-- 5) generateTable
---------------------------------------------------------
function p.generateTable(frame)
frame = frame or {}
local args = (frame.getParent and frame:getParent() and frame:getParent().args) or {}
local fargs = frame.args or {}
local population = tonumber(fargs.population or args.population) or 55499301
local unemploymentRate = tonumber(fargs.unemployment or args.unemployment) or 0.04
local maxChangeFrac = tonumber(fargs.maxChange or args.maxChange) or 0.04
if unemploymentRate < 0 then unemploymentRate = 0 end
if unemploymentRate > 1 then unemploymentRate = 1 end
if maxChangeFrac < 0 then maxChangeFrac = 0 end
if maxChangeFrac > 1 then maxChangeFrac = 1 end
local employedPopulation = math.floor(population * (1 - unemploymentRate))
local localTime = os.date("*t")
local dailySeed = localTime.year * 1000 + localTime.yday
local dateString = getCurrentDate()
local out = {}
out[#out + 1] = "{| class=\"wikitable\"\n"
out[#out + 1] = "! Date !! Company !! Employment % !! Previous Employees !! Current Employees !! Change\n"
local totalEmployed = 0
for _, company in ipairs(companies) do
local prevE, currE, normalizedShare = calculateEmployees(company, employedPopulation, maxChangeFrac, dailySeed)
local changeType
if currE > prevE then
changeType = "Increased"
elseif currE < prevE then
changeType = "Decreased"
else
changeType = "Same"
end
local rowStyle
if changeType == "Increased" then
rowStyle = ' style="background-color:lightblue;"'
elseif changeType == "Same" then
rowStyle = ' style="background-color:lightgreen;"'
else
rowStyle = ' style="background-color:#ffcccb;"'
end
totalEmployed = totalEmployed + currE
out[#out + 1] = string.format(
"|-%s\n| %s || %s || %.2f%% || %d || %d || %s\n",
rowStyle,
dateString,
company.name,
normalizedShare * 100,
prevE,
currE,
changeType
)
end
local totalUnemployed = population - totalEmployed
if totalUnemployed < 0 then totalUnemployed = 0 end
out[#out + 1] = string.format("|-\n! colspan=\"4\" | Total Employed || %d || \n", totalEmployed)
out[#out + 1] = string.format("|-\n! colspan=\"4\" | Total Unemployed || %d || \n", totalUnemployed)
out[#out + 1] = "|}"
return table.concat(out)
end
return p