Module:Sandbox/Pbrks
Appearance
From Wikipedia, the free encyclopedia
You might want to create a documentation page for this Scribunto module.
Editors can experiment in this module's sandbox (create | mirror) and testcases (create) pages.
Add categories to the /doc subpage. Subpages of this module.
Editors can experiment in this module's sandbox (create | mirror) and testcases (create) pages.
Add categories to the /doc subpage. Subpages of this module.
--- --- Build Bracket (refactor skeleton) --- Goal: separate data phases, minimize mutation, clarify responsibilities. --- This is a FIRST PASS skeleton wired to p.main(frame). --- We'll port functionality in phases while keeping the public API. --- localp={} -- ===================== -- Localized stdlib -- ===================== localstr_gsub,str_match,str_find=string.gsub,string.match,string.find localt_insert,t_sort,t_concat=table.insert,table.sort,table.concat localm_max,m_min,m_ceil=math.max,math.min,math.ceil localtonumber,tostring=tonumber,tostring localpairs,ipairs,type=pairs,ipairs,type localmw_html_create=mwandmw.htmlandmw.html.create -- ===================== -- Small utilities (pure) -- ===================== localfunctionisempty(s)returns==nilors==''end localfunctionnotempty(s)returns~=nilands~=''end localfunctionyesish(v) v=(tostring(vor''):lower()) returnv=='y'orv=='yes'orv=='true'orv=='1' end localfunctionnoish(v) v=(tostring(vor''):lower()) returnv=='n'orv=='no'orv=='false'orv=='0' end localfunctiontoChar(num)returnstring.char(string.byte('a')+num-1)end -- ===================== -- Types (documentation only) -- ===================== -- Config = { -- c:number, r:number|nil, minc:number, -- autocol:boolean, autolegs:boolean, -- seeds:boolean, forceseeds:boolean, -- boldwinner_mode:'off'|'high'|'low', boldwinner_aggonly:boolean, -- aggregate:boolean, aggregate_mode:'off'|'manual'|'sets'|'score', -- colspacing:number, height:number, nowrap:boolean, -- paramstyle:'indexed'|'numbered', -- } -- -- Grid[j][i] = Cell where Cell has a stable shape: -- { ctype='header'|'team'|'text'|'line'|'group'|'blank', -- index:number?, altindex:number?, headerindex:number?, -- position:'top'|nil, -- team:string?, seed:string?, -- legs:number?, -- score={ [1]=string?, [2]=string?, agg=string?, weight=table }?, -- weight:'bold'|'normal'|nil, -- pheader:string?, header:string?, shade:string?, shade_is_rd:boolean?, -- group:string?, colspan:number?, -- borderWidth:{number,number,number,number}? , -- border:'top'|'bottom'|'both'|nil, -- } -- -- Derived = { -- rlegs = { [j]=number }, -- maxlegs = { [j]=number }, -- teamsPerMatch = { [j]=number }, -- headercount = { [j]=number }, -- hide = { [j] = { [headerindex]=true } }, -- byes = { [j] = { [headerindex]=true } }, -- matchgroup = { [j] = { [i]=groupId } }, -- pathCell, crossCell, skipPath, hascross, shift, -- } -- ===================== -- Phase 0. Arg access -- ===================== localfunctionmakeArgReaders(frame) localfargs=frame.argsor{} localpargs=(frame.getParentandframe:getParent().args)or{} localfunctionbargs(k)returnpargs[k]orfargs[k]end returnfargs,pargs,bargs end -- ===================== -- Phase 1. Parse args → Config (pure) -- ===================== localfunctionparseConfig(fargs,pargs,bargs) localcfg={ r=tonumber(fargs.rows)ornil, c=tonumber(fargs.rounds)or1, minc=tonumber(pargs.minround)or1, autocol=yesish(fargs.autocol), colspacing=tonumber(fargs['col-spacing'])or5, height=bargs('height')or0, -- seeds forceseeds=yesish(pargs.seeds), seeds=notnoish(pargs.seeds), -- boldwinner boldwinner_mode='off', boldwinner_aggonly=false, -- aggregate aggregate=false, aggregate_mode='off', autolegs=yesish(pargs.autolegs), paramstyle=(bargs('paramstyle')=='numbered')and'numbered'or'indexed', nowrap=notnoish(pargs.nowrap), deprecation_category='Pages using Build Bracket with deprecated parameters', } -- rounds override via maxround(s) localmaxc=tonumber(pargs.maxrounds)ortonumber(pargs.maxround) ifmaxcthencfg.c=maxcend -- boldwinner legacy switch mapping do localbw=(bargs('boldwinner')or''):lower() ifbw=='low'thencfg.boldwinner_mode='low' elseifbw=='high'oryesish(bw)thencfg.boldwinner_mode='high' elseifbw=='aggregate'orbw=='agg'orbw=='aggregate-high'orbw=='agg-high'then cfg.boldwinner_mode='high';cfg.boldwinner_aggonly=true elseifbw=='aggregate-low'orbw=='agg-low'then cfg.boldwinner_mode='low';cfg.boldwinner_aggonly=true end ifyesish(bargs('boldwinner-aggregate-only'))thencfg.boldwinner_aggonly=trueend end -- aggregate mode do localaval=(bargs('aggregate')or''):lower() ifaval=='sets'oraval=='legs'then cfg.aggregate_mode='sets';cfg.aggregate=true elseifaval=='score'then cfg.aggregate_mode='score';cfg.aggregate=true elseifyesish(aval)then cfg.aggregate_mode='manual';cfg.aggregate=true else cfg.aggregate_mode='off';cfg.aggregate=false end end returncfg end -- ===================== -- Phase 2. Build initial grid (headers/teams/lines/text) → Grid -- NOTE: This mirrors the original getCells(), but returns a new grid and a -- small Derived stub. We keep arg names compatible (colX-headers, etc.). -- ===================== localfunctionsplitCSVInts(s) ifisempty(s)thenreturn{}end localout={} s=s:gsub('%s+','') fornins:gmatch('[^,]+')do localv=tonumber(n) ifvthent_insert(out,v)end end returnout end localfunctionbuildInitialGrid(cfg,fargs,pargs,bargs) localgrid={} localderived={ teamsPerMatch={}, shift={}, maxtpm=1, } localDEFAULT_TPM=2 -- detect explicit headers localhasHeaders=false forj=cfg.minc,cfg.cdo ifnotisempty(fargs['col'..j..'-headers'])thenhasHeaders=trueend end -- pass 1: get tpm per round and track maxtpm forj=cfg.minc,cfg.cdo localtpm=tonumber(fargs['RD'..j..'-teams-per-match']) ortonumber(fargs['col'..j..'-teams-per-match']) ortonumber(fargs['teams-per-match']) orDEFAULT_TPM derived.teamsPerMatch[j]=tpm iftpm>(derived.maxtpmor1)thenderived.maxtpm=tpmend end -- pass 2: populate skeleton cells localmaxrow=1 forj=cfg.minc,cfg.cdo grid[j]={} derived.shift[j]=tonumber(bargs('RD'..j..'-shift'))ortonumber(bargs('shift'))or0 localheaders=splitCSVInts(fargs['col'..j..'-headers']or'') localmatches=splitCSVInts(fargs['col'..j..'-matches']or'') locallines=splitCSVInts(fargs['col'..j..'-lines']or'') localtexts=splitCSVInts(fargs['col'..j..'-text']or'') -- Auto-insert a header if no explicit headers passed anywhere and noheaders flag not set. ifnothasHeadersand(fargs['noheaders']~='y'andfargs['noheaders']~='yes')then t_insert(headers,1) end localfunctionmark(i,cell) grid[j][i]=cell end localfunctionpopulateHeader(pos) locali=2*(pos+(derived.shift[j]or0))-1 mark(i,{ctype='header',index=#headers,position='top'}) mark(i+1,{ctype='blank'}) maxrow=m_max(maxrow,i+1) end localfunctionpopulateTeamGroup(n) localstart=2*(n+(derived.shift[j]or0))-1 -- ensure a text row before first team block ifgrid[j][start-1]==nilandgrid[j][start-2]==nilthen mark(start-2,{ctype='text',index=n}) mark(start-1,{ctype='blank'}) end -- top team mark(start,{ctype='team',index=derived.teamsPerMatch[j]*n-(derived.teamsPerMatch[j]-1),position='top'}) mark(start+1,{ctype='blank'}) -- remaining teams in match form=2,derived.teamsPerMatch[j]do localidx=derived.teamsPerMatch[j]*n-(derived.teamsPerMatch[j]-m) localrow=start+2*(m-1) mark(row,{ctype='team',index=idx}) mark(row+1,{ctype='blank'}) end maxrow=m_max(maxrow,start+2*derived.teamsPerMatch[j]-1) end localfunctionpopulateLine(pos) locali=2*(pos+(derived.shift[j]or0))-1 mark(i,{ctype='line',border='bottom'}) mark(i+1,{ctype='blank'}) mark(i+2,{ctype='line',border='top'}) mark(i+3,{ctype='blank'}) maxrow=m_max(maxrow,i+3) end localfunctionpopulateText(pos,textindex) locali=2*(pos+(derived.shift[j]or0))-1 mark(i,{ctype='text',index=textindex}) mark(i+1,{ctype='blank'}) maxrow=m_max(maxrow,i+1) end -- Sorted application keeps row math simple table.sort(headers);table.sort(matches);table.sort(lines);table.sort(texts) localtextindexCounter=0 for_,posinipairs(headers)dopopulateHeader(pos)end forn,_inipairs(matches)dopopulateTeamGroup(n)end for_,posinipairs(lines)dopopulateLine(pos)end fork,_inipairs(texts)dotextindexCounter=textindexCounter+1;populateText(texts[k],#matches+textindexCounter)end end -- finalize row count ifcfg.r==nilthen localmr=1 forj=cfg.minc,cfg.cdo fori,_inpairs(grid[j])doifi>mrthenmr=iendend end cfg.r=mr end returngrid,derived end -- ===================== -- Phase 3+. Placeholders to port next -- ===================== localfunctioncomputeAltIndices(grid,cfg,fargs,pargs,bargs) -- TODO: port getAltIndices() but return: headercount[j], and set .altindex/.headerindex on cells (pure-ISH) localheadercount={} forj=cfg.minc,cfg.cdo headercount[j]=0 localteamindex,textindex,groupindex=1,1,1 localrow=grid[j] ifrowandrow[1]==nilthenheadercount[j]=headercount[j]+1end fori=1,cfg.rdo locale=rowandrow[i] ifethen ife.ctype=='header'thene.altindex=headercount[j];teamindex,textindex=1,1;headercount[j]=headercount[j]+1 elseife.ctype=='team'thene.altindex=teamindex;teamindex=teamindex+1 elseife.ctype=='text'thene.altindex=textindex;textindex=textindex+1 elseife.ctype=='group'thene.altindex=groupindex;groupindex=groupindex+1end e.headerindex=headercount[j] end end end returnheadercount end localfunctioncomputeHideAndByes(grid,cfg,bargs,headercount) -- Lua 5.1-safe (no goto). Port of getHide/getByes and a basic empty-round hide pass. localhide,byes={},{} forj=cfg.minc,cfg.cdo hide[j],byes[j]={},{} fork=1,(headercount[j]or0)do -- hide flags localh=bargs('RD'..j..toChar(k)..'-hide') hide[j][k]=(h=='y'orh=='yes')andtrueorfalse -- byes: global, RDj, RDja (last writer wins, matching original precedence) localv=false localglobalByes=bargs('byes') ifyesish(globalByes)thenv=true elseiftonumber(globalByes)andj<=tonumber(globalByes)thenv=trueend localrd=bargs('RD'..j..'-byes');ifyesish(rd)thenv=trueelseifnoish(rd)thenv=falseend localrda=bargs('RD'..j..toChar(k)..'-byes');ifyesish(rda)thenv=trueend byes[j][k]=v end end -- helper localfunctionisBlankEntry(j,i) localcol=grid[j];locale=colandcol[i] ifnotethenreturntrueend ife.ctype=='team'thenreturn(e.team==nilore.team=='')and(e.text==nilore.text=='')end ife.ctype=='text'thenreturn(e.text==nilore.text=='')end returnfalse end -- hide empty-header rounds (no goto; nest conditions) forj=cfg.minc,cfg.cdo fori=1,cfg.rdo locale=grid[j]andgrid[j][i] ifeande.ctype=='header'then localhidx=e.headerindex ifhidxthen localrow=i+1;localany=false whilerow<=cfg.rdo localrce=grid[j][row] ifrceandrce.ctype=='header'thenbreakend ifrceandrce.ctype~='blank'andnotisBlankEntry(j,row)thenany=true;breakend row=row+1 end ifnotanythenhide[j][hidx]=hide[j][hidx]ortrueend end end end end returnhide,byes end -- Assign parameters (seed/team/score/header/text/group/line text) localfunctionassignParams(grid,cfg,fargs,pargs,bargs,derived) -- TODO: port paramNames() logic; for now, minimal pass to keep structure. returngrid-- unchanged placeholder end -- Aggregates & bolding (pure) localfunctioncomputeAggregatesAndBold(grid,cfg,derived) -- TODO: port computeAggregate() and boldWinner() in a pure style. end -- Paths (pure data) and post-processing merges localfunctioncomputePaths(grid,cfg,fargs,pargs,bargs,derived) -- TODO: port getPaths() and connect logic but return pathCell/crossCell/skipPath/hascross; do not mutate grid here. derived.pathCell,derived.crossCell,derived.skipPath,derived.hascross={},{},{},{} returnderived end localfunctionupdateMaxLegs(grid,cfg,derived) -- TODO: port updateMaxLegs() against current grid entries derived.rlegs,derived.maxlegs=derived.rlegsor{},derived.maxlegsor{} forj=cfg.minc,cfg.cdo derived.rlegs[j]=derived.rlegs[j]or1 derived.maxlegs[j]=derived.maxlegs[j]orderived.rlegs[j] end end -- ===================== -- Rendering (impure, view-only, no surprises) -- ===================== localCOLORS={ cell_bg_light='var(--background-color-neutral-subtle,#f8f9fa)', cell_bg_dark='var(--background-color-neutral,#eaecf0)', text_color='var(--color-base,#202122)', path_line_color='gray', cell_border='var(--border-color-base,#a2a9b1)' } localfunctioncellBorder(b)return(b[1]..'px '..b[2]..'px '..b[3]..'px '..b[4]..'px')end localfunctionrenderTable(grid,cfg,derived) localframe=mw.getCurrentFrame() localdiv=mw_html_create('div'):css('overflow','auto') div:wikitext(frame:extensionTag('templatestyles','',{src='Module:Build bracket/styles.css'})) ifcfg.height~=0thendiv:css('height',cfg.height)end localtbl=mw_html_create('table'):addClass('brk') ifcfg.nowrapthentbl:addClass('brk-nw')end -- invisible header row for col widths (placeholder; widths will depend on derived.maxlegs) tbl:tag('tr'):css('visibility','collapse'):tag('td'):css('width','1px') -- TODO: set width cells using derived.maxlegs/aggregate/seeds just like original fori=1,cfg.rdo localrow=tbl:tag('tr') row:tag('td'):css('height','11px') forj=cfg.minc,cfg.cdo locale=grid[j]andgrid[j][i] localtd=row:tag('td') ifethen td:wikitext(e.ctypeor''):css('font-size','smaller'):css('color','#666') else td:wikitext('') end -- TODO: replace with real insertors once phases are fully ported end end div:wikitext(tostring(tbl)) returntostring(div) end -- ===================== -- Deprecation cats (kept simple for now) -- ===================== localfunctionemitDeprecationCats() localtitle=mw.title.getCurrentTitle() ifnottitleortitle.namespace~=0thenreturn''end return'' end -- ===================== -- MAIN -- ===================== functionp.main(frame) -- Styles once frame:extensionTag('templatestyles','',{src='Module:Build bracket/styles.css'}) -- Phase 0/1 localfargs,pargs,bargs=makeArgReaders(frame) localcfg=parseConfig(fargs,pargs,bargs) -- Phase 2 localgrid,derived=buildInitialGrid(cfg,fargs,pargs,bargs) -- Phase 3+ derived.headercount=computeAltIndices(grid,cfg,fargs,pargs,bargs) derived.hide,derived.byes=computeHideAndByes(grid,cfg,bargs,derived.headercount) grid=assignParams(grid,cfg,fargs,pargs,bargs,derived) computeAggregatesAndBold(grid,cfg,derived) computePaths(grid,cfg,fargs,pargs,bargs,derived) updateMaxLegs(grid,cfg,derived) -- Output localout=renderTable(grid,cfg,derived) returntostring(out)..emitDeprecationCats() end returnp