Jump to content

Module:Build bracket/StateChecks

Permanently protected module
From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Pbrks (talk | contribs) at 03:49, 13 August 2025. The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
local StateChecks = {}

-- Bound at runtime
local state, config, Helpers

-- Local aliases (filled in bind)
local isempty, notempty

function StateChecks.bind(_state, _config, _Helpers)
  state, config, Helpers = _state, _config, _Helpers
  isempty   = Helpers.isempty
  notempty  = Helpers.notempty
end

-- reads entries
function StateChecks.isBlankEntry(col, row)
  local colEntries = state.entries[col]
  if not colEntries then return true end
  local e = colEntries[row]
  if not e then return true end
  return isempty(e.team) and isempty(e.text)
end

-- reads entries, teamsPerMatch, forceseeds
function StateChecks.showSeeds(j, i)
  local row = state.entries[j]
  if not row then return false end

  local e = row[i]
  if not e or e.ctype ~= 'team' then return false end

  -- Force show, or if this team already has a seed
  if config.forceseeds or notempty(e.seed) then
    return true
  end

  local group = e.group
  local tpm   = state.teamsPerMatch[j] or 2
  local step  = 2  -- layout uses every 2 rows for teams in a match

  local function neighborHasSeed(idx)
    local n = row[idx]
    return n and n.ctype == 'team' and n.group == group and notempty(n.seed)
  end

  for k = 1, tpm - 1 do
    local plus  = i + step * k
    local minus = i - step * k
    if plus  <= config.r and neighborHasSeed(plus)  then return true end
    if minus >= 1        and neighborHasSeed(minus) then return true end
  end

  return false
end

-- mutates state.hide
function StateChecks.isRoundHidden(j, i, headerindex)
  local col = state.entries[j]
  if not col then return end

  local e = col[i]
  if not e then return end

  local hidx = headerindex or e.headerindex
  if not state.hide[j] then state.hide[j] = {} end

  -- If there is a parent header, this header should be shown
  if notempty(e.pheader) then
    state.hide[j][hidx] = false
    return
  end

  -- Scan forward until next header (or end of round).
  local row = i + 1
  while row <= config.r do
    local r = col[row]
    if r and r.ctype == 'header' then
      break
    end
    if not StateChecks.isBlankEntry(j, row) then
      state.hide[j][hidx] = false
      break
    end
    row = row + 1
  end
end

-- reads entries, rlegs, autolegs
function StateChecks.teamLegs(j, i)
  local col = state.entries[j]
  if not col then return state.rlegs[j] or 1 end

  local e = col[i]
  if not e or e.ctype ~= 'team' then
    return state.rlegs[j] or 1
  end

  -- start with round default
  local legs = state.rlegs[j] or 1

  -- named override (if present)
  if notempty(e.legs) then
    legs = tonumber(e.legs) or legs
  end

  -- helper: treat nil/'' and values containing 'nbsp' as blank
  local function isScoreBlank(v)
    if isempty(v) then return true end
    return type(v) == 'string' and v:find('nbsp', 1, true) ~= nil
  end

  -- autolegs: count contiguous non-blank leg entries starting at 1
  if config.autolegs and e.score then
    local l = 1
    while not isScoreBlank(e.score[l]) do
      l = l + 1
    end
    local inferred = l - 1
    if inferred > 0 then
      legs = inferred
    end
  end

  return legs
end

-- used for bye detection
function StateChecks.roundIsEmpty(j, i)
  local col = state.entries[j]
  if not col then return true end

  local row = i + 1
  while row <= config.r do
    local e = col[row]
    if e and e.ctype == 'header' then
      break
    end
    if not StateChecks.isBlankEntry(j, row) then
      return false
    end
    row = row + 1
  end
  return true
end

-- default header text when none provided
function StateChecks.defaultHeaderText(j, headerindex)
  if headerindex ~= 1 then
    return 'Lower round ' .. tostring(j)
  end

  local c = tonumber(config.c) or j
  local rem = c - j
  if rem == 0 then
    return 'Final'
  elseif rem == 1 then
    return 'Semifinals'
  elseif rem == 2 then
    return 'Quarterfinals'
  else
    return 'Round ' .. tostring(j)
  end
end

function StateChecks.noPaths(j, i)
  -- how many path columns to check
  local cols = state.hascross[j] and 3 or 2

  -- path cells: any nonzero entry in [k][1][n] means there's a path
  local pcj = state.pathCell[j]
  local pci = pcj and pcj[i]
  if pci then
    for k = 1, cols do
      local ktab = pci[k]
      local dir1 = ktab and ktab[1]
      if dir1 then
        for n = 1, 4 do
          local v = dir1[n]
          if v and v ~= 0 then
            return false
          end
        end
      end
    end
  end

  -- cross cells: left/right flag of 1 means there's a cross path
  if state.hascross[j] then
    local ccj = state.crossCell[j]
    local cci = ccj and ccj[i]
    if cci then
      local left, right = cci.left, cci.right
      if (left and left[1] == 1) or (right and right[1] == 1) then
        return false
      end
    end
  end

  return true
end

return StateChecks