Module:StraitsDaily
From MicrasWiki
Documentation for this module may be created at Module:StraitsDaily/doc
-------------------------------------------------------------------------------
-- Module: StraitsDaily
-- Purpose: Daily, in-world report of Strait of Haifa enforcement
local p = {}
-------------------------------------------------------------------------------
-- (A) PSSC DATE
-------------------------------------------------------------------------------
local function getPSSCDate()
local startDate = os.time({year = 1999, month = 8, day = 6})
local secondsInDay = 86400
local daysPerYear = 183
local now = os.time()
local totalDaysElapsed = math.floor((now - 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
local dateString = string.format("%d/%d/%d PSSC", dayOfMonth, month, psscYear)
return { text = dateString, year = psscYear, dayOfYear = dayOfYear }
end
-------------------------------------------------------------------------------
-- (B) STATIC DATA
-------------------------------------------------------------------------------
local sectors = {
"Strait South Inbound","Strait South Outbound","Strait North Inbound","Strait North Outbound",
"Archipelago Zone","Sacred Buffer Approaches","Environmental Buffer","Northern Approach TSS","Southern Approach TSS"
}
-- Registries / flags (explicit IF/Matamoros allowed; bias is subtle and framed as process):
local flags = {
{name="Bassaridian-flag", weight=6, risky=false},
{name="Moorland", weight=2, risky=false},
{name="Imperial Federation", weight=5, risky=true}, -- slight audit bias later
{name="Matamoros (IF)", weight=2, risky=true}, -- slight audit bias later
{name="Neutral Merchant", weight=4, risky=false},
{name="Distant Partner (Euranidom)", weight=3, risky=true},
{name="Distant Partner (Corumia)", weight=3, risky=true},
{name="Distant Partner (Southern Keltiania)", weight=3, risky=true},
{name="Consortium Registry", weight=2, risky=true},
{name="Other Coastal State", weight=7, risky=false},
}
local vessels = {
"Handymax bulker","Product tanker","General cargo ship","Container feeder",
"Ro-Ro ferry","Research craft","Declared submarine","Undeclared submarine"
}
-- High-risk cargo categories (dual-use emphasized). Weight ~appearance frequency; risk amplifies enforcement odds.
local cargoTypes = {
{name="Medical reagents (dual-use precursors)", weight=3, risk=0.14, dual=true},
{name="Industrial oxidizers / nitrates (dual-use)", weight=2, risk=0.16, dual=true},
{name="Precision machine tools & CNC spares (dual-use)", weight=3, risk=0.15, dual=true},
{name="Encrypted electronics & comms modules (dual-use)",weight=2, risk=0.17, dual=true},
{name="Satellite components / sensors (dual-use)", weight=1, risk=0.18, dual=true},
{name="Aviation fuels & additives", weight=3, risk=0.12, dual=false},
{name="Refined petroleum products", weight=4, risk=0.08, dual=false},
{name="Relief supplies (mixed pallets)", weight=5, risk=0.06, dual=false},
{name="Agricultural commodities", weight=5, risk=0.04, dual=false},
{name="Finished consumer goods", weight=6, risk=0.03, dual=false}
}
-- Shipment tags; "emergency" / "expedited" are purposely given small extra risk to justify audits.
local cargoTags = {
{name="standard", weight=6, risk=0.00},
{name="expedited", weight=3, risk=0.05},
{name="emergency", weight=2, risk=0.07}
}
-- Indirect logistics partners (numerically coded). Some profiles have higher latent risk.
-- (Internally: a few profiles correlate with carriers servicing IF/Matamoros-linked ports.)
local partnerProfiles = {
{code="P-07", weight=4, risk=0.00},
{code="P-12", weight=3, risk=0.03},
{code="P-17", weight=2, risk=0.09}, -- elevated scrutiny
{code="P-22", weight=2, risk=0.11}, -- elevated scrutiny
{code="P-31", weight=3, risk=0.05},
{code="P-38", weight=2, risk=0.10}, -- elevated scrutiny
{code="P-44", weight=4, risk=0.02},
{code="P-53", weight=3, risk=0.04}
}
local clauses = {
{key="Pilotage & Traffic Scheme", cite="SC §A2–A4"},
{key="Tonnage & Warship Limits", cite="SC §B1–B4"},
{key="Submarine Surface/Declare", cite="SC §C2–C3"},
{key="Environmental Buffer", cite="SC §D1–D3"},
{key="Sacred Site Stand-off", cite="SC §E1–E2"},
{key="Emergency/Leviathan", cite="SC §L1–L4"},
{key="Right-of-Visit (Suspicion)",cite="SC §R1–R3"}
}
-- Actions with emphasis on manifest/partner scrutiny & procedural audits
local actions = {
{name="Documents & pilotage verification", weight=5, basis="Pilotage & Traffic Scheme"},
{name="Tonnage/armament declaration check", weight=4, basis="Tonnage & Warship Limits"},
{name="Cargo manifest reconciliation audit", weight=4, basis="Right-of-Visit (Suspicion)"},
{name="Indirect-partner disclosure & vetting", weight=3, basis="Right-of-Visit (Suspicion)"},
{name="Environmental reroute order", weight=3, basis="Environmental Buffer"},
{name="Submarine declare signal issued", weight=2, basis="Submarine Surface/Declare"},
{name="Procedural calibration spot-check (randomized)", weight=3, basis="Pilotage & Traffic Scheme"},
{name="SAR escort under Leviathan Protocol", weight=2, basis="Emergency/Leviathan"}
}
-- Outcomes include neutral "random audit" holds to implement soft delays
local outcomes = {
"Compliant; cleared",
"Minor non-compliance; corrected; cleared",
"Rerouted; passage maintained",
"Inspection completed; cargo intact",
"Administrative notice issued; monitoring",
"Random audit hold; clearance pending procedural update",
"Documentation harmonization hold (routine)",
"Escorted to anchorage for formal review",
"Denied entry pending documents",
"SAR/aid rendered; voyage resumed",
"No reports"
}
-------------------------------------------------------------------------------
-- (C) UTILITIES
-------------------------------------------------------------------------------
local function pickWeighted(list)
local total=0
for _,item in ipairs(list) do total=total+(item.weight or 1) end
local roll,cumul=math.random(total),0
for _,item in ipairs(list) do
cumul=cumul+(item.weight or 1)
if roll<=cumul then return item end
end
return list[#list]
end
local function pick(list) return list[math.random(#list)] end
local clauseIndex = (function()
local t={} for _,c in ipairs(clauses) do t[c.key]=c.cite end; return t
end)()
-------------------------------------------------------------------------------
-- (D) ENFORCEMENT PROBABILITY & FLAG SMOOTHING
-------------------------------------------------------------------------------
local function baseProb(flag, sector, cargo, tag, partner)
local p = 0.28
if flag.risky then p = p + 0.06 end
if sector=="Sacred Buffer Approaches" or sector=="Environmental Buffer" then p = p + 0.08 end
-- High-risk cargo amplification (dual-use + category risk + tag risk)
p = p + (cargo and (cargo.risk or 0) or 0)
if cargo and cargo.dual then p = p + 0.03 end
p = p + (tag and (tag.risk or 0) or 0)
-- Indirect partner latent risk
p = p + (partner and (partner.risk or 0) or 0)
-- Ceiling
if p > 0.72 then p = 0.72 end
return p
end
-- To avoid over-mentioning, impose per-flag cap and discourage repeats.
local function pickFlagWithCaps(flagCounts, maxPerFlag, lastFlagName)
local tries = 0
while tries < 20 do
local f = pickWeighted(flags)
if (flagCounts[f.name] or 0) < maxPerFlag then
if lastFlagName and f.name == lastFlagName and math.random() < 0.5 then
tries = tries + 1
else
return f
end
else
tries = tries + 1
end
end
for _,f in ipairs(flags) do
if (flagCounts[f.name] or 0) < maxPerFlag then return f end
end
return flags[#flags]
end
-------------------------------------------------------------------------------
-- (E) EVENT GENERATION
-------------------------------------------------------------------------------
local function makeEvent(dateText, state)
local sector = pick(sectors)
local flag = pickFlagWithCaps(state.flagCounts, state.maxPerFlag, state.lastFlag)
local vessel = pick(vessels)
local cargo = pickWeighted(cargoTypes)
local tag = pickWeighted(cargoTags)
local partner = pickWeighted(partnerProfiles)
state.flagCounts[flag.name] = (state.flagCounts[flag.name] or 0) + 1
state.lastFlag = flag.name
local pEnf = baseProb(flag, sector, cargo, tag, partner)
-- Most transits are routine; if not enforced, record compliant passage
if math.random() > pEnf then
return {
date=dateText, sector=sector,
vessel=vessel.." ("..flag.name..")",
action="Routine transit", legal="N/A",
outcome="No reports",
notes=string.format("Cargo: %s [%s]; Partner: %s — Compliant passage",
cargo.name, tag.name, partner.code)
}
end
-- Choose action & legal
local act = pickWeighted(actions)
local cite = clauseIndex[act.basis] or "SC §—"
-- Outcome selection with subtle bias: IF/Matamoros + high-risk cargo/tags/partner → procedural holds more likely
local out = pick(outcomes)
local riskPush = 0
if (flag.name=="Imperial Federation" or flag.name=="Matamoros (IF)") then
riskPush = riskPush + 1
end
if (cargo.dual or (cargo.risk >= 0.12)) then riskPush = riskPush + 1 end
if (tag.name ~= "standard") then riskPush = riskPush + 1 end
if (partner.risk >= 0.09) then riskPush = riskPush + 1 end
if riskPush >= 2 and math.random() < 0.65 then
-- steer to gentle, non-escalatory delay outcomes
local delayOutcomes = {
"Random audit hold; clearance pending procedural update",
"Documentation harmonization hold (routine)",
"Administrative notice issued; monitoring"
}
out = delayOutcomes[math.random(#delayOutcomes)]
end
-- Guardrail: cap strong outcomes (denied/anchorage review) total per day
if (out=="Denied entry pending documents" or out=="Escorted to anchorage for formal review")
and state.strongCount >= state.strongCap then
out = "Administrative notice issued; monitoring"
elseif out=="Denied entry pending documents" or out=="Escorted to anchorage for formal review" then
state.strongCount = state.strongCount + 1
end
-- Sub-rules & notes
local notes = string.format("Cargo: %s [%s]; Partner: %s — ", cargo.name, tag.name, partner.code)
if vessel=="Undeclared submarine" then
act = {name="Submarine declare signal issued", basis="Submarine Surface/Declare"}
cite = clauseIndex[act.basis]
notes=notes.."Undeclared contact directed to surface and declare per procedure"
if out=="No reports" then out="Administrative notice issued; monitoring" end
elseif vessel=="Declared submarine" then
notes=notes.."Declared sub transited in daylight per SC §C2–C3"
elseif sector=="Sacred Buffer Approaches" then
notes=notes.."Stand-off distance verified for sacred sites"
elseif sector=="Environmental Buffer" then
notes=notes.."Sensitive habitat protection enforced during passage"
end
-- IF/Matamoros narrative softener for delays (keep language neutral)
if (flag.name=="Imperial Federation" or flag.name=="Matamoros (IF)")
and (out=="Random audit hold; clearance pending procedural update"
or out=="Documentation harmonization hold (routine)") then
notes = notes.."; sampled under routine process calibration"
end
if out~="No reports" then state.enfCount = state.enfCount + 1 end
return {
date=dateText, sector=sector,
vessel=vessel.." ("..flag.name..")",
action=act.name, legal=(act.basis.." — "..cite),
outcome=out, notes=notes
}
end
-------------------------------------------------------------------------------
-- (F) PUBLIC: makeTable
-------------------------------------------------------------------------------
function p.makeTable(frame)
local dateInfo = getPSSCDate()
math.randomseed(dateInfo.year*1000 + dateInfo.dayOfYear)
local ROWS = tonumber(frame and frame.args and frame.args.rows) or 18
local out = {}
table.insert(out, '{| class="wikitable sortable"')
table.insert(out, '! Date !! Sector !! Vessel/Flag !! Action !! Legal Basis !! Outcome !! Notes')
-- Per-flag cap: ~35% of rows per registry max (rounded up)
local maxPerFlag = math.max(1, math.ceil(ROWS * 0.35))
local state = {
strongCount = 0, -- denied/anchorage combined
strongCap = 3, -- daily cap
flagCounts = {},
lastFlag = nil,
enfCount = 0,
maxPerFlag = maxPerFlag
}
local events = {}
for i=1, ROWS do
local ev = makeEvent(dateInfo.text, state)
table.insert(events, ev)
end
-- Balance guardrail: keep total enforcement ≤ 40% of rows
local maxEnf = math.floor(ROWS * 0.40)
if state.enfCount > maxEnf then
local toReduce = state.enfCount - maxEnf
for _,ev in ipairs(events) do
if toReduce <= 0 then break end
if ev.outcome ~= "No reports" and ev.outcome ~= "Compliant; cleared" then
ev.outcome = "Compliant; cleared"
if ev.notes=="—" then
ev.notes = "Auto-balanced for daily transparency"
else
ev.notes = ev.notes.."; auto-balanced"
end
toReduce = toReduce - 1
end
end
end
for _,ev in ipairs(events) do
table.insert(out, "|-")
table.insert(out, "| "..ev.date.." || "..ev.sector.." || "..ev.vessel.." || "..ev.action.." || "..ev.legal.." || "..ev.outcome.." || "..ev.notes)
end
table.insert(out, '|}')
table.insert(out, '<!-- StraitsDaily v4.1 (high-risk cargo focus; coded partner scrutiny; neutral procedural holds) -->')
return table.concat(out, "\n")
end
return p