Module:BassaridEmployment: Difference between revisions
From MicrasWiki
Jump to navigationJump to search
NewZimiaGov (talk | contribs) No edit summary Tag: Manual revert |
NewZimiaGov (talk | contribs) No edit summary |
||
| Line 1: | Line 1: | ||
local p = {} | 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 function getCurrentDate() | ||
local startDate = os.time({ year = 1999, month = 8, day = 6 }) | local startDate = os.time({ year = 1999, month = 8, day = 6 }) | ||
local secondsInDay = 86400 | local secondsInDay = 86400 | ||
local daysPerYear = 183 | local daysPerYear = 183 | ||
local | local currentDate = os.time() | ||
local totalDaysElapsed = math.floor(( | local totalDaysElapsed = math.floor((currentDate - startDate) / secondsInDay) | ||
local yearFraction = totalDaysElapsed / daysPerYear | local yearFraction = totalDaysElapsed / daysPerYear | ||
local psscYear = math.floor(yearFraction) | local psscYear = math.floor(yearFraction) | ||
local dayOfYear = math.floor((yearFraction - psscYear) * daysPerYear) + 1 | local dayOfYear = math.floor((yearFraction - psscYear) * daysPerYear) + 1 | ||
local month, dayOfMonth | local month, dayOfMonth | ||
if dayOfYear <= 61 then | if dayOfYear <= 61 then | ||
month = 1 | month = 1; dayOfMonth = dayOfYear | ||
elseif dayOfYear <= 122 then | elseif dayOfYear <= 122 then | ||
month = 2 | month = 2; dayOfMonth = dayOfYear - 61 | ||
else | else | ||
month = 3 | month = 3; dayOfMonth = dayOfYear - 122 | ||
end | end | ||
return string.format("%d/%d/%d PSSC", dayOfMonth, month, psscYear) | |||
return string.format("%d/%d/%d [[PSSC]]", dayOfMonth, month, psscYear), dayOfMonth, month, psscYear, dayOfYear | |||
end | end | ||
local MOD = 2147483647 | --------------------------------------------------------- | ||
-- 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 A = 1103515245 | ||
local C = 12345 | local C = 12345 | ||
| Line 97: | Line 117: | ||
end | end | ||
local function | 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 | end | ||
local function | --------------------------------------------------------- | ||
-- 4) Per-company calculation | |||
--------------------------------------------------------- | |||
local function calculateEmployees(company, employedPopulation, maxChangeFrac, dailySeed) | |||
local normalizedShare = company.employmentPercentage / totalEmploymentWeight | |||
local base = math.floor(employedPopulation * normalizedShare) | |||
local | local h = hashString(company.name) | ||
local nextInt = lcg(dailySeed + h) | |||
end | local maxChange = math.floor(base * maxChangeFrac) | ||
if maxChange < 0 then maxChange = 0 end | |||
local changeAmount = 0 | |||
local | if maxChange > 0 then | ||
changeAmount = randIntInclusive(nextInt, -maxChange, maxChange) | |||
end | end | ||
local prevEmployees = base | |||
local currEmployees = base + changeAmount | |||
if currEmployees < 1 then currEmployees = 1 end | |||
end | |||
return prevEmployees, currEmployees, normalizedShare | |||
return | |||
end | end | ||
--------------------------------------------------------- | |||
-- 5) generateTable | |||
--------------------------------------------------------- | |||
function p.generateTable(frame) | function p.generateTable(frame) | ||
frame = frame or {} | frame = frame or {} | ||
local args = (frame.getParent and frame:getParent() and frame:getParent().args) or {} | local args = (frame.getParent and frame:getParent() and frame:getParent().args) or {} | ||
local fargs = frame.args or {} | local fargs = frame.args or {} | ||
local population = tonumber(fargs.population or args.population) or 55499301 | |||
local population = tonumber(fargs.population or args.population) or | |||
local unemploymentRate = tonumber(fargs.unemployment or args.unemployment) or 0.04 | 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 < 0 then unemploymentRate = 0 end | ||
if unemploymentRate > 1 then unemploymentRate = 1 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 localTime = os.date("*t") | ||
local dailySeed = localTime.year * 1000 + localTime.yday | local dailySeed = localTime.year * 1000 + localTime.yday | ||
local dateString = getCurrentDate() | local dateString = getCurrentDate() | ||
local out = {} | local out = {} | ||
out[#out + 1] = | out[#out + 1] = "{| class=\"wikitable\"\n" | ||
out[#out + 1] = | out[#out + 1] = "! Date !! Company !! Employment % !! Previous Employees !! Current Employees !! Change\n" | ||
local totalEmployed = 0 | 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 | local rowStyle | ||
if | if changeType == "Increased" then | ||
rowStyle = ' style="background-color: | rowStyle = ' style="background-color:lightblue;"' | ||
elseif | elseif changeType == "Same" then | ||
rowStyle = ' style="background-color: | rowStyle = ' style="background-color:lightgreen;"' | ||
else | else | ||
rowStyle = ' style="background-color:# | rowStyle = ' style="background-color:#ffcccb;"' | ||
end | end | ||
totalEmployed = totalEmployed + currE | |||
out[#out + 1] = string.format( | out[#out + 1] = string.format( | ||
"|-%s\n| %s || %s || %.2f%% || %d || %d || %s\n", | |||
rowStyle, | rowStyle, | ||
dateString, | dateString, | ||
company.name, | |||
normalizedShare * 100, | |||
prevE, | |||
currE, | |||
changeType | |||
) | ) | ||
end | end | ||
local totalUnemployed = | local totalUnemployed = population - totalEmployed | ||
if totalUnemployed < 0 then totalUnemployed = 0 end | |||
out[#out + 1] = string.format( | out[#out + 1] = string.format("|-\n! colspan=\"4\" | Total Employed || %d || \n", totalEmployed) | ||
out[#out + 1] = string.format( | out[#out + 1] = string.format("|-\n! colspan=\"4\" | Total Unemployed || %d || \n", totalUnemployed) | ||
out[#out + 1] = | out[#out + 1] = "|}" | ||
return table.concat(out) | return table.concat(out) | ||
Revision as of 18:27, 20 March 2026
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