Module:Tournoi

De Penspinning France Wiki
Aller à la navigation Aller à la recherche

Ce modèle permet de représenter un tournoi à élimination simple. Il est copié du module TeamBracket de Wikipédia.

Usage

{{#invoke: Tournoi | tournoi
| manches         =

| graine-largeur  =
| equipe-largeur  =
| score-largeur   =

| manche1         =
| manche1-groupe1 =
| manche1-graine1 =
| manche1-équipe1 =
| manche1-score1  =
 ...
}}

Liste des paramètres

Les paramètres sont les suivants:

graine-largeur – la largeur des cellules pour les graines.
équipe-largeur – la largeur des cellules pour les noms d'équipes.
score-largeur – la largeur des cellules pour les scores.
graines – mettre à non pour masquer toutes les graines.
compact – mettre à oui pour un tournoi compact et la désactivation des groupes.
manchenLe nom de la manche n.
manchen-groupemLe nom du groupe m à la manche n.
manchen-grainemLa graine de l'équipe m à la manche n.
manchen-équipemLe nom de l'équipe m à la manche n.
manchen-scoremLe score de l'équipe m à la manche n.

Descriptions des paramètres

graine-largeur
La largeur des cellules de graines en CSS. Exemples :
N [em/%/px]
auto
équipe-largeur
La largeur des cellules de noms d'équipes en CSS.
score-largeur
La largeur des cellules de score en CSS.
manchen
Le nom de la manche n. Par défaut "mèmes de finale", ..., "Quarts de finale", "Demi-finales", et "Finale", où m est le nombre d'équipes dans la manche.
manchen-groupem
Le nom du groupe m à la manche n. Pour chaque manche, chaque ensemble de quatre équipes est classifiée comme un groupe.
manchen-grainem
La graine de l'équipe m à la manche n. À la manche 1, cette valeur vaut par défaut l'allocation de graine conventionnelle pour les tournois. Si omis, la cellule est masquée. Pour masquer une cellule à la manche 1, la valeur doit explicitement être vide. m est la position comblée de 0.
manchen-équipem
Le nom de l'équipe m à la manche n. m est la position comblée de 0.
manchen-scorem
Le score de l'équipe m à la manche n. m est la position comblée de 0.

Exemples

Sans graine

{{#invoke: Tournoi | tournoi
| manches         = 2
| manche1-graine1 =
| manche1-graine2 =
| manche1-graine3 =
| manche1-graine4 =
}}
Demi-finales Finale
      
 
 
 
 
 
 

Groupes

{{#invoke: Tournoi | tournoi
| autograines     = oui
| manches         = 4
| scores          = non
| manche1-groupe1 = Pacific
| manche1-groupe2 = Mountain
| manche2-groupe1 = West
}}
16ème de finale Quarts de finale Demi-finales Finale
        
1  
16  
 
Pacific
 
9  
8  
 
West
 
5  
12  
 
Mountain
 
13  
4  
 
 
3  
14  
 
 
11  
6  
 
 
7  
10  
 
 
15  
2  

--
-- This module will implement {{Tournoi}}
--

local p = {}
local args
local rounds
local padding
local hideColspanModifier = 0
local hideSeeds
local showSeeds
local hideScores
local tracking = ''

local WIDTH_NAME          = 'largeur'
local ROUND_NAME          = 'manche'
local ROUNDS_NAME         = 'manches'
local COMPACT_NAME        = 'compact'
local AUTOSEEDS_NAME      = 'autograines'
local SEEDS_NAME          = 'graines'
local SEED_NAME           = 'graine'
local TEAM_NAME           = 'équipe'
local SCORES_NAME         = 'scores'
local SCORE_NAME          = 'score'
local GROUP_NAME          = 'groupe'
local YES_VALUE           = 'oui'
local Y_VALUE             = 'o'
local NO_VALUE            = 'non'
local N_VALUE             = 'n'
local FINALS_LABEL        = 'Finale'
local SEMIFINALS_LABEL    = 'Demi-finales'
local QUARTERFINALS_LABEL = 'Quarts de finale'
local ROUNDOF_LABEL       = '%d<sup>ème</sup> de finale'

function getSeeds()
	local seeds = {1, 2}
	local count = 2
	local before = false
	for r = 2, rounds do
		local max = math.pow(2, r)
		for i = 1, count do
			local pos = i * 2
			if before then pos = pos - 1 end
			table.insert(seeds, pos, max - seeds[i * 2 - 1] + 1)
			before = not before
		end
		count = count * 2
	end
	return seeds
end

function addTableRow(tbl)
	return tbl:tag('tr')
end

function addBlank(row, width)
	local cell = row:tag('td')
	if width then
		cell:css('width', width)
	end
	return cell
end

function addPath(rows, index, round, top, left)
	local prop = top and 'border-bottom-width' or 'border-top-width'
	if left and round == 1 then
		addBlank(rows[index]):css('height', '7px')
		addBlank(rows[index + 1]):css('height', '7px')
		return nil
	else
		local cell = addBlank(rows[index])
			:attr('rowspan', '2')
			:css('border-width', '0')
			:css('border-style', 'solid')
			:css('border-color', 'black')
		if left or round < rounds and not left then
			cell:css(prop, '2px')
		end
		return cell
	end
end

function addCompactPath(rows, index, round, top, left)
	local prop = top and 'border-bottom-width' or 'border-top-width'
	if left and round == 1 then
		addBlank(rows[index])
		return nil
	else
		local cell = addBlank(rows[index])
			:css('border-width', '0')
			:css('border-style', 'solid')
			:css('border-color', 'black')
		if left or round < rounds and not left then
			cell:css(prop, '2px')
		end
		return cell
	end
end

function getWidth(param, default)
	local arg = args[param .. '-' .. WIDTH_NAME]
	if not arg or string.len(arg) == 0 then
		arg = default
	end
	if tonumber(arg) ~= nil then
		arg = arg .. 'px'
	end
	return arg
end

function getTeamArg(round, type, team)
	return args[getTeamArgName(round, type, team)]
end

function getTeamArgName(round, type, team)
	return string.format(ROUND_NAME .. '%d-%s' .. padding, round, type, team)
end

function getRoundName(round)
	local name = args[ROUND_NAME .. round]
	if name and string.len(name) > 0 then
		return name
	end
	local roundFromLast = rounds - round + 1
	if roundFromLast == 1 then
		return FINALS_LABEL
	elseif roundFromLast == 2 then
		return SEMIFINALS_LABEL
	elseif roundFromLast == 3 then
		return QUARTERFINALS_LABEL
	else
		return string.format(ROUNDOF_LABEL, math.pow(2, roundFromLast))
	end
end

function renderTeam(row, round, team, top, compact)
    local scoreCell
	local seedCell
	local seedArg = getTeamArg(round, SEED_NAME, team)
	-- seed value for the paired team
	local pairSeedArg = getTeamArg(round, SEED_NAME,
		team % 2 == 0 and team - 1 or team + 1)
	-- show seed if seed is defined for either or both
	local showSeed = showSeeds 
		or (seedArg and string.len(seedArg) > 0)
		or (pairSeedArg and string.len(pairSeedArg) > 0)
	if showSeed and (not hideSeeds) then
		seedCell = row:tag('td')
			:css('text-align', 'center')
			:css('background-color', '#f2f2f2')
			:css('border-color', '#aaa')
			:css('border-style', 'solid')
			:css('border-top-width', '1px')
			:css('border-left-width', '1px')
			:css('border-right-width', '1px')
			:css('border-bottom-width', '0')
			:wikitext(seedArg)
			:newline()
	end

	local teamArg = getTeamArg(round, TEAM_NAME, team)
	if not teamArg or string.len(teamArg) == 0 then
		teamArg = '&nbsp;'
	end
	local teamCell = row:tag('td')
		:css('background-color', '#f9f9f9')
		:css('border-color', '#aaa')
		:css('border-style', 'solid')
		:css('border-top-width', '1px')
		:css('border-left-width', '1px')
		:css('border-right-width', '1px')
		:css('border-bottom-width', '0')
		:css('padding', '0 2px')
		:wikitext(teamArg)
		:newline()
	if not showSeed and (not hideSeeds) then
		teamCell:attr('colspan', '2')
	end

    if (not hideScores) then
        scoreCell = row:tag('td')
            :css('text-align', 'center')
            :css('border-color', '#aaa')
            :css('border-style', 'solid')
            :css('border-top-width', '1px')
            :css('border-left-width', '0')
            :css('border-right-width', '1px')
            :css('border-bottom-width', '0')
            :css('background-color', '#f9f9f9')
            :wikitext(getTeamArg(round, SCORE_NAME, team))
            :newline()
    end

	if not compact then
		if seedCell then
			seedCell:attr('rowspan', '2')
				:css('border-bottom-width', '1px')
		end
		if scoreCell then
            scoreCell:attr('rowspan', '2')
                :css('border-bottom-width', '1px')
        end
		teamCell:attr('rowspan', '2')
			:css('border-right-width', '1px')
			:css('border-bottom-width', '1px')
	else
		if not top then
			if seedCell then
				seedCell:css('border-bottom-width', '1px')
			end
			teamCell:css('border-bottom-width', '1px')
            if scoreCell then
                scoreCell:css('border-bottom-width', '1px')
            end
		end
	end
end

function renderRound(rows, count, r)
	local teams = math.pow(2, rounds - r + 1)
	local step = count / teams
	local topTeam = true -- is top row in match-up
	local topPair = true -- is top match-up in pair of match
	local team = 1
	for i = 1, count, step do
		local offset, height, blank
		-- leave room for groups for teams other than first and last
		if team == 1 or team == teams then
			offset = topTeam and i or i + 2
			height = step - 2
		else
			offset = topTeam and i + 1 or i + 2
			height = step - 3
		end
		if height > 0 then
			blank = addBlank(rows[offset])
				:attr('colspan', hideColspanModifier + 5)
				:attr('rowspan', height)
				:css('border-color', 'black')
				:css('border-style', 'solid')
				:css('border-width', '0')
		end
		-- add bracket
		local j = topTeam and i + step - 2 or i
		-- add left path
		addPath(rows, j, r, topTeam, true)
		renderTeam(rows[j], r, team, topTeam, false)
		local rightPath = addPath(rows, j, r, topTeam, false)
		if not topTeam then topPair = not topPair end
		if not topPair and r < rounds then
			if blank then blank:css('border-right-width', '2px') end
			rightPath:css('border-right-width', '2px')
		end
		team = team + 1
		topTeam = not topTeam
	end
end

function renderCompactRound(rows, count, r)
	local teams = math.pow(2, rounds - r + 1)
	local step = count / teams
	local topTeam = true -- is top row in match-up
	local topPair = true -- is top match-up in pair of match
	local team = 1

	for i = 1, count, step do
		local offset, height, blank
		-- empty space above or below
		local offset = topTeam and i or i + 1
		local height = step - 1

		if height > 0 then
			blank = addBlank(rows[offset])
				:attr('colspan', hideColspanModifier + 5)
				:css('border-color', 'black')
				:css('border-style', 'solid')
				:css('border-width', '0')
				:attr('rowspan', height)
		end
		-- add bracket
		local j = topTeam and i + step - 1 or i
		-- add left path
		addCompactPath(rows, j, r, topTeam, true)
		renderTeam(rows[j], r, team, topTeam, true)
		local rightPath = addCompactPath(rows, j, r, topTeam, false)
		if not topTeam then topPair = not topPair end
		if not topPair and r < rounds then
			if blank then blank:css('border-right-width', '2px') end
			rightPath:css('border-right-width', '2px')
		end
		team = team + 1
		topTeam = not topTeam
	end
end

function renderGroups(rows, count, round)
	local roundFromLast = rounds - round + 1
	local groups = math.pow(2, roundFromLast - 2)
	local step = count / groups
	local group = 1
	for i = step / 2, count, step do
		local name = ROUND_NAME .. round .. '-' .. GROUP_NAME .. group
		addBlank(rows[i]):css('height', '7px')
		addBlank(rows[i + 1]):css('height', '7px')
		addBlank(rows[i])
			:attr('rowspan', '2')
			:attr('colspan', (hideColspanModifier + 5) * round - 1)
			:css('text-align', 'center')
			:css('border-color', 'black')
			:css('border-style', 'solid')
			:css('border-width', '0 2px 0 0')
			:wikitext(args[name])
			:newline()
		group = group + 1
	end
end

function renderTree(tbl, compact)
	-- create 3 or 1 rows for every team
	local count = math.pow(2, rounds) * (compact and 1 or 3)
	local rows = {}
	for i = 1, count do
		rows[i] = addTableRow(tbl)
	end
	if not compact then
		-- fill rows with groups
		for r = 1, rounds - 1 do
			renderGroups(rows, count, r)
		end
	end
	-- fill rows with bracket
	for r = 1, rounds do
		if compact then
			renderCompactRound(rows, count, r)
		else
			renderRound(rows, count, r)
		end
	end
end

function renderHeading(tbl, compact)
	local titleRow = addTableRow(tbl)
	local widthRow = addTableRow(tbl)
	for r = 1, rounds do
		addBlank(titleRow)
		addBlank(widthRow, r > 1 and '5px' or nil)
		titleRow:tag('td')
			:attr('colspan', hideColspanModifier + 3)
			:css('text-align', 'center')
			:css('border', '1px solid #aaa')
			:css('background-color', '#f2f2f2')
			:wikitext(getRoundName(r))
			:newline()
		local seedCell
		if not hideSeeds then
			seedCell = addBlank(widthRow, getWidth(SEED_NAME, '25px'))
		end
		local teamCell = addBlank(widthRow, getWidth(TEAM_NAME, '150px'))
        local scoreCell
		if not hideScores then
            scoreCell = addBlank(widthRow, getWidth(SCORE_NAME, '25px'))
		end
		addBlank(titleRow)
		addBlank(widthRow, r < rounds and '5px' or nil)

		if compact then
			teamCell:css('height', '7px')
		else
			if seedCell then
				seedCell:wikitext('&nbsp;')
			end
			teamCell:wikitext('&nbsp;')
			if scoreCell then
                scoreCell:wikitext('&nbsp;')
			end
		end
	end
end

function p.tournoi(frame)
	local getArgs = require('Module:Arguments').getArgs
	args = getArgs(frame, {trim = false, removeBlanks = false})

	-- exit early if this section is not to be transcluded
	if args['section'] and args['transcludesection'] 
		and args['section'] ~= args['transcludesection'] then return ''
	end

	rounds = tonumber(args[ROUNDS_NAME]) or 2
	local teams = math.pow(2, rounds)
	padding = '%0' .. (teams < 10 and 1 or 2) .. 'd'
	local compact = (args[COMPACT_NAME] and (args[COMPACT_NAME] == YES_VALUE or args[COMPACT_NAME] == Y_VALUE))
	local autoSeeds = (args[AUTOSEEDS_NAME] and (args[AUTOSEEDS_NAME] == YES_VALUE or args[AUTOSEEDS_NAME] == Y_VALUE))
	hideSeeds = (args[SEEDS_NAME] and (args[SEEDS_NAME] == NO_VALUE or args[SEEDS_NAME] == N_VALUE))
	showSeeds = (args[SEEDS_NAME] and (args[SEEDS_NAME] == YES_VALUE or args[SEEDS_NAME] == Y_VALUE))
	hideScores = (args[SCORES_NAME] and (args[SCORES_NAME] == NO_VALUE or args[SCORES_NAME] == N_VALUE))
    
    if hideSeeds then
        hideColspanModifier = hideColspanModifier - 1
    end
    
    if hideScores then
        hideColspanModifier = hideColspanModifier - 1
    end

	if autoSeeds then
		-- set default seeds for round 1
		local seeds = getSeeds()
		for i = 1, table.getn(seeds) do
			local argname = getTeamArgName(1, SEED_NAME, i)
			if not args[argname] then
				args[argname] = seeds[i]
			end
		end
	end

	local tbl = mw.html.create('table')
		:css('border-style', 'none')
		:css('font-size', '90%')
		:css('margin', '1em 2em 1em 1em')
		:css('border-collapse', 'separate')
		:css('border-spacing', '0')

	if (args['nowrap'] and (args['nowrap'] == YES_VALUE or args['nowrap'] == Y_VALUE)) then
		tbl:css('white-space', 'nowrap')
	end

	if compact then
		tbl:css('font-size', '90%'):attr('cellpadding', '0')
	end

	renderHeading(tbl, compact)
	renderTree(tbl, compact)
	return tostring(tbl) .. tracking
end

return p