Case Study · Sports & federations

TBF: a decision support system that builds referee and evaluator assignment on rules, fairness and proposals

A system that turns the Turkish Basketball Federation's Central Referee Board's weekly assignment work into mixed-integer optimization respecting federation rules and fairness between referees. The system proposes; the board reviews, edits by hand and commits. Referee assignment is in production pilot, evaluator assignment was added as a second problem, and the multi-user web interface is under way.

Proposal
Decision

The system proposes, the board commits

9
Hard rules

Never violated — in eligibility or in the model

Rolling
Horizon

A long window is solved, the first week is committed

280+
Tests

Independent constraint checker + regressions

Problem

Every week the Central Referee Board has to assign three-person crews to hundreds of matches from among hundreds of referees. Done by hand, four things break at once:

  • Fairness cannot be measured. Who got how many matches and who travelled how far cannot be tracked across a season; complaints stay subjective.
  • Rule breaches slip through. Excuses, bans, city restrictions, the ban on refereeing the same team twice in a row, two matches on one day — none of it can be held in the head at once.
  • Match difficulty and crew strength do not match. A weak crew can go to a big match, an unnecessarily strong one to a small match.
  • It takes far too long. A week’s assignment takes hours; when the fixture changes it starts over.

The first design principle: control stays with the board

The system does not decide, it proposes. The flow has three steps: the system solves and produces a proposal; the board reviews and, if needed, changes assignments one by one — every change is checked instantly against the hard constraints and any breach is flagged with its reason; the board commits. A committed match is never changed automatically again and enters later solves as history. Every proposal’s reasoning is recorded: which rule, which value.

This principle was not a technical preference but the precondition for acceptance. A referee board cannot say “the algorithm said so”; it has to be able to say “by this rule, with this value”.

Model

The decision variable is binary: is referee r on duty at match m. The model is built per classification — A, B and C are solved separately and pools do not mix.

Most hard constraints live in eligibility, not in the model. Excuses, bans, league licences, city restrictions, a day already filled by another committed assignment, a committed duty within the rotation window — these eliminate the (referee, match) pair before a variable is even created. What remains in the model: exactly three referees per match, the crew rating floor, the day gap, in-window rotation, the monthly cap.

The objective function minimises a weighted sum whose priority order is deliberately well separated: match-count equality ≫ cross-league balance ≫ different league ≫ rating fit ≫ travel. Match counts and travel are computed cumulatively — committed history since the start of the season plus the window; fairness is a season-long concept, not a weekly one.

A match’s required floor rating is the largest of three things: the two teams’ rating sum, a match-specific importance floor (derby, cup, goal average) and a user-defined minimum. The crew’s rating above that floor is penalised as “surplus” — waste of strong referees.

Two weeks, two solvers, three safety nets

Two notions of week. The federation’s fixture round numbers are not aligned across leagues; some leagues have byes, some start differently. The system therefore derives a Friday-to-Thursday calendar week and runs every time constraint — window, rotation, rolling horizon — on it; the league week remains for display only.

Two solvers. The same model is built for both open-source CBC (PuLP) and OR-Tools CP-SAT; the default mode runs both and picks the better one by a solver-independent canonical objective. Status labels are honest: a solution cut off at the time limit is reported not as “Optimal” but as “Feasible (time limit)” — so the user does not over-trust the word “Optimal”.

Three safety nets. A feasibility diagnosis before solving — if there is a block, the solver never runs and the user is told in plain language which match is stuck and why. Data quality warnings — zero-rated referees, missing distances, empty league licences — make silently disabled rules visible. And the produced solution is re-checked against the hard constraints by a checker independent of the model; the same checker runs in the test suite by reading the database directly.

Rolling horizon

A long window is solved, only the first week(s) are committed, the window rolls forward. A run persists as window + proposals + record (parameters, solver, canonical score); several competing runs for the same weeks can be kept side by side. When the fixture changes, only the affected weeks are re-solved.

The second problem: evaluator assignment

Assigning one evaluator (observer) to each match in addition to the referee crew was built as a separate optimization problem: referee assignment is committed first, then the evaluator solution sits on top of it. The rules differ — rating–league matching, at most one duty a week, locality priority with a distance cost, balanced distribution by season end. An uncovered match does not produce infeasibility, it is reported with a heavy penalty; the fairness rule lives in a separate, replaceable module rather than being embedded in the model. Open rules to ask the federation were collected into a question set; implementation could start before the answers came because every decision was built as a parameter or a replaceable module.

System and status

Data lives in PostgreSQL; the business logic is a Python package (cozucu) separate from the presentation layer — the interface never talks to the database, everything goes through the service layer. The first version ran in the cloud with a Streamlit interface and entered pilot; corrections were applied following an independent audit report. Phase three is an authenticated web application (FastAPI + React) running under Docker Compose on the federation’s own server: asynchronous solve flow, manual editing, run management, commit, report screens, user management and audit trail are complete; fixture revision and integration with the federation’s ERP are next.

The system is protected by more than 280 tests; the independent hard-constraint checker re-checks every solution by reading it back from the database.

How the model was built

ObjectiveDirectionTension
Match-count inequality (most – least)The heaviest term — fairness is cumulative across the season; new weeks count together with history
Cross-league distribution deviation and clustering in one leagueA referee going evenly to the leagues within a classification can conflict with short-term match equality
Rating surplus — the crew's rating above the floorSending a strong referee to a small match is waste; but fairness sometimes requires it
Travel inequality (km)The lightest term — dropped from the model entirely when distance data is missing

Constraints

  • Exactly three referees per match; the crew's total rating cannot fall below the match's required floor
  • No assignment on dates with an excuse, ban, Euroleague duty or administrative restriction — filtered before the pair is even created
  • The referee must hold the league licence; in classifications B and C, cannot be assigned to a team from their own city
  • A minimum day gap between two matches; the same team cannot be refereed twice within the rotation window
  • A cap on matches per calendar month — committed history counts toward it
  • Committed matches never enter the model; proposals are hypotheses, committed ones are facts
  • Evaluators are assigned only to matches with committed referees, at most once a week, by rating–league match
  • An uncovered match does not produce infeasibility — it is reported with a heavy penalty

All our work