Skip to content

Swiss pairing accepts an avoidable rematch (greedy fallback) — a 4-player pod repeats an opponent #7937

Description

@cuinhellcat

Observed

4-player pod, Swiss (3 rounds — SWISS_ROUNDS applies to every pod size): a player faces the same opponent twice and never meets one of the other seats. Reproduced 2026-08-26 in a local bot pod (Premier / Swiss / Casual / size 4).

Cause

generate_swiss_pairings (crates/draft-core/src/session.rs:259) pairs greedily: it takes the pool head and picks the first non-rematch partner, but on failure it falls back to the first partner regardless (.unwrap_or(0)), and pairs already made are never revisited.

With records A=2, B=1, C=1, D=0 after two rounds (prior: A–B, C–D, A–C, B–D), the walk is: [A] carries into [A,B,C]; both B and C are prior opponents of A, so the fallback pairs A–B again; C carries into [C,D], prior again, fallback pairs C–D again. The rematch-free perfect matching A–D / B–C exists — round 3 of a 4-pod is always the round-robin completion — but the greedy cannot find it.

The existing test_swiss_rematch_avoidance only covers 8 players / round 2, where the head-first pick happens to succeed.

Expected

When a rematch-free perfect matching exists for the round, it is chosen. A rematch is admitted only when no such matching exists (e.g., a 2-player pod from round 2 on).

Fix direction

Replace the head-first greedy with a small backtracking search over the standings-ordered pool (bracket order preserved, partners tried in standings order, depth bounded by pod size ≤ 8): dead ends backtrack; only if the full search fails is a rematch admitted. Odd pods: pick the bye bottom-up such that the paired remainder still admits a rematch-free matching. PR follows.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions