Planning a large table tennis tournament is a classic optimization problem. You have a limited set of resources (tables), a large number of tasks (matches), and a complex web of dependencies (winners move on, losers drop out).

In TourneyPilot, we solve this using a custom implementation of Discrete-Time List Scheduling over a Dependency Graph (DAG). Here’s a look at the architecture.

1. The Challenge: It’s Not Just About Time

Simple calendar scheduling is easy (“Event A at 9:00”). Tournament scheduling is hard because:

  1. Dependencies: You can’t schedule the Semi-Final before the Quarter-Final.
  2. Resource Contention: 500 matches all want to “start as early as possible”, but you only have 20 tables.
  3. Hybrid Constraints: A user might say “Start Men’s Singles at 9:00 AM” (Manual Constraint) while leaving “Women’s Doubles” completely floating (Auto).

2. The Model: Scheduling as a Graph

First, we transform the hierarchical tournament structure (Tournament -> Competition -> Stage -> Round -> Match) into a flat Directed Acyclic Graph (DAG).

We define ScheduleNode and ScheduleEdge types effectively allowing us to abstract away the “sport” and focus on the “schedule”.

Graph Model of Schedule Hierarchy

This transformation happens in GraphBuilder. It walks the domain entities and builds edges:

  • Structural Edges: Stage 1 must finish before Stage 2.
  • Logical Edges: Round 1 must finish before Round 2.

3. The Constraint Engine

Before we schedule a single match, we need to know the earliest possible start time for every node. This is where the Hybrid part comes in.

If a user manually pins the “Finals” to 4:00 PM, that constraint must propagate backwards. But more commonly, they pin a “Start Time” for a competition.

Constraint Propagation Diagram

This allows mixed-mode planning. You can fix the “skeleton” of the day manually, and let the algorithm fill in the muscle.

4. The Algorithm: Discrete-Time List Scheduling

We generally use a List Scheduling approach. It’s a heuristic method that is near-optimal for this class of problem and, importantly, is very fast (O(N)).

Step 1: Time Discretization

We slice the day into “slots” (e.g., 5 minutes or 10 minutes).

Step 2: The Loop

The core loop in AutoScheduler simulates the passage of time:

List Scheduling Algorithm Flowchart

Why Workload Priority?

We prioritize tasks with the highest “workload” (total match minutes required) because they are most likely to be the bottleneck. By scheduling the “heavy” items first, we fit the smaller, lighter items (like small groups) into the gaps left behind, maximizing table utilization.

Conclusion

By treating a tournament as a Graph problem, we can support:

  1. Correctness: Guarantees no match is scheduled before its players are ready.
  2. Efficiency: Maximizes table usage density.
  3. Flexibility: The “Hybrid” mix of manual control and automation gives organizers the best of both worlds.

This architecture is defined in packages/domain and runs entirely client-side, giving users instant feedback as they plan their event.