# FIFA World Cup: Group Stage to Knockout

    library(bracketeer)

This vignette models a 16-team World Cup: four groups of four teams, with the top two from each group advancing to a single-elimination knockout round.

It demonstrates:

- `round_robin("groups", groups = 4)` for parallel group play,
- `top_per_group(2)` for per-group advancement routing,
- auto-advance behavior (no explicit `advance()` needed),
- `stage_status()`, `matches()`, and `standings()` for live inspection.

## 1) Participant pool

    teams <- c(
      "Argentina", "Australia", "Brazil",   "Croatia",
      "England",   "France",   "Japan",     "Morocco",
      "Netherlands", "Poland", "Portugal",  "Senegal",
      "South Korea", "Spain",  "Switzerland", "United States"
    )

    length(teams)

## 2) Define the tournament

`round_robin("groups", groups = 4)` creates four independent groups within a single stage node. Each group runs a full round-robin schedule.

`top_per_group(2)` selects the top two finishers from each group’s own standings — not the top 8 from a combined ranking. This is the standard World Cup advancement rule.

    trn <- tournament(teams) |>
      round_robin("groups", groups = 4) |>
      single_elim("knockout", take = top_per_group(2))

`from = previous_stage()` is implicit — “knockout” reads from “groups” automatically.

## 3) Inspect the group schedule

    stage_status(trn)

    group_ms <- matches(trn, "groups")
    nrow(group_ms)   # 4 groups × 6 matches = 24 matches
    head(group_ms)

## 4) Enter group results

Auto-advance is on by default. Once the last group match is entered, the knockout stage materializes automatically with the top two from each group.

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

No explicit `advance()` call — the knockout stage materializes on its own.

## 5) Confirm advancement

    stage_status(trn)
    #   stage     status        complete  total  materialized
    #   groups    complete            24     24          TRUE
    #   knockout  active               0      8          TRUE

    matches(trn, "knockout")  # 8 teams: 2 × 4 groups

## 6) Inspect group standings

    standings(trn, "groups")

The standings include a group membership column when the source stage uses `groups =`.

## 7) Run the knockout round

    knockout_ms <- matches(trn, "knockout")

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

    winner(trn)
    rankings(trn)

## 8) Routing audit

`routing_log()` records the transition resolution: which participants were selected, from which source stage, by which selector.

    routing_log(trn)

## 9) Comparison with previous API

Task | Old API | New API
--- | --- | ---
Define groups → knockout | `tournament_spec() \|> add_stage() \|> add_stage() \|> split_stage(from = ..., into = list(...)) \|> build_tournament(spec, teams)` | `tournament(teams) \|> round_robin("groups", groups = 4) \|> single_elim("knockout", take = top_per_group(2))`
Enter a result | `result(trn, "groups", match_id = 1, score1 = 1, score2 = 0)` | `result(trn, "groups", match = 1, score = c(1, 0))`
Advance stage | `if (is_stage_complete(trn, "groups")) advance(trn, "groups")` | automatic by default
See standings | `get_standings(trn$stage_state$groups$bracket)` | `standings(trn, "groups")`
Winner | `get_winner(trn$stage_state$knockout$bracket)` | `winner(trn)`