Modul:Turnierplan

aus Wikipedia, der freien Enzyklopädie
Vorlagenprogrammierung Diskussionen Lua Unterseiten
Modul Deutsch

Modul: Dokumentation

Diese Seite enthält Code in der Programmiersprache Lua. Einbindungszahl Cirrus

Modul zur Erstellung kompletter Turnierpläne.

Beispiele

{{#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 function notNilStr(s)
	if s == nil then return '' end
	return s
end

local function fillRounds(runden)
	if type(runden) == "string" then
		runden = tonumber(runden)
	end
	if type(runden) == "number" then
		for i = 1, runden do
			rounds[i] = 'Runde ' .. i
		end
	else
		rounds = runden
	end
end

local function setBracketSize(bracket)
	if bracket.branch[0].sizeU == 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].sizeO == 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, i)
	local branch = createBranch()
	branch.key = key
	branch.sizeO = 1 - i
	branch.sizeU = i
	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 then
			branch[i] = atomBranch(key, args, i)
		elseif round == tonumber(args.runden) then
			args[entry] = ''
			branch[i] = atomBranch(entry, args, i)
		else
			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, math.floor((2 * i - 1)/j))
		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 straightLine(ret, x, y, color)
	ret[x][y] = 'colspan="2" style="border-bottom:2px solid' .. color .. '"'
	ret[x][y + 1] = 'colspan="2" style="border-top:1px solid' .. color .. '"'
end

local function connectLine(ret, x, y, d, wid, pos, th, z, color)
	local fullColor = ''
	if color ~= '' then fullColor = ' border-color:' .. string.sub(color, 2, -1) end
	ret[x][y] = 'rowspan="' .. d .. '" style="border-width:' .. wid .. ' 0; border-style:solid;' .. fullColor .. '"| ||rowspan="' .. d .. '" style="border-' .. pos .. ':3px solid' .. color .. '"'
	ret[x][z] = 'style="border-' .. pos .. ':' .. th .. 'px solid' .. color .. '"| |'

	for i = 1, d-1 do
		ret[x][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
	if y0 == y1 then
		straightLine(ma, x, y1 - 1, color)
	elseif y0 < y1 then
		connectLine(ma, x, y0, y1 - y0, '1px 3px 0', 'bottom', 2, y0 - 1, color)
	else
		connectLine(ma, x, y1, y0 - y1, '0 3px 2px', 'top', 1, y0, color)
	end
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].sizeU == 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 = {}

function tp.processArgs(args)
	fillRounds(args.runden)
	for i = 1, table.getn(rounds) do
		if args['rd' .. i] ~= nil then
			rounds[i] = args['rd' .. i]
		elseif rounds[i] ~= 'Runde ' ..  i then
		elseif table.getn(rounds) == i + 3 then
			rounds[i] = 'Achtelfinale'
		elseif table.getn(rounds) == i + 2 then
			rounds[i] = 'Viertelfinale'
		elseif table.getn(rounds) == i + 1 then
			rounds[i] = 'Halbfinale'
		elseif table.getn(rounds) == i then
			rounds[i] = 'Finale'
			if args.finale ~= nil then rounds[i] = args.finale end
		end
	end
	if args.width ~= nil then nameWidth = args.width end
	if args.nameWidth ~= nil then nameWidth = args.nameWidth 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