Modulo:TeamBracket

Questo è un modulo scritto in Lua. Le istruzioni che seguono sono contenute nella sottopagina Modulo:TeamBracket/man (modifica · cronologia)
Sandbox: Modulo:TeamBracket/sandbox (modifica · cronologia) · Sottopagine: lista · Test: Modulo:TeamBracket/test (modifica · cronologia · Esegui)
Questo modulo serve a realizzare dei tabelloni per i tornei sportivi.
{{#invoke:TeamBracket|teamBracket
|compact = yes/no [default: yes]
|rounds =
|maxround = deve essere inferiore a rounds
|headings = valorizzare con no
per non farli apparire
|RD-shade = colore di sfondo dell'intestazione del round (se diverso da quello di default)
|RDn-RDn+1-path = valorizzare con 0
per non far apparire i collegamenti fra i due round selezionati
|byes = valorizzare con il numero di turni in cui un incontro potrebbe non verificarsi (bye, walkover, ecc.)
|seeds = valorizzare con yes
per farli apparire in ogni turno
|autoseeds = yes/no [default: no]
|sets = alternativo a legs
(se variano da turno a turno, separare con /)
|legs = alternativo a sets
(se variano da turno a turno, separare con /)
|hideomittedscores = yes/no [default: no]
|aggregate = valorizzare con yes
, sets
o legs
|boldwinner = yes/no [default: no]
|seed-width = [default: 28px]
|team-width = [default: 170px]
|score-width = [default: 28px]
|RDn = intestazione del round (se diverso dal nome di default)
|RD-finalina = intestazione della finale per il 3º posto (se diverso dal nome di default)
|RDn-groupm =
|RDn-seedm = seed della squadra o del partecipante
|RDn-teamm = squadra o partecipante
|RDn-scorem = punteggio della squadra o del partecipante
|RDn-scorem-s = punteggio della squadra o del partecipante (se l'incontro prevede più di una gara o di un set)
|RDn-shadem-s = colore di sfondo del risultato (se diverso da quello di default)
|RDn-scorem-agg = sovrascrive il valore automatico
}}
--
-- This module will implement {{TeamBracket}}
--
require('Module:No globals')
local getArgs = require('Module:Arguments').getArgs
local p = {}
local yes_replies = { 'yes', 'y', 's', 'si', 'sì'}
local no_replies = { 'n', 'no'}
local debug = false
local style = {
table = "border-style:none;font-size:90%;margin:1em 2em 1em 1em;border-collapse:separate;border-spacing:0",
seed_cell = "background-color:#f2f2f2;border:1px solid #aaa;text-align:center;",
team_cell = "background-color:#f2f2f2;border:1px solid #aaa;text-align:left;", --padding:0 2px
score_cell ="background-color:#f9f9f9;border:1px solid #aaa;text-align:center;",
header_third_place = "text-align:center;border:1px solid #aaa;background-color:#f2f2f2",
header_cell = "text-align:center;border:1px solid #aaa;background-color:#f2f2f2",
path_bottom = "border:0 solid black;border-bottom-width:1px;",
path_rigth_top = "border:0 solid black;border-top-width:1px;border-right-width:2px;",
path_right_bottom = "border:0 solid black;border-bottom-width:1px;border-right-width:2px;",
path_top = "border:0 solid black;border-top-width:2px;",
group = "text-align:center",
winner = 'font-weight:bold',
first_place = 'background-color:gold',
second_place = 'background-color:silver',
third_place = 'background-color:#C96',
row_height = "3px",
buffer_sx_width = "5px",
seed_width = "25px",
team_width = "150px",
score_width = "25px",
row_width = "10px"
}
if debug then
style.group = style.group .. ";background-color:green"
style.path_bottom = style.path_bottom .. ";background-color:#F08080"
style.path_top= style.path_top .. ";background-color:#F08080"
style.path_rigth_top= style.path_rigth_top .. ";background-color:#F08080"
style.path_right_bottom= style.path_right_bottom .. ";background-color:#F08080"
end
--[[ ===============================================================================
Ritorna true se needle è lista haystack, altrimenti ritorna false
===============================================================================]]
local function in_array( needle, haystack )
if needle == nil then return false; end
for n,v in ipairs( haystack ) do
if v == needle then return true; end
end
return false;
end
--[[ ===============================================================================
Oggetto per generare un TeamBracket
- args: array dei parametri passati al modulo
===============================================================================]]
local TeamBracket = {}
function TeamBracket:new(args)
local self = {}
setmetatable(self, { __index = TeamBracket,
__tostring = function(t) return self:__tostring() end })
self.args = args
self.rounds = tonumber(self.args['rounds']) or 2
self.teams = math.pow(2, self.rounds)
self.compact = in_array(self.args['compact'], yes_replies)
self.hideSeeds = in_array(self.args['seeds'], no_replies)
self.showSeeds = in_array(self.args['seeds'], yes_replies)
local padding = '%0' .. ((self.teams < 10) and 1 or 2) .. 'd'
self.argname_pattern = 'RD%d-%s' .. padding
self.scorename_pattern = 'RD%d-score' .. padding
local autobold_par = self.args['bold_winner'] or ''
self.bold_winner = not in_array(autobold_par, no_replies)
self.low_winner = self.bold_winner and (autobold_par == 'low')
self.medals = self.bold_winner and not in_array(self.args['medals'], no_replies)
-- load number of scores for each round
self.scores = {}
local scores_raw = self.args.scores or '1'
local max_scores = 1
for i,score_value in ipairs(mw.text.split(scores_raw, ',')) do
self.scores[i] = tonumber(score_value) or 1
if self.scores[i] > max_scores then max_scores = self.scores[i] end
end
local last_scores = self.scores[#self.scores]
for i =#self.scores+1, self.rounds do
self.scores[i] = last_scores
end
if max_scores > 1 then
self.scorename_pattern = self.scorename_pattern .. "-%d"
end
-- custom style
style.winner = self.args.style_medal or style.winner
style.first_place = self.args.style_first_place or style.first_place
style.second_place = self.args.style_second_place or style.second_place
style.third_place = self.args.style_third_place or style.third_place
style.header_cell = self.args.style_header or style.header_cell
style.header_third_place = self.args.style_header_third_place or style.header_third_place
style.seed_width = self:getWidth('seed', style.seed_width)
style.team_width = self:getWidth('team', style.team_width)
style.score_width = self:getWidth('score', style.score_width)
-- set default seeds for round 1
if not(self.hideSeeds) then
local seeds = self:getSeeds()
local argname
for i = 1, table.getn(seeds) do
argname = self:getTeamArgName(1, 'seed', i)
if not args[argname] then
args[argname] = seeds[i]
end
end
end
self.tbl = mw.html.create('table'):cssText(style.table)
if in_array(args['nowrap'], yes_replies) then
self.tbl:css('white-space', 'nowrap')
end
self.rows = { }
if self.compact then self.tbl:attr('cellpadding', '0') end
self.last_element = {}
self.rows = {}
self.current_col = 0
self.dump = {}
self.not_draw_top = false
self:renderHeading()
self:renderTree()
return self
end
--[[ ===============================================================================
-- collassa l'oggetto in una stringa contenente il wikicodice per generare l'albero
===============================================================================]]
function TeamBracket:__tostring()
return tostring(self.tbl)
end
--[[ ===============================================================================
Genera i valori di default dei seeds
===============================================================================]]
function TeamBracket:getSeeds()
local seeds = {1, 2}
local count = 2
local before = false
for r = 2, self.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
--[[ ===============================================================================
Ritorna il valore per i parameri di larghezza (seed-widt, score-width, team-width)
===============================================================================]]
function TeamBracket:getWidth(param, default)
local arg = self.args[param .. '-width']
if not arg then return default end
return arg .. ((tonumber(arg) and 'px') or '')
end
--[[ ===============================================================================
Se fino alla riga row, colonna col-1 non sono ancora stati aggiunti elementi aggiunte
una cella vuota dall'ultima colonna usta dalla riga.
Aggiorna quindi il conteggio delle colonne usate alla posizione colonna +width
Il valore di default di width è 0
=============================================================================== --]]
function TeamBracket:addGap(row, col, width, debug_info)
width = width or 1
if self.last_element[row] + 1 < col then
local gap = tostring(col - self.last_element[row] - 1)
local cell=self.rows[row]:tag('td'):css('background-color', (debug and '#F5F5DC') or ''):attr('colspan', gap)
if debug then
cell:wikitext(string.format('<div style="text-align:center;" title="row=%d, colonna:%d, last_col=%d, width:%d, gap:%s, last_col_set:%d, round:%d, deb:%s">info</div>',
row, col, self.last_element[row], width, gap, col+width, self.current_round, debug_info or ''))
end
end
self.last_element[row] = col + width - 1
end
--[[ ===============================================================================
Ritorna il nome completo del parametro 'argname' della squadra 'team' nel round 'round'
Per esempio getTeamArgName(1, 'seed', 3) ritorna 'RD1-seed3' o ''RD1-seed03' se ci sono
10 squadre o più
===============================================================================]]
function TeamBracket:getTeamArgName(round, argname, team)
return string.format(self.argname_pattern, round, argname, team)
end
--[[ ===============================================================================
Ritorna il valore del parametro 'argname' della squadra 'team' nel round 'round',
Per esempio getTeamArg(1, 'seed', 3) ritorna il valore del parametro 'RD1-seed3'
===============================================================================]]
function TeamBracket:getTeamArg(round, argname, team)
return self.args[self:getTeamArgName(round, argname, team)]
end
--[[ ===============================================================================
Ritorna il valore dello score dato il round, la posizione nel round (team) e il
progressivo dell'incontro.
===============================================================================]]
function TeamBracket:getScoreArg(round, team, score)
return self.args[string.format(self.scorename_pattern, round, team, score)]
end
--[[ ===============================================================================
Ritorna il nome del round 'round' o il suo nome di default se non è precisato.
===============================================================================]]
function TeamBracket:getRoundName(round)
local name = self.args['RD' .. round]
if name then return name end
local round_names = {"Finale", "Semifinali", "Quarti", "Ottavi"}
local roundFromLast = self.rounds - round + 1
if roundFromLast < 5 then
return round_names[roundFromLast]
else
return tostring(round) .. "° turno"
end
end
--[[ ===============================================================================
Aggiunge le righe grafiche di congiunzione delle celle per la riga index del
turno round. top e left indicano se deve essere generata rispettivamente la riga
sinistra o quella superiore
- row1 = prima riga del match superiore da cui esce il path
- row2 = prima riga del match inferiore da cui esce il path
- row_gap = ampiezza del path
- col = colonna in cui inizia il path
row1 nullo significa che il path parte solo da row2 e viceversa. Se entrambi sono
nulli la funzione non disegna niente.
===============================================================================]]
function TeamBracket:addPath(row1, row2, row_gap, next_match)
local col = self.current_col + self.current_width
local half_gap = row_gap / 2
if not (row1 or row2) then return end
self.render_match[self.current_round+1][next_match] = true
-- first half
if row1 then
self.rows[row1+1]:tag('td'):cssText(style.path_bottom)
for r = row1+1, row1+3 do self.last_element[r] = col end
for r = row1+4, row1+4+half_gap-1 do self:addGap(r, col, 1, 'ap1') end
self.rows[row1+2]:tag('td')
:attr('rowspan', half_gap + 2)
:cssText(style.path_rigth_top)
end
--second half
if row2 then
self.rows[row2+2]:tag('td'):cssText(style.path_top)
for r = row2, row2+2 do self.last_element[r] = col end
for r = row2-half_gap, row2-1 do self:addGap(r, col, 1, 'ap2') end
self.rows[row2-half_gap]:tag('td')
:attr('rowspan', half_gap+2)
:cssText(style.path_right_bottom)
end
local middle_row
middle_row = (row1 and (row1 + 2 + half_gap)) or (row2 - half_gap -2)
for r = middle_row-1, middle_row+2 do self:addGap(r, col+1, 1, 'ap3') end
--self:addGap(middle_row, col+1, 1, 'ap3')
--self:addGap(middle_row+1, col+1, 1, 'ap3')
self.rows[middle_row]:tag('td'):cssText(style.path_bottom):attr('rowspan', '2')
self.rows[middle_row+2]:tag('td'):cssText(style.path_top):attr('rowspan', '2')
end
--[[ ------------------------------------------------------------
Compare score values and return and return the winner of the match
(team with majority of win, 0 mean parity, 1 fist team win, 2
second team win) and and array with winner for single scores (for
each single score 0 parity, 1 first team win, 2 second team win.
------------------------------------------------------------ --]]
function TeamBracket:getWinner(first_team_number)
local victories1 = 0
local victories2 = 0
local score_results = {}
for i = 1, self.scores[self.current_round] do
local score1 = self:getScore(self.current_round, first_team_number, i)
local score2 = self:getScore(self.current_round, first_team_number+1, i)
if score1 and score2 then
if score1 ~= score2 then
local first_winner = score1 > score2
if self.low_winner then first_winner = not first_winner end
if first_winner then
victories1 = victories1 + 1
score_results[i] = 1
else
victories2 = victories2 + 1
score_results[i] = 2
end
end
end
score_results[i] = score_results[i] or 0
end
if victories1 == victories2 then return 0, score_results end
if victories1 > victories2 then return 1, score_results end
return 2, score_results
end
--[[ ===============================================================================
Disegna la cella per il team.
-- team_name: il nome del team
-- team_numer: il numero del team nella sequenza del round
-- row: la riga della prima cella in cui disegnare il team
-- top: true se è il primo team del match
-- medal: per assegnare la medaglia (1: oro, 2: argento, 0:pari)
-- score_results: risultato degli scores individuali (1: vinto dal primo team, 2 vinto dal secondo, 3 pari)
===============================================================================]]
function TeamBracket:renderTeam(team_name, team_number, row, show_seed, top, medal, score_results, is_bye)
self:addGap(row, self.current_col, self.current_width, 'rt')
self:addGap(row+1, self.current_col, self.current_width, 'rt')
-- seed cell
local seedArg = self:getTeamArg(self.current_round, 'seed', team_number)
if show_seed and (not self.hideSeeds) then
local seedCell = self.rows[row]:tag('td'):attr('rowspan', '2'):cssText(style.seed_cell):wikitext(seedArg):newline()
if self.not_draw_top then seedCell:css('border-top-width', '0' ) end
if self.bold_winner and (medal == 1 or medal == 3) then
seedCell:cssText(style.winner)
end
end
-- team cell
local teamCell = self.rows[row]:tag('td'):attr('rowspan', '2'):cssText(style.team_cell):wikitext(team_name):newline()
--local teamCell = self.rows[row]:tag('td'):attr('rowspan', '2'):cssText(style.team_cell):wikitext(tostring(seedArg)):newline()
if not show_seed and (not self.hideSeeds) then
teamCell:attr('colspan', '2')
end
if self.not_draw_top then
teamCell:css('border-top-width', '0' )
end
if self.bold_winner and (medal == 1 or medal == 3) then
teamCell:cssText(style.winner)
end
if self.current_round == self.rounds and medal ~= 0 and self.medals then
if medal == 1 then
teamCell:cssText(style.first_place)
elseif medal == 2 then
teamCell:cssText(style.second_place)
elseif medal == 3 then
teamCell:cssText(style.third_place)
end
end
-- scores cells
for i = 1, self.scores[self.current_round] do
local scoreCell = self.rows[row]
:tag('td')
:attr('rowspan', '2')
:cssText(style.score_cell)
:wikitext(self:getScoreArg(self.current_round, team_number, i))
:newline()
if self.not_draw_top then scoreCell:css('border-top-width', '0') end
local pos = (top and 1) or 2
if score_results[i] == pos then scoreCell:cssText(style.winner) end
end
self.not_draw_top = (self.current_round == 1) and (not self.not_draw_top)
end
function TeamBracket:getScore(round, team_number, i)
local score = string.gsub(self:getScoreArg(round, team_number, i) or '', '%D', '')
return tonumber(score)
end
--[[ ===============================================================================
Gestisce un match
-- match_number: il numero del match contando dall'alto (1, 2, 3, ...)
-- row_base: la prima riga della tabella in cui disegnare il batch
===============================================================================]]
function TeamBracket:renderMatch(match_number, row_base)
-- ottiene i nomi dei team del match, verifica che non siano bye e setta self.not_draw_top
-- per registrare se si deve disegnare o meno la riga superiore del match
-- ritorna i nomi dei team e se è un bye (true) o no (false)
local function getTeams(team_number1, team_number2)
local team_name1 = self:getTeamArg(self.current_round, 'team', team_number1)
local team_name2 = self:getTeamArg(self.current_round, 'team', team_number2)
if team_name1 == 'bye' or team_name2 == 'bye' or not (team_name1 and team_name2) then
self.not_draw_top = false
return '', '', true
end
return team_name1, team_name2, false
end
local team_number2 = match_number * 2
local team_number1 = team_number2 - 1
local team_name1, team_name2, is_bye = getTeams(team_number1, team_number2)
if is_bye then
if self.render_match[self.current_round][match_number] then
for r = row_base, row_base+3 do
self:addGap(r, self.current_col, self.current_width, 'rm')
end
local cell=self.rows[row_base]:tag('td')
:attr('rowspan', '2')
:attr('colspan', self.current_width)
:cssText(style.path_bottom)
if debug then cell:css('background-color', '#ADD8E6') end
cell = self.rows[row_base+2]:tag('td')
:attr('rowspan', '2')
:attr('colspan', self.current_width)
:cssText(style.path_top)
if debug then cell:css('background-color', '#ADD8E6') end
return row_base
end
return nil
end
if row_base < self.min_row_used then self.min_row_used = row_base end
if row_base + 3 > self.max_row_used then self.max_row_used = row_base + 3 end
local showSeed = false
local winner, score_results = 0, {}
local seedArg1 = self:getTeamArg(self.current_round, 'seed', team_number1)
local seedArg2 = self:getTeamArg(self.current_round, 'seed', team_number2)
local showSeed = self.showSeeds
or (seedArg1 and seedArg1 ~= '-')
or (seedArg2 and seedArg2 ~= '-')
if self.bold_winner then
winner, score_results = self:getWinner(team_number1)
end
local medal2 = (winner == 1 and 2) or (winner == 2 and 1) or 0
self:renderTeam(team_name1, team_number1, row_base, showSeed, true, winner, score_results, is_bye)
self:renderTeam(team_name2, team_number2, row_base+2, showSeed, false, medal2, score_results, is_bye)
return row_base
end
--[[ ===============================================================================
Disegna la finale per il terzo posto
--row: riga in cui iniziare a disegnare la finale
===============================================================================]]
function TeamBracket:render_final_3(row)
local team_name1 = self:getTeamArg(self.rounds, 'team', 3)
local team_name2 = self:getTeamArg(self.rounds, 'team', 4)
if team_name1 == 'bye' or team_name2 == 'bye' then
return nil
end
local seedArg1 = self:getTeamArg(self.rounds, 'seed', 3)
local seedArg2 = self:getTeamArg(self.rounds, 'seed', 4)
local showSeed = self.showSeeds
or (seedArg1 and seedArg1 == '-')
or (seedArg2 and seedArg2 == '-')
local winner, score_results = 0, {}
if self.bold_winner then
winner, score_results = self:getWinner(self.rounds, 3)
end
local seedArg1 = self:getTeamArg(self.rounds, 'seed', 3)
local seedArg2 = self:getTeamArg(self.rounds, 'seed', 4)
self:addGap(row, self.current_col, self.current_width, 'f3')
if row+4 > self.max_row_used then self.max_row_used = row + 4 end
self.rows[row]:tag('td')
:cssText(style.header_third_place)
:attr('colspan', self.current_width)
:wikitext("Finale 3° posto")
:newline()
local medal1 = (winner == 1 and 3) or 0
local medal2 = (winner == 2 and 3) or 0
self:renderTeam(team_name1, 3, row+2, showSeed, true, medal1, score_results)
self:renderTeam(team_name2, 4, row+4, showSeed, false, medal2, score_results)
end
function TeamBracket:AddGroup(row, group_number)
local name = self.args[string.format('RD%d-group%d', self.current_round, group_number)]
if name then
local span = self.current_col + self.current_width - 2
self:addGap(row, self.current_col, self.current_col + span, 'g')
self:addGap(row+1, self.current_col, self.current_col + span, 'g')
self.rows[row]:tag('td')
:attr('rowspan', '2')
:attr('colspan', span)
:cssText(style.group)
:wikitext(name)
:newline()
end
end
function TeamBracket:renderTree()
local function normal_gap(row_count, match_number)
return (row_count + 2 - match_number * 4) / match_number
end
local function compact_gap(row_count, match_number)
return (row_count - 4 * match_number) / match_number
end
-- create 3 or 1 rows for every team
local row_count = self.teams * 2 + (self.compact and 0 or (self.teams - 2))
self.min_row_used = row_count
self.max_row_used = 1
local gap_function = (self.compact and compact_gap) or normal_gap
for i = 1, row_count do
self.rows[i] = mw.html.create('tr')
self.rows[i]:tag('td'):css('height', style.row_height):wikitext(debug and i or '')
self.last_element[i] = 1
end
self.current_col = 2
self.render_match = {}
self.render_match[1] = {}
for round =1, self.rounds do
self.current_round = round
self.render_match[round+1] = {}
self.current_width = (self.hideSeeds and 1 or 2) + self.scores[round]
local match_number = math.pow(2, self.rounds - round)
local gap = gap_function(row_count, match_number)
local row_base = gap / 2 + (self.compact and 1 or 0)
local group_number = 1
for n = 1, match_number, 2 do
local match1 = self:renderMatch(n, row_base)
if round < self.rounds then
local match2 = self:renderMatch(n+1, row_base + gap + 4)
if not self.compact and round % 4 == 1 then
self:AddGroup(row_base+4, group_number)
group_number = group_number + 1
end
self:addPath(match1, match2, gap, (n+1)/2)
row_base = row_base + 2 * gap + 8
end
end
self.current_col = self.current_col + self.current_width + 2
end
local third_place = self:getTeamArg(self.rounds, 'team', 3)
--if true then return third_place end
if third_place then -- semifinale 3° e 4° posto
self.current_col = self.current_col - self.current_width - 2
local offset = gap_function(row_count, 1) / 2 + (self.compact and 1 or 0) + 6
if offset+5 > row_count then
for i=row_count+1, offset+5 do
self.rows[i] = mw.html.create('tr')
self.rows[i]:tag('td'):css('height', style.row_height):wikitext(debug and i or '')
self.last_element[i] = 1
end
end
self:render_final_3(offset)
end
for row = self.min_row_used, self.max_row_used do
self.tbl:node(self.rows[row])
end
end
--[[ ===============================================================================
Disegna le righe di testata del template
===============================================================================]]
function TeamBracket:renderHeading()
local titleRow = self.tbl:tag('tr')
local widthRow = self.tbl:tag('tr')
local blank_text = self.compact and '' or ' '
titleRow:tag('td')
local row_count = 1
widthRow:tag('td')
:css('width', style.buffer_sx_width)
:css('height', '5px')
:wikitext(debug and row_count or '')
row_count = row_count + 1
for round = 1, self.rounds do
local colspan = tostring((self.hideSeeds and 1 or 2) + self.scores[round])
local teamCell = titleRow:tag('td')
:cssText(style.header_cell)
:attr('colspan', colspan)
:wikitext(self:getRoundName(round))
:newline()
if not self.hideSeeds then
widthRow:tag('td'):css('width', style.seed_width):wikitext(debug and row_count or blank_text)
row_count = row_count + 1
end
teamCell = widthRow:tag('td'):css('width', style.team_width):wikitext(debug and row_count or blank_text)
row_count = row_count + 1
for i = 1, self.scores[round] do
widthRow:tag('td'):css('width', style.score_width):wikitext(debug and row_count or blank_text)
row_count = row_count + 1
end
if round < self.rounds then
titleRow:tag('td'):attr('colspan', '2')
widthRow:tag('td'):css('width', style.row_width):wikitext(debug and row_count or blank_text)
row_count = row_count + 1
widthRow:tag('td'):css('width', style.row_width):wikitext(debug and row_count or blank_text)
row_count = row_count + 1
end
end
end
-- =================================================================
-- Funzione di interfaccia con i template
-- =================================================================
function p.teamBracket(frame)
local args = getArgs(frame, {
-- se l'argomento è un seed lo ritorna così com'è anche se blank, tutti gli altri argomenti sono puliti
valueFunc = function (key, value)
if not value then return nil end
if key:find("^RD%d%d?-seed") then return value end
value = mw.text.trim(value)
if value == '' then return nil end
return value
end
}
)
if args['debug'] then
debug = in_array(args['debug'], yes_replies) or debug
end
local team_bracket = TeamBracket:new(args)
return tostring(team_bracket)
end
return p