Module:StraitsDaily: Difference between revisions

From MicrasWiki
Jump to navigationJump to search
NewZimiaGov (talk | contribs)
No edit summary
NewZimiaGov (talk | contribs)
No edit summary
Line 35: Line 35:
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
local sectors = {
local sectors = {
     "Strait South Inbound","Strait South Outbound","Strait North Inbound","Strait North Outbound",
     "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"
     "Archipelago Zone", "Sacred Buffer Approaches", "Environmental Buffer", "Northern Approach TSS", "Southern Approach TSS"
}
}


local flags = {
local flags = {
     {name="Bassaridian-flag",                     weight=6, risky=false},
     {name="Bassaridian-flag", weight=6, risky=false},
     {name="Moorland",                             weight=2, risky=false},
     {name="Moorland", weight=2, risky=false},
     {name="Imperial Federation",                 weight=5, risky=true},
     {name="Imperial Federation", weight=5, risky=true},
     {name="Matamoros (IF)",                       weight=2, risky=true},
     {name="Matamoros (IF)", weight=2, risky=true},
     {name="Neutral Merchant",                     weight=4, risky=false},
     {name="Neutral Merchant", weight=4, risky=false},
     {name="Distant Partner (Euranidom)",         weight=3, risky=true},
     {name="Distant Partner (Euranidom)", weight=3, risky=true},
     {name="Distant Partner (Corumia)",           weight=3, risky=true},
     {name="Distant Partner (Corumia)", weight=3, risky=true},
     {name="Distant Partner (Southern Keltiania)", weight=3, risky=true},
     {name="Distant Partner (Southern Keltiania)", weight=3, risky=true},
     {name="Consortium Registry",                 weight=2, risky=true},
     {name="Consortium Registry", weight=2, risky=true},
     {name="Other Coastal State",                 weight=7, risky=false}
     {name="Other Coastal State", weight=7, risky=false}
}
}


local vessels = {
local vessels = {
     "Handymax bulker","Product tanker","General cargo ship","Container feeder",
     "Handymax bulker", "Product tanker", "General cargo ship", "Container feeder",
     "Ro-Ro ferry","Research craft","Declared submarine","Undeclared submarine"
     "Ro-Ro ferry", "Research craft", "Declared submarine", "Undeclared submarine"
}
}


local cargoTypes = {
local cargoTypes = {
     {name="Medical reagents (dual-use precursors)",         weight=3, risk=0.14, dual=true},
     {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="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="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="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="Satellite components / sensors (dual-use)", weight=1, risk=0.18, dual=true},
     {name="Industrial fasteners (coded)",                   weight=2, risk=0.12, dual=true},
     {name="Industrial fasteners (coded)", weight=2, risk=0.12, dual=true},
     {name="Hydraulic lubricants (dual-use)",                 weight=2, risk=0.13, dual=true},
     {name="Hydraulic lubricants (dual-use)", weight=2, risk=0.13, dual=true},
     {name="High-frequency alloys (coded)",                   weight=1, risk=0.14, dual=true},
     {name="High-frequency alloys (coded)", weight=1, risk=0.14, dual=true},
     {name="Aviation fuels & additives",                     weight=3, risk=0.12, dual=false},
     {name="Aviation fuels & additives", weight=3, risk=0.12, dual=false},
     {name="Refined petroleum products",                     weight=4, risk=0.08, 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="Relief supplies (mixed pallets)", weight=5, risk=0.06, dual=false},
     {name="Agricultural commodities",                       weight=5, risk=0.04, dual=false},
     {name="Agricultural commodities", weight=5, risk=0.04, dual=false},
     {name="Finished consumer goods",                         weight=6, risk=0.03, dual=false}
     {name="Finished consumer goods", weight=6, risk=0.03, dual=false}
}
}


local cargoTags = {
local cargoTags = {
     {name="standard", weight=6, risk=0.00},
     {name="standard", weight=6, risk=0.00},
     {name="expedited", weight=3, risk=0.05},
     {name="expedited", weight=3, risk=0.05},
     {name="emergency", weight=2, risk=0.07},
     {name="emergency", weight=2, risk=0.07},
Line 92: Line 92:


-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- (C) UTILITIES
-- (C) MAIN GENERATION
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
local function pickWeighted(list)
function p.makeTable(frame)
    local total=0
    local dateInfo = getPSSCDate()
    for _,item in ipairs(list) do total=total+(item.weight or 1) end
    math.randomseed(dateInfo.year*1000 + dateInfo.dayOfYear)
    local roll,cumul=math.random(total),0
    local rows = tonumber(frame and frame.args and frame.args.rows) or 18
    for _,item in ipairs(list) do
 
        cumul=cumul+(item.weight or 1)
    local function pickWeighted(list)
        if roll<=cumul then return item end
        if type(list) ~= "table" then return nil end
        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
     end
    return list[#list]
end


local function pick(list) return list[math.random(#list)] end
    local function pick(list) return list[math.random(#list)] end


-------------------------------------------------------------------------------
    local function baseProb(flag, sector, cargo, tag, partner)
-- (D) ENFORCEMENT LOGIC
        local p = 0.28
-------------------------------------------------------------------------------
        if flag.risky then p = p + 0.06 end
local function baseProb(flag, sector, cargo, tag, partner)
        if sector == "Sacred Buffer Approaches" or sector == "Environmental Buffer" then p = p + 0.08 end
    local p = 0.28
        p = p + (cargo and cargo.risk or 0)
    if flag.risky then p = p + 0.06 end
        if cargo and cargo.dual then p = p + 0.03 end
    if sector=="Sacred Buffer Approaches" or sector=="Environmental Buffer" then p = p + 0.08 end
        p = p + (tag and tag.risk or 0)
    p = p + (cargo and (cargo.risk or 0) or 0)
        p = p + (partner and partner.risk or 0)
    if cargo and cargo.dual then p = p + 0.03 end
        return math.min(p, 0.72)
    p = p + (tag and (tag.risk or 0) or 0)
     end
    p = p + (partner and (partner.risk or 0) or 0)
    if p > 0.72 then p = 0.72 end
     return p
end


-------------------------------------------------------------------------------
-- (E) MAIN EVENT GENERATION
-------------------------------------------------------------------------------
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 = {}
     local out = {}
     table.insert(out, '{| class="wikitable sortable"')
     table.insert(out, "{| class='wikitable sortable'")
     table.insert(out, '! Date !! Sector !! Vessel/Flag !! Action !! Legal Basis !! Outcome !! Notes')
     table.insert(out, "! Date !! Sector !! Vessel/Flag !! Action !! Legal Basis !! Outcome !! Notes")
 
    local maxPerFlag = math.max(1, math.ceil(ROWS * 0.35))
    local state = {
        strongCount = 0, strongCap = 3, flagCounts = {}, lastFlag = nil,
        enfCount = 0, maxPerFlag = maxPerFlag
    }


     local function makeEvent(dateText)
     for _ = 1, rows do
         local sector = pick(sectors)
         local sector = pick(sectors)
         local flag = pickWeighted(flags)
         local flag = pickWeighted(flags)
Line 148: Line 136:
         local partner = pickWeighted(partnerProfiles)
         local partner = pickWeighted(partnerProfiles)


         local prob = baseProb(flag, sector, cargo, tag, partner)
         local enforce = baseProb(flag, sector, cargo, tag, partner)
         if math.random() > prob then
         local outcome, action, legal, notes
            return {date=dateText, sector=sector, vessel=vessel.." ("..flag.name..")",
                    action="Routine transit", legal="N/A", outcome="No reports",
                    notes="Cargo: "..cargo.name.." ["..tag.name.."]; Partner: "..partner.code.." — Compliant passage"}
        end


         local action = pickWeighted(actions)
         if math.random() > enforce then
        local legal = action.basis.." — "..(clauses[action.basis] or "SC §—")
            outcome = "No reports"
        local outcome = pick(outcomes)
            action = "Routine transit"
            legal = "N/A"
            notes = string.format("Cargo: %s [%s]; Partner: %s — Compliant passage", cargo.name, tag.name, partner.code)
        else
            action = "Cargo manifest reconciliation audit"
            legal = "SC §R1–R3"
            outcome = "Administrative notice issued; monitoring"
            notes = string.format("Cargo: %s [%s]; Partner: %s", cargo.name, tag.name, partner.code)


        local riskPush = 0
            local riskPush = 0
        if flag.name:match("Imperial Federation") or flag.name:match("Matamoros") then riskPush = riskPush + 1 end
            if flag.name:match("Imperial Federation") or flag.name:match("Matamoros") then riskPush = riskPush + 1 end
        if cargo.dual or (cargo.risk >= 0.12) 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 tag.name ~= "standard" then riskPush = riskPush + 1 end
        if partner.risk >= 0.09 then riskPush = riskPush + 1 end
            if partner.risk >= 0.09 then riskPush = riskPush + 1 end


        if riskPush >= 2 and math.random() < 0.65 then
            if riskPush >= 2 and math.random() < 0.65 then
            local delays = {
                outcome = "Random audit hold; clearance pending procedural update"
                "Random audit hold; clearance pending procedural update",
                 if flag.name == "Imperial Federation" or flag.name == "Matamoros (IF)" then
                 "Documentation harmonization hold (routine)",
                    outcome = outcome .. " [Extended regulatory review]"
                "Administrative notice issued; monitoring"
                 end
            }
            outcome = delays[math.random(#delays)]
        end
 
        if outcome:match("anchorage") or outcome:match("Denied entry") then
            if state.strongCount >= state.strongCap then
                outcome = "Administrative notice issued; monitoring"
            else
                 state.strongCount = state.strongCount + 1
             end
             end
         end
         end


        if (flag.name == "Imperial Federation" or flag.name == "Matamoros (IF)") and
          (outcome:match("audit hold") or outcome:match("harmonization")) then
            outcome = outcome .. " [Extended regulatory review]"
        end
        state.enfCount = state.enfCount + 1
        local note = "Cargo: "..cargo.name.." ["..tag.name.."]; Partner: "..partner.code
        return {date=dateText, sector=sector, vessel=vessel.." ("..flag.name..")",
                action=action.name, legal=legal, outcome=outcome, notes=note}
    end
    for i=1, ROWS do
        local ev = makeEvent(dateInfo.text)
         table.insert(out, "|-")
         table.insert(out, "|-")
         table.insert(out, "| "..ev.date.." || "..ev.sector.." || "..ev.vessel.." || "..ev.action.." || "..ev.legal.." || "..ev.outcome.." || "..ev.notes)
         table.insert(out, string.format("| %s || %s || %s (%s) || %s || %s || %s || %s",
            dateInfo.text, sector, vessel, flag.name, action, legal, outcome, notes))
     end
     end


     table.insert(out, '|}')
     table.insert(out, "|}")
    table.insert(out, '<!-- StraitsDaily v5.0: Shelf-life tags, military-adjacent profiling, enhanced IF/Matamoros visibility -->')
     return table.concat(out, "\n")
     return table.concat(out, "\n")
end
end


return p
return p

Revision as of 16:49, 6 September 2025

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"
}

local flags = {
    {name="Bassaridian-flag", weight=6, risky=false},
    {name="Moorland", weight=2, risky=false},
    {name="Imperial Federation", weight=5, risky=true},
    {name="Matamoros (IF)", weight=2, risky=true},
    {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"
}

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="Industrial fasteners (coded)", weight=2, risk=0.12, dual=true},
    {name="Hydraulic lubricants (dual-use)", weight=2, risk=0.13, dual=true},
    {name="High-frequency alloys (coded)", weight=1, risk=0.14, 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}
}

local cargoTags = {
    {name="standard", weight=6, risk=0.00},
    {name="expedited", weight=3, risk=0.05},
    {name="emergency", weight=2, risk=0.07},
    {name="perishable", weight=2, risk=0.06}
}

local partnerProfiles = {
    {code="P-07", weight=4, risk=0.00},
    {code="P-12", weight=3, risk=0.03},
    {code="P-17", weight=3, risk=0.12},
    {code="P-22", weight=3, risk=0.14},
    {code="P-31", weight=3, risk=0.05},
    {code="P-38", weight=2, risk=0.10},
    {code="P-44", weight=4, risk=0.02},
    {code="P-53", weight=3, risk=0.04}
}

-------------------------------------------------------------------------------
-- (C) MAIN GENERATION
-------------------------------------------------------------------------------
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 function pickWeighted(list)
        if type(list) ~= "table" then return nil end
        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 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
        p = p + (cargo and cargo.risk or 0)
        if cargo and cargo.dual then p = p + 0.03 end
        p = p + (tag and tag.risk or 0)
        p = p + (partner and partner.risk or 0)
        return math.min(p, 0.72)
    end

    local out = {}
    table.insert(out, "{| class='wikitable sortable'")
    table.insert(out, "! Date !! Sector !! Vessel/Flag !! Action !! Legal Basis !! Outcome !! Notes")

    for _ = 1, rows do
        local sector = pick(sectors)
        local flag = pickWeighted(flags)
        local vessel = pick(vessels)
        local cargo = pickWeighted(cargoTypes)
        local tag = pickWeighted(cargoTags)
        local partner = pickWeighted(partnerProfiles)

        local enforce = baseProb(flag, sector, cargo, tag, partner)
        local outcome, action, legal, notes

        if math.random() > enforce then
            outcome = "No reports"
            action = "Routine transit"
            legal = "N/A"
            notes = string.format("Cargo: %s [%s]; Partner: %s — Compliant passage", cargo.name, tag.name, partner.code)
        else
            action = "Cargo manifest reconciliation audit"
            legal = "SC §R1–R3"
            outcome = "Administrative notice issued; monitoring"
            notes = string.format("Cargo: %s [%s]; Partner: %s", cargo.name, tag.name, partner.code)

            local riskPush = 0
            if flag.name:match("Imperial Federation") or flag.name:match("Matamoros") 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
                outcome = "Random audit hold; clearance pending procedural update"
                if flag.name == "Imperial Federation" or flag.name == "Matamoros (IF)" then
                    outcome = outcome .. " [Extended regulatory review]"
                end
            end
        end

        table.insert(out, "|-")
        table.insert(out, string.format("| %s || %s || %s (%s) || %s || %s || %s || %s",
            dateInfo.text, sector, vessel, flag.name, action, legal, outcome, notes))
    end

    table.insert(out, "|}")
    return table.concat(out, "\n")
end

return p