Modul:Turnierplan
Zur Navigation springen
Zur Suche springen
Vorlagenprogrammierung | Diskussionen | Lua | Unterseiten | ||
Modul | Deutsch
|
Modul: | Dokumentation |
Diese Seite enthält Code in der Programmiersprache Lua. Einbindungszahl Cirrus
Weiterleitung der Diskussionsseite fehlt
Modul zur Erstellung kompletter Turnierpläne.
Beispiele
[Quelltext bearbeiten]{{#invoke:Turnierplan|bracket|runden=2}}
Halbfinale | Finale | |||||||
{{#invoke:Turnierplan|bracket|reseed=ja|runden=3 |n1= |n2= |n3= |n4= |n5= |n6= |n7=}}
Viertelfinale | Halbfinale | Finale | |||||||||||
1 | |||||||||||||
4 | |||||||||||||
5 | |||||||||||||
2 | |||||||||||||
7 | |||||||||||||
3 | |||||||||||||
6 | |||||||||||||
|
„Reseeding“: In jeder Runde spielt das höchstgesetzte Team zu Hause gegen das niedrigste, das zweithöchste zu Hause gegen das zweitniedrigste und so weiter. Die hellblauen Linien entsprechen dem Fall, in dem jeweils das Heimteam gewinnt. |
local footnotes = {}
local rounds = {}
local nameWidth = '12em'
local byFinish = { 'Achtelfinale', 'Viertelfinale', 'Halbfinale', 'Finale' }
local function notNilStr(s)
if s == nil then return '' end
return s
end
--[[
fillRounds(args)
* generates rounds (global variable) by args.runden
* optionally overwrites items of rounds by args.rd
parameters:
args.runden mandantory: either a number or a predefined table
args.rd<number> optional, name of round with given number where number
starts with 1 in closed sequence
(if there's no args.rd3 then args.rd4 and subsequent
parameters are ignored.)
returns: nothing
]]
function fillRounds(args)
if args.runden then
if type(args.runden) ~= 'table' then
local count = 1
if type(args.runden) == 'string' then
local c = tonumber(args.runden)
if c then count = c end
elseif type(args.runden) == 'number' then count = args.runden
end
-- fill rounds with default names
local i = 1
-- fill rounds with generic names by given number
while i <= count - 4 do
table.insert(rounds, 'Runde ' .. i)
i = i + 1
end
if i == 1 then i = 5 - count else i = 1 end
-- fill final rounds with default names
while i <= 4 do
table.insert(rounds, byFinish[i])
i = i + 1
end
end
else
rounds = byFinish
end
-- overwrite rounds with explicitely given args
for i = 1, table.getn(rounds) do
if args['rd' .. i] ~= nil then
rounds[i] = args['rd' .. i]
end
end
if args.finale ~= nil then rounds[#rounds] = args.finale end
end
local function setBracketSize(bracket)
if bracket.branch[0].size() == 0 then
bracket.sizeO = math.max(bracket.branch[1].sizeO - 1, 3)
bracket.sizeU = math.max(bracket.branch[1].sizeU + 1, 3)
elseif bracket.branch[1].size() == 0 then
bracket.sizeO = bracket.branch[0].sizeO + 1
bracket.sizeU = math.max(bracket.branch[0].sizeU - 1, 3)
else
bracket.sizeO = bracket.branch[0].size()
bracket.sizeU = bracket.branch[1].size()
end
if bracket.header ~= nil then
bracket.sizeO = math.max(bracket.sizeO, 5)
end
if bracket.footer ~= nil then
bracket.sizeU = math.max(bracket.sizeU, 5)
end
end
local function addFootnote(fn)
local count = 1
while footnotes[count] ~= nil do
if footnotes[count] == fn then return count end
count = count + 1
end
footnotes[count] = fn
return count
end
local function parseScore(input)
local score = {}
score.num = tonumber(mw.ustring.match(input, "^%s*(%d+)"))
local fn = mw.ustring.match(input, "^%d+%*(.*)%s*$")
if fn ~= nil then
if fn == '' then fn = 'nach Verlängerung' end
local index = addFootnote(fn)
score.text = '<sup><span style="visibility:hidden;">' .. index .. '</span></sup>' .. score.num ..
'<span class="reference"><sup id="FN_tp' .. index .. '_v">[[#FN_tp' .. index ..
'|' .. index .. ']]</sup></span>'
else score.text = score.num end
return score
end
local function fillScore(bracket, score)
if score == nil then return end
local hs = parseScore(mw.ustring.match(score,"^(.+)[:-]"))
local as = parseScore(mw.ustring.match(score,"[:-](.+)$"))
if hs ~= nil and as ~= nil then
if hs.num > as.num then
bracket.branch[0].win = true
bracket.key = bracket.branch[0].key
elseif hs.num < as.num then
bracket.branch[1].win = true
bracket.key = bracket.branch[1].key
end
bracket.branch[0].score = hs.text
bracket.branch[1].score = as.text
end
end
local function fillBranch(branch, args)
if branch.key == nil then
return
end
branch.name = branch.key
if args[branch.key] ~= nil then
branch.name = args[branch.key]
end
branch.seed = args['seed_' .. branch.key]
end
local function bracketDetails(kind, key, args, round)
local kindKey = key .. string.sub(kind, 1, 1)
if args[kindKey] == nil then
if args[kind .. round] ~= nil then
kindKey = kind .. round
else
return nil
end
end
details = {}
details.text = args[kindKey]
if args[kindKey .. 's'] ~= nil then
details.style = args[kindKey .. 's']
else
details.style = args[kind .. '-style']
end
return details
end
local function fillBracket(bracket, args)
local b0, b1 = bracket.branch[0], bracket.branch[1]
local key = notNilStr(b0.key) .. '-' .. notNilStr(b1.key)
fillScore(bracket, args[key])
bracket.header = bracketDetails('header', key, args, bracket.round)
bracket.footer = bracketDetails('footer', key, args, bracket.round)
setBracketSize(bracket)
fillBranch(bracket, args)
end
local function createBranch()
local branch = {}
function branch.size()
return branch.sizeO + branch.sizeU
end
return branch
end
local function atomBranch(key, args)
local branch = createBranch()
branch.key = key
branch.sizeO = 0
branch.sizeU = 0
branch.round = 0
branch.link = args[key .. '_l']
fillBranch(branch, args)
return branch
end
local function branchesToBracket(upper, lower, args, round)
local bracket = createBranch()
bracket.branch = {}
bracket.branch[0] = upper
bracket.branch[1] = lower
if round == nil then round = math.max(upper.round, lower.round) + 1 end
bracket.round = round
fillBracket(bracket, args)
return bracket
end
local function buildBracket(args, round, spot)
local branch = {}
for i = 0, 1 do
local entry = 'r' .. round .. '_' .. spot+i
local key = args[entry]
if key == nil and round == #rounds then key = args['n'..spot+i+1] end
if key ~= nil then
branch[i] = atomBranch(key, args)
elseif round == #rounds then
args[entry] = '' -- rather be nil?
branch[i] = atomBranch(entry, args)
else
if round > 20 then
mw.logObject(args, 'args')
mw.logObject(rounds, 'rounds')
assert(false)
end
branch[i] = buildBracket(args, round + 1, 2*(spot+i))
end
end
return branchesToBracket(branch[0], branch[1], args, round + 1)
end
local function reseedColumn(t, p, args, branch)
local l, i = p - 1, 0
local reseed = false
while t <= l do
local j = 2 * p - i - 1
local bracket
local ziel = p + t
if branch[2 * p + j] == nil then
bracket = branch[2 * p + i]
else
bracket = branchesToBracket(branch[2 * p + i], branch[2 * p + j], args)
if reseed then bracket.key, bracket.seed, bracket.name = nil, nil, nil end
if bracket.branch[1].win then
ziel = p + l
t, l = t - 1, l - 1
elseif not bracket.branch[0].win and t < l then
reseed = true
local re = '\n{|\n|-\n|style="height:.2ex; width:1.2em; border-top:3px solid #CAD2EE"|\n|}\n'
if footnotes[re] == nil then
footnotes[re] = '„Reseeding“: In jeder Runde spielt das höchstgesetzte Team zu Hause gegen das niedrigste, das zweithöchste zu Hause gegen das zweitniedrigste und so weiter. Die hellblauen Linien entsprechen dem Fall, in dem jeweils das Heimteam gewinnt.'
end
end
end
bracket.reseed = reseed
branch[ziel] = bracket
t = t + 1
i = i + 1
end
end
local function buildReseedBracket(args)
local branch = {}
local j = math.pow(2, args.runden)
for i = 1, j do
local key = args['n' .. i]
if key ~= nil then
if key == '' then key = 'n' .. i end
if args['seed_' .. key] == nil then args['seed_' .. key] = i end
branch[j + i - 1] = atomBranch(key, args)
else
branch[j + i - 1] = nil
end
end
for i = args.runden - 1, 0, -1 do
reseedColumn(0, math.pow(2, i), args, branch)
end
return branch[1]
end
-- print bracket methods
local function printBox(seed, name, score, border, weight)
local ret = {}
local sty0 = '|rowspan="2" style="background:#'
local sty1 = '; border-style:solid; border-color:#AAAAAA; '
table.insert(ret, sty0..'EAECF0;'..border..' 0 1px 1px'..sty1..'text-align:center; padding:0 0.6em"| '..seed..'\n')
table.insert(ret, sty0..'F8F9FB;'..border..' 1px 1px 1px'..sty1..weight..'padding-left:0.4em"| '..name..'\n')
table.insert(ret, sty0..'F8F9FB;'..border..' 1px 1px 0'..sty1..weight..'text-align:center; padding:0 0.6em"| '..score..'\n')
return table.concat(ret)
end
local function printContent(content)
if content == nil or content == '' then
return content
end
if type(content) == "string" then
return '|rowspan="2" colspan="3" style="font-size:90%; vertical-align:bottom; padding:.2ex .5em .2ex .5em"| ' .. content .. '\n'
end
if content.text ~= nil then
local ret = '|rowspan="2" colspan="3"'
if content.style ~= nil then ret = ret .. ' style="' .. content.style .. '"' end
return ret .. '| ' .. content.text .. '\n'
end
local seed = notNilStr(content.seed)
local name = notNilStr(content.name)
if content.link ~= nil then name = '[[' .. content.link .. '|' .. name .. ']]' end
local score = notNilStr(content.score)
local weight = ''
if content.win then weight = 'font-weight:bold; ' end
return printBox(seed, name, score, ' border-width:' .. content.top, weight)
end
local function connectLine(col, y, z, color)
local s0, s1, i, pos, wid = 'rowspan="', 'px solid', 1, 'bottom:', '1px 3px 0'
if color ~= '' then s1 = s1 .. ' ' .. color end
if y == z then
col[y - 1] = 'colspan="2" style="border-bottom:2' .. s1 .. '"'
col[y] = 'colspan="2" style="border-top:1' .. s1 .. '"'
return
elseif y > z then
i, pos, wid = 0, 'top:', '0 3px 2px'
end
col[y - i] = 'style="border-' .. pos .. 1 + i .. s1 .. '"| |'
if y > z then y, z = z, y end
s0 = s0 .. z - y .. '" style="border-'
col[y] = s0 .. 'width:' .. wid .. ' 0; border-style:solid; border-color:' .. color .. '"| ||' .. s0 .. pos .. 3 .. s1 .. '"'
for i = 1, z - y - 1 do
col[y + i] = ''
end
end
local function bracketPos(pos, p0, p1, header)
if p0 ~= nil and p1 ~= nil then
return math.floor((p0+p1)/2)
elseif p0 ~= nil then
return p0 + 1
elseif p1 ~= nil then
return p1 - 1
elseif header ~= nil then
return pos + 5
else
return pos + 3
end
end
local function printLines(ma, branch, x, y1)
if branch.branch == nil then
return
end
local color = ''
if branch.reseed then
color = ' #CAD2EE'
end
local y0 = branch.pos
connectLine(ma[x], y0, y1, color)
end
local function insertContentInMatrix(ma, x, y, h, content)
if content == nil then
return
end
ma[x][y] = content
for i = 1, h - 1 do
ma[x][y + i] = ''
end
end
local function printBranch(ma, bracket, round, pos)
if bracket.branch == nil then
return ma
end
ma = printBranch(ma, bracket.branch[0], round-1, pos)
if bracket.branch[0].size() == 0 then
posOs = bracket.sizeO + 1 - bracket.branch[1].sizeO
else
posOs = bracket.branch[0].size()
end
ma = printBranch(ma, bracket.branch[1], round-1, pos + posOs)
bracket.pos = bracketPos(pos, bracket.branch[0].pos, bracket.branch[1].pos, bracket.header)
local x = 2 * round - 2
insertContentInMatrix(ma, x, bracket.pos-4, 2, bracket.header)
insertContentInMatrix(ma, x, bracket.pos+2, 2, bracket.footer)
bracket.branch[0].top = '1px'
insertContentInMatrix(ma, x, bracket.pos-2, 2, bracket.branch[0])
bracket.branch[1].top = '0'
insertContentInMatrix(ma, x, bracket.pos, 2, bracket.branch[1])
printLines(ma, bracket.branch[0], 2 * round - 3, bracket.pos - 1)
printLines(ma, bracket.branch[1], 2 * round - 3, bracket.pos + 1)
return ma
end
local function tableHeader(x, class)
local tab = {}
table.insert(tab, '{| ' .. class .. ' style="font-size:90%"\n')
table.insert(tab, '|-\n|\n')
for i = 1, x do
table.insert(tab, '|style="width:2em"| ||style="width:' .. nameWidth .. '"| ||style="width:3em"|\n')
table.insert(tab, '|style="width:1em"| ||style="width:.8em"|\n')
end
table.insert(tab, '|style="width:2em"| ||style="width:' .. nameWidth .. '"| ||style="width:3em"|\n')
return tab
end
local function matrixToTable(ma, x, y, tab)
for j = 0, y do
table.insert(tab, '|-\n|style="height:1.6ex"|\n')
for i = 0, 2*x, 2 do
if ma[i][j] == nil then
table.insert(tab, '|colspan="3"|\n')
else
table.insert(tab, printContent(ma[i][j]))
end
if ma[i+1] == nil or ma[i+1][j] == '' then
elseif ma[i+1][j] == nil then
table.insert(tab, '|colspan="2"|\n')
else
table.insert(tab, '|' .. ma[i+1][j] .. '|\n')
end
end
end
table.insert(tab, '|}\n')
return table.concat(tab)
end
local tp = {}
--[[
processArgs(args)
* preprocesses given args
* generates rounds based on args.runden
* optionally overwrites nameWidth (global variable)
parameters:
args.runden mandantory: either a number or a predefined table
args.rd<number> optional, name of round with given number where number
starts with 1 (no closed sequence needed, if <number> is
larger than count of rounds args.rd<number> is ignored.)
args.nameWidth optional, fall back to args.width
args.width optional, string, css style width information
returns: nothing
]]
function tp.processArgs(args)
fillRounds(args)
if args.nameWidth and type(args.nameWidth) == 'string' then
nameWidth = args.nameWidth
elseif args.width and type(args.width) == 'string' then
nameWidth = args.width
end
end
function tp.buildBracket(args)
if args.reseed then
return buildReseedBracket(args)
end
return buildBracket(args, 1, 0)
end
function tp.branchesToBracket(upper, lower, args)
return branchesToBracket(upper, lower, args)
end
function tp.footnoteTable()
if not next(footnotes) then
return ''
end
local ret = '\n<div style="border-bottom:1px solid #777777; margin-top:2ex; margin-bottom:.5ex; width:7em"></div><div style="padding-left:0.7em">\n'
ret = ret .. '{| border="0" cellspacing="0" style="font-size:81%"\n'
for i, fn in pairs(footnotes) do
ret = ret .. '|-\n|style="text-align:right"| '
if type(i) == "number" then
ret = ret .. '[[#FN_tp' .. i .. '_v|<sup id="FN_tp' .. i
.. '" style="display:inline-block; min-width:0.5em">' .. i .. '</sup>]]'
else
ret = ret .. i
end
ret = ret .. ' || ' .. fn .. '\n'
end
return ret .. '|}</div>'
end
function tp.bracketToTable(bracket)
local content = {}
local x = table.getn(rounds)
for i = 0, 2 * (x - 1) do
content[i] = {}
end
for i = 0, 2 * (x - 1), 2 do
content[i][0] = {}
content[i][0].style = 'text-align:center; border:1px solid; background:#EAECF0; padding:.2ex .5em .2ex .5em'
content[i][0].text = rounds[i/2 + 1]
content[i][1] = ''
end
content = printBranch(content, bracket, x, 2)
local tab = tableHeader(x - 1, 'border="0" cellpadding="0" cellspacing="0"')
return matrixToTable(content, x - 1, bracket.size(), tab)
end
function tp.bracket(frame)
tp.processArgs(frame.args)
local bracket = tp.buildBracket(frame.args)
local tabB = tp.bracketToTable(bracket)
local tabF = tp.footnoteTable()
return tabB .. tabF
end
return tp