Module:PillSeasonSchedule: Difference between revisions
From MicrasWiki
Jump to navigationJump to search
NewZimiaGov (talk | contribs) No edit summary |
NewZimiaGov (talk | contribs) No edit summary |
||
| Line 3: | Line 3: | ||
local date = require('Module:BassaridianCalendar') | local date = require('Module:BassaridianCalendar') | ||
-- 1) TEAM DEFINITIONS ( | -- 1) TEAM DEFINITIONS (20px thumbnails) | ||
local teams = { | local teams = { | ||
{ code="Allegro Symphonara", fullName="Allegro Symphonara", logo=" | { code="Allegro Symphonara", fullName="Allegro Symphonara", logo="[[File:AllegrosymphonaraB.png|20px]]", championships=4 }, | ||
{ code="Ashguard Pyralis", fullName="Ashguard Pyralis", logo=" | { code="Ashguard Pyralis", fullName="Ashguard Pyralis", logo="[[File:AshguardpyralisB.png|20px]]", championships=1 }, | ||
{ code="Aurelia Auric", fullName="Aurelia Auric", logo=" | { code="Aurelia Auric", fullName="Aurelia Auric", logo="[[File:AureliaAuricB.png|20px]]", championships=2 }, | ||
{ code="Brightpath Aureum", fullName="Brightpath Aureum", logo=" | { code="Brightpath Aureum", fullName="Brightpath Aureum", logo="[[File:BrightpathaureumB.png|20px]]", championships=2 }, | ||
{ code="Celestial Sancta Lunaris",fullName="Celestial Sancta Lunaris", logo=" | { code="Celestial Sancta Lunaris",fullName="Celestial Sancta Lunaris", logo="[[File:CelestialsanctaB.png|20px]]", championships=1 }, | ||
{ code="Delphica Windborne", fullName="Delphica Windborne", logo=" | { code="Delphica Windborne", fullName="Delphica Windborne", logo="[[File:DelphicawindborneB.png|20px]]", championships=1 }, | ||
{ code="Emberstone Vaeringheim", fullName="Emberstone Vaeringheim", logo=" | { code="Emberstone Vaeringheim", fullName="Emberstone Vaeringheim", logo="[[File:EmberstonevaeringheimB.png|20px]]", championships=3 }, | ||
{ code="Flameborne Erythros", fullName="Flameborne Erythros", logo=" | { code="Flameborne Erythros", fullName="Flameborne Erythros", logo="[[File:FlameborneerythrosB.png|20px]]", championships=1 }, | ||
{ code="Forgeward Nexa", fullName="Forgeward Nexa", logo=" | { code="Forgeward Nexa", fullName="Forgeward Nexa", logo="[[File:ForgewardnexaB.png|20px]]", championships=0 }, | ||
{ code="Hearthkeeper Koinon", fullName="Hearthkeeper Koinon", logo=" | { code="Hearthkeeper Koinon", fullName="Hearthkeeper Koinon", logo="[[File:HearthkeeperkoinonB.png|20px]]", championships=0 }, | ||
{ code="Ironbough Sylvapolis", fullName="Ironbough Sylvapolis", logo=" | { code="Ironbough Sylvapolis", fullName="Ironbough Sylvapolis", logo="[[File:IronboughSylvapolisB.png|20px]]", championships=1 }, | ||
{ code="Redcliff Citadel", fullName="Redcliff Citadel", logo=" | { code="Redcliff Citadel", fullName="Redcliff Citadel", logo="[[File:RedcliffCitadel.png|20px]]", championships=1 }, | ||
{ code="Riftwarden Acheron", fullName="Riftwarden Acheron", logo=" | { code="Riftwarden Acheron", fullName="Riftwarden Acheron", logo="[[File:RiftwardenAcheronB.png|20px]]", championships=3 }, | ||
{ code="Saluria Skylight", fullName="Saluria Skylight", logo=" | { code="Saluria Skylight", fullName="Saluria Skylight", logo="[[File:SaluriaSkylightB.png|20px]]", championships=0 }, | ||
{ code="Sandveil Somniumpolis", fullName="Sandveil Somniumpolis", logo=" | { code="Sandveil Somniumpolis", fullName="Sandveil Somniumpolis", logo="[[File:SandVeilSomniumB.png|20px]]", championships=1 }, | ||
{ code="Serena Tidesong", fullName="Serena Tidesong", logo=" | { code="Serena Tidesong", fullName="Serena Tidesong", logo="[[File:SerenaTidesongB.png|20px]]", championships=2 }, | ||
{ code="Steppe Luminaria", fullName="Steppe Luminaria", logo=" | { code="Steppe Luminaria", fullName="Steppe Luminaria", logo="[[File:SteppeLuminariaB.png|20px]]", championships=3 }, | ||
{ code="Strider Myrene", fullName="Strider Myrene", logo=" | { code="Strider Myrene", fullName="Strider Myrene", logo="[[File:StriderMyreneB.png|20px]]", championships=1 }, | ||
{ code="Vaeringheim Pillar", fullName="Vaeringheim Pillar", logo=" | { code="Vaeringheim Pillar", fullName="Vaeringheim Pillar", logo="[[File:VaeringheimPillarB.png|20px]]", championships=5 }, | ||
{ code="Imperial Delphica", fullName="Imperial Delphica", logo=" | { code="Imperial Delphica", fullName="Imperial Delphica", logo="[[File:ImperialDelphicaB.png|20px]]", championships=1 }, | ||
{ code="Ascendant Aetherium", fullName="Ascendant Aetherium", logo=" | { code="Ascendant Aetherium", fullName="Ascendant Aetherium", logo="[[File:AscendantAetheriumB.png|20px]]", championships=2 }, | ||
{ code="Sufriya Stormwake", fullName="Sufriya Stormwake", logo=" | { code="Sufriya Stormwake", fullName="Sufriya Stormwake", logo="[[File:SufriyaStormwake.png|20px]]", championships=1 }, | ||
{ code="Pillarion Club Suncliff", fullName="Pillarion Club Suncliff", logo=" | { code="Pillarion Club Suncliff", fullName="Pillarion Club Suncliff", logo="[[File:PCSuncliff.png|20px]]", championships=0 }, | ||
{ code="Jogi Regiment", fullName="Jogi Regiment", logo=" | { code="Jogi Regiment", fullName="Jogi Regiment", logo="[[File:JogiRegiment.png|20px]]", championships=0 }, | ||
{ code="Vine Fleet Mylecia", fullName="Vine Fleet Mylecia", logo=" | { code="Vine Fleet Mylecia", fullName="Vine Fleet Mylecia", logo="[[File:VineFleetMylecia.png|20px]]", championships=0 }, | ||
{ code="Port of Blore Heath", fullName="Port of Blore Heath", logo=" | { code="Port of Blore Heath", fullName="Port of Blore Heath", logo="[[File:PortBloreHeath.png|20px]]", championships=0 }, | ||
{ code="Free State Abeis", fullName="Free State Abeis", logo=" | { code="Free State Abeis", fullName="Free State Abeis", logo="[[File:FreeStateAbeis.png|20px]]", championships=0 }, | ||
{ code="Jezeraah City", fullName="Jezeraah City", logo=" | { code="Jezeraah City", fullName="Jezeraah City", logo="[[File:JezeraahCity.png|20px]]", championships=0 } | ||
} | } | ||
| Line 41: | Line 41: | ||
local pool = {} | local pool = {} | ||
for i = 1, n do pool[i] = teams[i] end | for i = 1, n do pool[i] = teams[i] end | ||
local fixed = table.remove(pool,1) | local fixed = table.remove(pool, 1) | ||
local rounds = {} | local rounds = {} | ||
for r = 1, n-1 do | for r = 1, n - 1 do | ||
local pairings = {} | local pairings = {} | ||
if r % 2 == 1 then | if r % 2 == 1 then | ||
table.insert(pairings,{fixed, pool[1]}) | table.insert(pairings, { fixed, pool[1] }) | ||
else | else | ||
table.insert(pairings,{pool[1], fixed}) | table.insert(pairings, { pool[1], fixed }) | ||
end | end | ||
for i = 2, n/2 do | for i = 2, n / 2 do | ||
local t1, t2 = pool[i], pool[#pool-(i-2)] | local t1, t2 = pool[i], pool[#pool - (i - 2)] | ||
if i % 2 == 0 then | if i % 2 == 0 then | ||
table.insert(pairings,{t1, t2}) | table.insert(pairings, { t1, t2 }) | ||
else | else | ||
table.insert(pairings,{t2, t1}) | table.insert(pairings, { t2, t1 }) | ||
end | end | ||
end | end | ||
rounds[r] = pairings | rounds[r] = pairings | ||
table.insert(pool,1,table.remove(pool)) | table.insert(pool, 1, table.remove(pool)) | ||
end | end | ||
for r = 1, n-1 do | for r = 1, n - 1 do | ||
local rev = {} | local rev = {} | ||
for _, m in ipairs(rounds[r]) do | for _, m in ipairs(rounds[r]) do | ||
table.insert(rev,{m[2],m[1]}) | table.insert(rev, { m[2], m[1] }) | ||
end | end | ||
rounds[n-1+r] = rev | rounds[n - 1 + r] = rev | ||
end | end | ||
return rounds | return rounds | ||
end | end | ||
-- 3) MAP ROUNDS | -- 3) MAP ROUNDS TO PSSC DAYS | ||
local START, END = 62, 183 | local START, END = 62, 183 | ||
local function mapDays() | local function mapDays() | ||
local total = END - START + 1 | local total = END - START + 1 | ||
local rounds = (#teams-1)*2 | local rounds = (#teams - 1) * 2 | ||
local | local iv = math.floor(total / rounds) | ||
local | local ex = total % rounds | ||
local m, | local m, d = {}, START | ||
for i=1,rounds do | for i = 1, rounds do | ||
m[ | m[d] = i | ||
d = d + iv + (ex > 0 and 1 or 0) | |||
if | if ex > 0 then ex = ex - 1 end | ||
end | end | ||
return m | return m | ||
| Line 91: | Line 91: | ||
local sched = generateSchedule() | local sched = generateSchedule() | ||
local dm = mapDays() | local dm = mapDays() | ||
local | local idx = dm[day] | ||
if not | if not idx then return {} end | ||
math.randomseed(day) | math.randomseed(day) | ||
local out = {} | local out = {} | ||
for _, m in ipairs(sched[ | for _, m in ipairs(sched[idx]) do | ||
local h,a | local h, a = m[1], m[2] | ||
local | local p = math.random() | ||
local hg,ag | local hg, ag | ||
if p < | if p < 0.1 then | ||
hg = math.random(0,12); ag = hg | hg = math.random(0, 12); ag = hg | ||
else | else | ||
local wh, wa = h.championships+1, a.championships+1 | local wh, wa = h.championships + 1, a.championships + 1 | ||
local rem | local rem = (p - 0.1) / 0.9 | ||
local homeWin= rem < (wh/(wh+wa)) | local homeWin= rem < (wh / (wh + wa)) | ||
local winG = math.random(1,12) | local winG = math.random(1, 12) | ||
local loseG = math.random(0,math.min(winG-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 | if homeWin then hg, ag = winG, loseG else hg, ag = loseG, winG end | ||
end | end | ||
table.insert(out,{home=h,away=a,hg=hg,ag=ag}) | table.insert(out, { home = h, away = a, hg = hg, ag = ag }) | ||
end | end | ||
return out | return out | ||
end | end | ||
-- helper: month/ | -- helper: convert PSSC day → month/day | ||
local function getMonthDay(d) | local function getMonthDay(d) | ||
if d <= 61 then | if d <= 61 then | ||
return "Atosiel", d | return "Atosiel", d | ||
elseif d <= 122 then | elseif d <= 122 then | ||
return "Thalassiel", d-61 | return "Thalassiel", d - 61 | ||
else | else | ||
return "Opsitheiel", d-122 | return "Opsitheiel", d - 122 | ||
end | end | ||
end | end | ||
-- 5) RENDER TODAY or NEXT MATCHDAY | -- 5) RENDER TODAY or NEXT MATCHDAY | ||
local function renderRound( | local function renderRound(curDay, year) | ||
local dm | local dm = mapDays() | ||
local | local target = curDay | ||
local upcoming | local upcoming= false | ||
if not dm[ | if not dm[target] then | ||
for d = | for d = curDay + 1, END do | ||
if dm[d] then | if dm[d] then target = d; upcoming = true; break end | ||
end | end | ||
end | end | ||
if not dm[target] then | |||
if not dm[ | |||
return "'''No more matches this season.'''" | return "'''No more matches this season.'''" | ||
end | end | ||
local prefix = upcoming and "'''Next matchday:''' " or "" | local prefix = upcoming and "'''Next matchday:''' " or "" | ||
local mon, dom = getMonthDay( | local mon, dom = getMonthDay(target) | ||
local label = dom.." "..mon.." "..year.." PSSC" | local label = dom .. " " .. mon .. " " .. year .. " PSSC" | ||
local rows = {} | local rows = {} | ||
if not upcoming then | if not upcoming then | ||
for _, r in ipairs(simulateDayResults( | for _, r in ipairs(simulateDayResults(target)) do | ||
local h,a,hg,ag = r.home, r.away, r.hg, r.ag | local h, a, hg, ag = r.home, r.away, r.hg, r.ag | ||
local hc = (hg>ag and "green") or (hg<ag and "red") or "yellow" | local hc = (hg > ag and "green") or (hg < ag and "red") or "yellow" | ||
local ac = (ag>hg and "green") or (ag<hg and "red") or "yellow" | local ac = (ag > hg and "green") or (ag < hg and "red") or "yellow" | ||
local hs = string.format("<span style='color:%s;'>'''%d'''</span>", hc, hg) | local hs = string.format("<span style='color:%s;'>'''%d'''</span>", hc, hg) | ||
local as = string.format("<span style='color:%s;'>'''%d'''</span>", ac, ag) | local as = string.format("<span style='color:%s;'>'''%d'''</span>", ac, ag) | ||
| Line 161: | Line 160: | ||
end | end | ||
else | else | ||
local sched = generateSchedule() | local sched = generateSchedule() | ||
local | local idx = dm[target] | ||
for _, m in ipairs(sched[ | for _, m in ipairs(sched[idx]) do | ||
local h,a = m[1], m[2] | local h, a = m[1], m[2] | ||
table.insert(rows, | table.insert(rows, | ||
string.format("%s %s vs %s %s", | string.format("%s %s vs %s %s", | ||
| Line 176: | Line 175: | ||
return string.format( | return string.format( | ||
"|-\n| %s%d || %s || %s", | "|-\n| %s%d || %s || %s", | ||
prefix, | prefix, target, label, table.concat(rows, "<br>") | ||
) | ) | ||
end | end | ||
-- 6) | -- 6) Invoke: schedule | ||
function p.renderSchedule(frame) | function p.renderSchedule(frame) | ||
local curr = date.getCurrentDate() | local curr = date.getCurrentDate() | ||
| Line 193: | Line 192: | ||
end | end | ||
-- 7) | -- 7) Invoke: standings (no logos) | ||
function p.renderStandings(frame) | function p.renderStandings(frame) | ||
local curr = date.getCurrentDate() | local curr = date.getCurrentDate() | ||
| Line 201: | Line 200: | ||
for _, t in ipairs(teams) do | for _, t in ipairs(teams) do | ||
recs[t.code] = {team=t, W=0, D=0, L=0, Pts=0, PF=0, PA=0} | recs[t.code] = { team = t, W = 0, D = 0, L = 0, Pts = 0, PF = 0, PA = 0 } | ||
end | end | ||
| Line 210: | Line 209: | ||
aR.PF, aR.PA = aR.PF + r.ag, aR.PA + r.hg | aR.PF, aR.PA = aR.PF + r.ag, aR.PA + r.hg | ||
if r.hg > r.ag then | if r.hg > r.ag then | ||
hR.W, hR.Pts = hR.W+1, hR.Pts+3; aR.L = aR.L+1 | hR.W, hR.Pts = hR.W + 1, hR.Pts + 3; aR.L = aR.L + 1 | ||
elseif r.hg < r.ag then | elseif r.hg < r.ag then | ||
aR.W, aR.Pts = aR.W+1, aR.Pts+3; hR.L = hR.L+1 | aR.W, aR.Pts = aR.W + 1, aR.Pts + 3; hR.L = hR.L + 1 | ||
else | else | ||
hR.D, hR.Pts = hR.D+1, hR.Pts+1 | hR.D, hR.Pts = hR.D + 1, hR.Pts + 1 | ||
aR.D, aR.Pts = aR.D+1, aR.Pts+1 | aR.D, aR.Pts = aR.D + 1, aR.Pts + 1 | ||
end | end | ||
end | end | ||
| Line 225: | Line 224: | ||
table.insert(list, v) | table.insert(list, v) | ||
end | end | ||
table.sort(list, function(a,b) | table.sort(list, function(a, b) | ||
if a.Pts~=b.Pts then return a.Pts>b.Pts end | if a.Pts ~= b.Pts then return a.Pts > b.Pts end | ||
if a.PD~=b.PD | if a.PD ~= b.PD then return a.PD > b.PD end | ||
if a.W~=b.W | if a.W ~= b.W then return a.W > b.W end | ||
return a.team.fullName<b.team.fullName | return a.team.fullName < b.team.fullName | ||
end) | end) | ||
Revision as of 21:12, 19 April 2025
Documentation for this module may be created at Module:PillSeasonSchedule/doc
-- Module:PillSeasonSchedule
local p = {}
local date = require('Module:BassaridianCalendar')
-- 1) TEAM DEFINITIONS (20px thumbnails)
local teams = {
{ code="Allegro Symphonara", fullName="Allegro Symphonara", logo="[[File:AllegrosymphonaraB.png|20px]]", championships=4 },
{ code="Ashguard Pyralis", fullName="Ashguard Pyralis", logo="[[File:AshguardpyralisB.png|20px]]", championships=1 },
{ code="Aurelia Auric", fullName="Aurelia Auric", logo="[[File:AureliaAuricB.png|20px]]", championships=2 },
{ code="Brightpath Aureum", fullName="Brightpath Aureum", logo="[[File:BrightpathaureumB.png|20px]]", championships=2 },
{ code="Celestial Sancta Lunaris",fullName="Celestial Sancta Lunaris", logo="[[File:CelestialsanctaB.png|20px]]", championships=1 },
{ code="Delphica Windborne", fullName="Delphica Windborne", logo="[[File:DelphicawindborneB.png|20px]]", championships=1 },
{ code="Emberstone Vaeringheim", fullName="Emberstone Vaeringheim", logo="[[File:EmberstonevaeringheimB.png|20px]]", championships=3 },
{ code="Flameborne Erythros", fullName="Flameborne Erythros", logo="[[File:FlameborneerythrosB.png|20px]]", championships=1 },
{ code="Forgeward Nexa", fullName="Forgeward Nexa", logo="[[File:ForgewardnexaB.png|20px]]", championships=0 },
{ code="Hearthkeeper Koinon", fullName="Hearthkeeper Koinon", logo="[[File:HearthkeeperkoinonB.png|20px]]", championships=0 },
{ code="Ironbough Sylvapolis", fullName="Ironbough Sylvapolis", logo="[[File:IronboughSylvapolisB.png|20px]]", championships=1 },
{ code="Redcliff Citadel", fullName="Redcliff Citadel", logo="[[File:RedcliffCitadel.png|20px]]", championships=1 },
{ code="Riftwarden Acheron", fullName="Riftwarden Acheron", logo="[[File:RiftwardenAcheronB.png|20px]]", championships=3 },
{ code="Saluria Skylight", fullName="Saluria Skylight", logo="[[File:SaluriaSkylightB.png|20px]]", championships=0 },
{ code="Sandveil Somniumpolis", fullName="Sandveil Somniumpolis", logo="[[File:SandVeilSomniumB.png|20px]]", championships=1 },
{ code="Serena Tidesong", fullName="Serena Tidesong", logo="[[File:SerenaTidesongB.png|20px]]", championships=2 },
{ code="Steppe Luminaria", fullName="Steppe Luminaria", logo="[[File:SteppeLuminariaB.png|20px]]", championships=3 },
{ code="Strider Myrene", fullName="Strider Myrene", logo="[[File:StriderMyreneB.png|20px]]", championships=1 },
{ code="Vaeringheim Pillar", fullName="Vaeringheim Pillar", logo="[[File:VaeringheimPillarB.png|20px]]", championships=5 },
{ code="Imperial Delphica", fullName="Imperial Delphica", logo="[[File:ImperialDelphicaB.png|20px]]", championships=1 },
{ code="Ascendant Aetherium", fullName="Ascendant Aetherium", logo="[[File:AscendantAetheriumB.png|20px]]", championships=2 },
{ code="Sufriya Stormwake", fullName="Sufriya Stormwake", logo="[[File:SufriyaStormwake.png|20px]]", championships=1 },
{ code="Pillarion Club Suncliff", fullName="Pillarion Club Suncliff", logo="[[File:PCSuncliff.png|20px]]", championships=0 },
{ code="Jogi Regiment", fullName="Jogi Regiment", logo="[[File:JogiRegiment.png|20px]]", championships=0 },
{ code="Vine Fleet Mylecia", fullName="Vine Fleet Mylecia", logo="[[File:VineFleetMylecia.png|20px]]", championships=0 },
{ code="Port of Blore Heath", fullName="Port of Blore Heath", logo="[[File:PortBloreHeath.png|20px]]", championships=0 },
{ code="Free State Abeis", fullName="Free State Abeis", logo="[[File:FreeStateAbeis.png|20px]]", championships=0 },
{ code="Jezeraah City", fullName="Jezeraah City", logo="[[File:JezeraahCity.png|20px]]", championships=0 }
}
-- 2) DOUBLE ROUND‑ROBIN GENERATOR
local function generateSchedule()
local n = #teams
assert(n % 2 == 0)
local pool = {}
for i = 1, n do pool[i] = teams[i] end
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 iv = math.floor(total / rounds)
local ex = total % rounds
local m, d = {}, START
for i = 1, rounds do
m[d] = i
d = d + iv + (ex > 0 and 1 or 0)
if ex > 0 then ex = ex - 1 end
end
return m
end
-- 4) SIMULATE RESULTS FOR A DAY
local function simulateDayResults(day)
local sched = generateSchedule()
local dm = mapDays()
local idx = dm[day]
if not idx then return {} end
math.randomseed(day)
local out = {}
for _, m in ipairs(sched[idx]) do
local h, a = m[1], m[2]
local p = math.random()
local hg, ag
if p < 0.1 then
hg = math.random(0, 12); ag = hg
else
local wh, wa = h.championships + 1, a.championships + 1
local rem = (p - 0.1) / 0.9
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(out, { home = h, away = a, hg = hg, ag = ag })
end
return out
end
-- helper: convert PSSC day → month/day
local function getMonthDay(d)
if d <= 61 then
return "Atosiel", d
elseif d <= 122 then
return "Thalassiel", d - 61
else
return "Opsitheiel", d - 122
end
end
-- 5) RENDER TODAY or NEXT MATCHDAY
local function renderRound(curDay, year)
local dm = mapDays()
local target = curDay
local upcoming= false
if not dm[target] then
for d = curDay + 1, END do
if dm[d] then target = d; upcoming = true; break end
end
end
if not dm[target] then
return "'''No more matches this season.'''"
end
local prefix = upcoming and "'''Next matchday:''' " or ""
local mon, dom = getMonthDay(target)
local label = dom .. " " .. mon .. " " .. year .. " PSSC"
local rows = {}
if not upcoming then
for _, r in ipairs(simulateDayResults(target)) do
local h, a, hg, ag = r.home, r.away, r.hg, r.ag
local hc = (hg > ag and "green") or (hg < ag and "red") or "yellow"
local ac = (ag > hg and "green") or (ag < hg and "red") or "yellow"
local hs = string.format("<span style='color:%s;'>'''%d'''</span>", hc, hg)
local as = string.format("<span style='color:%s;'>'''%d'''</span>", ac, ag)
table.insert(rows,
string.format("%s %s %s vs %s %s %s",
h.logo, hs, h.fullName,
a.logo, as, a.fullName
)
)
end
else
local sched = generateSchedule()
local idx = dm[target]
for _, m in ipairs(sched[idx]) do
local h, a = m[1], m[2]
table.insert(rows,
string.format("%s %s vs %s %s",
h.logo, h.fullName,
a.logo, a.fullName
)
)
end
end
return string.format(
"|-\n| %s%d || %s || %s",
prefix, target, label, table.concat(rows, "<br>")
)
end
-- 6) Invoke: schedule
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
return table.concat({
'{| class="wikitable sortable"',
'! Day !! Date !! Matches',
renderRound(day, year),
'|}'
}, "\n")
end
-- 7) Invoke: 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 _, v in pairs(recs) do
v.PD = v.PF - v.PA
table.insert(list, v)
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, v in ipairs(list) do
table.insert(out, string.format(
"|-\n| %d || %s || %d || %d || %d || %d || %d || %d || %d",
i, v.team.fullName, v.W, v.D, v.L, v.Pts, v.PF, v.PA, v.PD
))
end
table.insert(out, "|}")
return table.concat(out, "\n")
end
return p