Skip to contents

This vignette models a League of Legends Worlds style event:

  • A Swiss stage where all 16 teams compete over several rounds,
  • The top 8 advance to a single-elimination top-cut bracket.

This is the canonical linear chain pattern — two stages in sequence with no branching. It demonstrates:

  • swiss("open", rounds = 5) for a Swiss-system stage,
  • single_elim("top_cut", take = top_n(8)) reading from the prior stage automatically (no from = needed),
  • top_n(8) as the simplest flat routing selector,
  • the auto-advance default, and
  • full inspection with stage_status(), standings(), and winner().

1) Define the entrants

teams <- c(
  "Gen.G",        "T1",          "Hanwha Life", "Dplus KIA",
  "BLG",          "TES",         "LNG",         "JDG",
  "G2",           "Fnatic",      "MAD Lions",   "BDS",
  "Cloud9",       "Team Liquid",  "FlyQuest",    "100 Thieves"
)

length(teams)

2) Define the tournament

Because the pipeline is a straight line, from is never written. The single_elim stage implicitly reads from swiss via previous_stage().

trn <- tournament(teams) |>
  swiss("open", rounds = 5) |>
  single_elim("top_cut", take = top_n(8))

This is the entire routing definition: 16 teams play a 5-round Swiss; the top 8 by Swiss standings advance to a single-elimination bracket.


3) Preflight check (spec path)

When reusing a format across multiple events, define a spec first and validate before building.

worlds_spec <- spec() |>
  swiss("open", rounds = 5) |>
  single_elim("top_cut", take = top_n(8))

validate(worlds_spec, n = 16)   # passes

# Build for this year's entrants
trn <- worlds_spec |> build(teams)

4) Inspect the Swiss schedule

stage_status(trn)

open_ms <- matches(trn, "open")
nrow(open_ms)   # 5 rounds × 8 matches = 40 matches
head(open_ms)

5) Enter Swiss results

Round-by-round entry. Swiss schedules are generated round-by-round as results come in, so you’ll see new pending matches after each round.

for (i in seq_len(nrow(open_ms))) {
  trn <- trn |> result("open", match = open_ms$id[i], score = c(1, 0))

  # Refresh pending matches — Swiss generates each round dynamically.
  open_ms <- matches(trn, "open")
}

After the final Swiss result, the top-cut stage materializes automatically.


6) Confirm advancement

stage_status(trn)
#   stage     status     complete  total  materialized
#   open      complete         40     40          TRUE
#   top_cut   active            0      7          TRUE

standings(trn, "open")   # full Swiss standings, 1st through 16th
matches(trn, "top_cut")  # 8-team bracket

7) Run the top-cut bracket

cut_ms <- matches(trn, "top_cut")

for (i in seq_len(nrow(cut_ms))) {
  trn <- trn |> result("top_cut", match = cut_ms$id[i], score = c(1, 0))
}

8) Outcomes

winner(trn)
rankings(trn)
routing_log(trn)

9) Manual advance mode

For tournament operations where stages advance on an explicit signal (e.g., after a broadcast break), use auto_advance = FALSE:

trn <- tournament(teams, auto_advance = FALSE) |>
  swiss("open", rounds = 5) |>
  single_elim("top_cut", take = top_n(8))

# ... enter all Swiss results ...

# Stage does not advance until you say so:
trn <- trn |> advance("open")

10) Batch result entry

results() accepts a data frame for entering multiple results at once.

round1 <- data.frame(
  match  = matches(trn, "open")$id,
  score1 = rep(1L, 8),
  score2 = rep(0L, 8)
)

trn <- trn |> results("open", round1)