Module:PillSeasonSchedule
From MicrasWiki
Documentation for this module may be created at Module:PillSeasonSchedule/doc
-- Module:PillSeasonSchedule
local p = {}
local date = require('Module:BassaridianCalendar')
-- 1) TEAM DEFINITIONS (with logos used only in the schedule)
local teams = {
{ code="Allegro Symphonara", fullName="Allegro Symphonara", logo="{{flagicon image|AllegrosymphonaraB.png|200px}}", championships=4 },
{ code="Ashguard Pyralis", fullName="Ashguard Pyralis", logo="{{flagicon image|AshguardpyralisB.png|200px}}", championships=1 },
{ code="Aurelia Auric", fullName="Aurelia Auric", logo="{{flagicon image|AureliaAuricB.png|200px}}", championships=2 },
{ code="Brightpath Aureum", fullName="Brightpath Aureum", logo="{{flagicon image|BrightpathaureumB.png|200px}}", championships=2 },
{ code="Celestial Sancta Lunaris",fullName="Celestial Sancta Lunaris", logo="{{flagicon image|CelestialsanctaB.png|200px}}", championships=1 },
{ code="Delphica Windborne", fullName="Delphica Windborne", logo="{{flagicon image|DelphicawindborneB.png|200px}}", championships=1 },
{ code="Emberstone Vaeringheim", fullName="Emberstone Vaeringheim", logo="{{flagicon image|EmberstonevaeringheimB.png|200px}}", championships=3 },
{ code="Flameborne Erythros", fullName="Flameborne Erythros", logo="{{flagicon image|FlameborneerythrosB.png|200px}}", championships=1 },
{ code="Forgeward Nexa", fullName="Forgeward Nexa", logo="{{flagicon image|ForgewardnexaB.png|200px}}", championships=0 },
{ code="Hearthkeeper Koinon", fullName="Hearthkeeper Koinon", logo="{{flagicon image|HearthkeeperkoinonB.png|200px}}", championships=0 },
{ code="Ironbough Sylvapolis", fullName="Ironbough Sylvapolis", logo="{{flagicon image|IronboughSylvapolisB.png|200px}}", championships=1 },
{ code="Redcliff Citadel", fullName="Redcliff Citadel", logo="{{flagicon image|RedcliffCitadel.png|200px}}", championships=1 },
{ code="Riftwarden Acheron", fullName="Riftwarden Acheron", logo="{{flagicon image|RiftwardenAcheronB.png|200px}}", championships=3 },
{ code="Saluria Skylight", fullName="Saluria Skylight", logo="{{flagicon image|SaluriaSkylightB.png|200px}}", championships=0 },
{ code="Sandveil Somniumpolis", fullName="Sandveil Somniumpolis", logo="{{flagicon image|SandVeilSomniumB.png|200px}}", championships=1 },
{ code="Serena Tidesong", fullName="Serena Tidesong", logo="{{flagicon image|Serena TidesongB.png|200px}}", championships=2 },
{ code="Steppe Luminaria", fullName="Steppe Luminaria", logo="{{flagicon image|Steppe\"LuminariaB.png|200px}}", championships=3 },
{ code="Strider Myrene", fullName="Strider Myrene", logo="{{flagicon image|StriderMyreneB.png|200px}}", championships=1 },
{ code="Vaeringheim Pillar", fullName="Vaeringheim Pillar", logo="{{flagicon image|VaeringheimPillarB.png|200px}}", championships=5 },
{ code="Imperial Delphica", fullName="Imperial Delphica", logo="{{flagicon image|ImperialDelphicaB.png|200px}}", championships=1 },
{ code="Ascendant Aetherium", fullName="Ascendant Aetherium", logo="{{flagicon image|AscendantAetheriumB.png|200px}}", championships=2 },
{ code="Sufriya Stormwake", fullName="Sufriya Stormwake", logo="{{flagicon image|SufriyaStormwake.png|200px}}", championships=1 },
{ code="Pillarion Club Suncliff", fullName="Pillarion Club Suncliff", logo="{{flagicon image|PCSuncliff.png|200px}}", championships=0 },
{ code="Jogi Regiment", fullName="Jogi Regiment", logo="{{flagicon image|JogiRegiment.png|200px}}", championships=0 },
{ code="Vine Fleet Mylecia", fullName="Vine Fleet Mylecia", logo="{{flagicon image|VineFleetMylecia.png|200px}}", championships=0 },
{ code="Port of Blore Heath", fullName="Port of Blore Heath", logo="{{flagicon image|PortBloreHeath.png|200px}}", championships=0 },
{ code="Free State Abeis", fullName="Free State Abeis", logo="{{flagicon image|FreeStateAbeis.png|200px}}", championships=0 },
{ code="Jezeraah City", fullName="Jezeraah City", logo="{{flagicon image|JezeraahCity.png|200px}}", championships=0 }
}
-- 2) DOUBLE ROUND-ROBIN GENERATOR
local function generateSchedule()
local n = #teams
assert(n % 2 == 0)
local pool = { unpack(teams) }
local fixed = table.remove(pool,1)
local rounds = {}
for r=1,n-1 do
local pairings = {}
if r % 2 == 1 then
table.insert(pairings,{fixed,pool[1]})
else
table.insert(pairings,{pool[1],fixed})
end
for i=2, n/2 do
local t1,t2 = pool[i], pool[#pool-(i-2)]
if i % 2 == 0 then
table.insert(pairings,{t1,t2})
else
table.insert(pairings,{t2,t1})
end
end
rounds[r] = pairings
table.insert(pool,1,table.remove(pool))
end
for r=1,n-1 do
local rev = {}
for _,m in ipairs(rounds[r]) do
table.insert(rev,{m[2],m[1]})
end
rounds[n-1+r] = rev
end
return rounds
end
-- 3) MAP ROUNDS TO PSSC DAYS
local START,END = 62,183
local function mapDays()
local total = END-START+1
local rounds = (#teams-1)*2
local interval = math.floor(total/rounds)
local extra = total % rounds
local m,day = {}, START
for i=1,rounds do
m[day] = i
day = day + interval + (extra>0 and 1 or 0)
if extra>0 then extra = extra-1 end
end
return m
end
-- 4) SIMULATE A GIVEN DAY
local function simulateDayResults(day)
local sched = generateSchedule()
local dm = mapDays()
local ridx = dm[day]
if not ridx then return {} end
math.randomseed(day)
local res = {}
for _,m in ipairs(sched[ridx]) do
local h,a = m[1],m[2]
local draw = 0.1
local r = math.random()
local hg,ag
if r < draw then
hg = math.random(0,12); ag = hg
else
local wh,wa = h.championships+1, a.championships+1
local rem = (r-draw)/(1-draw)
local homeWin = rem < (wh/(wh+wa))
local winG = math.random(1,12)
local loseG = math.random(0,math.min(winG-1,12))
if homeWin then hg,ag = winG,loseG else hg,ag = loseG,winG end
end
table.insert(res,{home=h,away=a,hg=hg,ag=ag})
end
return res
end
-- 5) RENDER TODAY’S MATCHDAY
local function renderRound(day,year)
local dm = mapDays()
if not dm[day] then return "'''No matches scheduled today'''" end
local month,dom = date.getDateFromDay(day)
local label = dom.." "..month.." "..year.." PSSC"
local content = ""
for _,r in ipairs(simulateDayResults(day)) do
local h,a,hg,ag = r.home,r.away,r.hg,r.ag
local hcol = hg>ag and "green" or hg<ag and "red" or "yellow"
local acol = ag>hg and "green" or ag<hg and "red" or "yellow"
local hs = "<span style='color:"..hcol.."'>'''"..hg.."'''</span>"
local as = "<span style='color:"..acol.."'>'''"..ag.."'''</span>"
content = content..h.logo.." "..hs.." "..h.fullName.." vs "..a.logo.." "..as.." "..a.fullName.."<br>"
end
content = content:sub(1,-5)
return "|-\n| "..day.." || "..label.." || "..content
end
function p.renderSchedule(frame)
local curr = date.getCurrentDate()
local day = tonumber(curr:match('^(%d+),')) or 0
local year = tonumber(curr:match(', (%d+) PSSC')) or 0
if not mapDays()[day] then return "'''No matches scheduled today'''" end
return "{| class=\"wikitable sortable\"\n! Day !! Date !! Matches\n"..renderRound(day,year).."\n|}"
end
-- 6) CURRENT STANDINGS (no logos)
function p.renderStandings(frame)
local curr = date.getCurrentDate()
local today = tonumber(curr:match('^(%d+),')) or 0
local last = math.min(today,END)
local recs = {}
for _,t in ipairs(teams) do recs[t.code]={team=t,W=0,D=0,L=0,Pts=0,PF=0,PA=0} end
for d=START,last do
for _,r in ipairs(simulateDayResults(d)) do
local hR,aR = recs[r.home.code], recs[r.away.code]
hR.PF, hR.PA = hR.PF + r.hg, hR.PA + r.ag
aR.PF, aR.PA = aR.PF + r.ag, aR.PA + r.hg
if r.hg>r.ag then
hR.W,hR.Pts = hR.W+1,hR.Pts+3; aR.L=aR.L+1
elseif r.hg<r.ag then
aR.W,aR.Pts = aR.W+1,aR.Pts+3; hR.L=hR.L+1
else
hR.D,hR.Pts = hR.D+1,hR.Pts+1
aR.D,aR.Pts = aR.D+1,aR.Pts+1
end
end
end
local list = {}
for _,r in pairs(recs) do r.PD=r.PF-r.PA; table.insert(list,r) end
table.sort(list,function(a,b)
if a.Pts~=b.Pts then return a.Pts>b.Pts end
if a.PD~=b.PD then return a.PD>b.PD end
if a.W~=b.W then return a.W>b.W end
return a.team.fullName<b.team.fullName
end)
local out = {'{| class="wikitable sortable"','! Pos !! Team !! W !! D !! L !! Pts !! PF !! PA !! PD'}
for i,r in ipairs(list) do
table.insert(out,string.format("|-\n| %d || %s || %d || %d || %d || %d || %d || %d || %d",
i, r.team.fullName, r.W, r.D, r.L, r.Pts, r.PF, r.PA, r.PD))
end
table.insert(out,"|}")
return table.concat(out,"\n")
end
return p