Skip to main content

Draw Links

Links define the flow of participants between structures and determine finishing positions for ranking point calculations. Each link specifies:

Try It: Topology Builder

Use the interactive builder below to create structures and connect them with links. Click "+ Add Structure" to add draw components, then drag from output ports (green = winner, red = loser) to input ports on other cards to create links.

type Link = {
linkType: 'LOSER' | 'WINNER' | 'POSITION'; // Type of participant flow
source: {
structureId: string; // Source structure UUID
roundNumber?: number; // Optional: specific round
finishingPositions?: number[]; // For POSITION links
};
target: {
structureId: string; // Destination structure UUID
roundNumber?: number; // Optional: target round
feedProfile?: string; // Feed pattern (e.g., 'DRAW')
};
};

LOSER Links - Direct losing participants:

{
linkType: 'LOSER',
source: {
structureId: 'main-draw-id',
roundNumber: 1 // Round 1 losers
},
target: {
structureId: 'consolation-id',
roundNumber: 1,
feedProfile: 'DRAW' // Positioned by draw
}
}

WINNER Links - Direct winning participants:

{
linkType: 'WINNER',
source: {
structureId: 'round-robin-group-1',
finishingPositions: [1] // Group winners
},
target: {
structureId: 'playoff-structure',
roundNumber: 1
}
}

POSITION Links - Feed based on finishing position:

{
linkType: 'POSITION',
source: {
structureId: 'qualifying-structure-a',
finishingPositions: [1, 2, 3, 4] // Top 4 finishers
},
target: {
structureId: 'main-draw-id',
roundNumber: 1,
feedProfile: 'DRAW'
}
}

Feed Profiles

DRAW - Position assignments determined by draw/seeding TOP_DOWN - Fill positions sequentially from top BOTTOM_UP - Fill positions sequentially from bottom WATERFALL - Distribute across available positions

A Compass draw (8 structures) uses multiple links:

drawDefinition.links = [
// Main draw losers to compass points
{
linkType: 'LOSER',
source: { structureId: 'main', roundNumber: 1 },
target: { structureId: 'east', roundNumber: 1 },
},
{
linkType: 'LOSER',
source: { structureId: 'main', roundNumber: 2 },
target: { structureId: 'west', roundNumber: 1 },
},

// East losers to southeast
{
linkType: 'LOSER',
source: { structureId: 'east', roundNumber: 1 },
target: { structureId: 'southeast', roundNumber: 1 },
},

// West losers to southwest
{
linkType: 'LOSER',
source: { structureId: 'west', roundNumber: 1 },
target: { structureId: 'southwest', roundNumber: 1 },
},

// ... more links for all 8 structures
];

Creating Custom Linked Structures

While pre-defined draw types cover most scenarios, you can create custom configurations:

// Generate structures separately
const { structure: mainStructure } = tournamentEngine.generateStructure({
structureName: 'Main Draw',
stage: 'MAIN',
drawSize: 16,
});

const { structure: consolationStructure } = tournamentEngine.generateStructure({
structureName: 'Consolation',
stage: 'CONSOLATION',
drawSize: 8,
});

// Define custom links
const links = [
{
linkType: 'LOSER',
source: { structureId: mainStructure.structureId, roundNumber: 1 },
target: { structureId: consolationStructure.structureId, roundNumber: 1 },
},
];

// Combine into draw definition
const drawDefinition = {
drawId: UUID(),
structures: [mainStructure, consolationStructure],
links: links,
};

Qualifying Conceptual Model

Traditional View (Incorrect in CODES)

Main Draw (separate entity)
Qualifying Draw (separate entity)

CODES View (Correct)

Draw:
├─ QUALIFYING Stage
│ ├─ Qualifying Structure A
│ ├─ Qualifying Structure B
│ └─ Qualifying Structure C
└─ MAIN Stage
└─ Main Structure (receives qualifiers via links)

Key Points

  1. Qualifying is a stage, not a separate draw
  2. Multiple qualifying structures can exist in the same QUALIFYING stage
  3. Different qualifying structures can feed into different rounds of the main draw
  4. Fed participants at each round can come from different qualifying structures
  5. Links define the flow from qualifying structures to main draw positions

Real-World Example: ITF Tournament

// 64-player main draw with 16 qualifiers
const { drawDefinition } = tournamentEngine.generateDrawDefinition({
drawSize: 64,
drawType: 'SINGLE_ELIMINATION',
qualifyingProfiles: [
{
roundTarget: 1,
structureProfiles: [
// 64 players compete for 16 qualifying spots
{ drawSize: 64, seedsCount: 16, qualifyingPositions: 16 },
],
},
],
});

// Results in:
// QUALIFYING Stage:
// - One 64-player structure producing 16 qualifiers
// MAIN Stage:
// - 64-player main draw with 16 qualifier positions in Round 1

Advanced Example: Progressive Qualifying

Some tournaments use progressive qualifying where different levels feed into different rounds:


**API Reference:** [generateDrawDefinition](/docs/governors/generation-governor#generatedrawdefinition)

const { drawDefinition } = tournamentEngine.generateDrawDefinition({
drawSize: 128,
qualifyingProfiles: [
{
roundTarget: 1,
structureProfiles: [
{ drawSize: 128, qualifyingPositions: 16 } // Main qualifying
]
},
{
roundTarget: 2,
structureProfiles: [
{ drawSize: 32, qualifyingPositions: 4 } // Lucky loser qualifying
]
}
]
});

// Creates:
// - 128-player qualifying for 16 spots in Main Round 1
// - 32-player lucky loser qualifying for 4 spots in Main Round 2

Custom Playoff Topologies with withPlayoffs

The withPlayoffs parameter on generateDrawDefinition supports recursive nesting via its roundPlayoffs field. This enables building arbitrary COMPASS-like topologies — or any custom tree of playoff structures — in a single call, without manually chaining addPlayoffStructures.

How Recursive Playoffs Work

Each level of withPlayoffs creates PLAY_OFF structures from the specified source rounds via LOSER links. The roundPlayoffs field maps a source round number to a child WithPlayoffsArgs, creating sub-playoffs from that child structure's losers.

withPlayoffs tree:                        Resulting structures:

East (MAIN) East (MAIN, drawSize 32)
├── R1 losers → West ├── West (PLAY_OFF, 16)
│ ├── R1 losers → South │ ├── South (PLAY_OFF, 8)
│ │ └── R1 losers → Southeast │ │ └── Southeast (PLAY_OFF, 4)
│ └── R2 losers → Southwest │ └── Southwest (PLAY_OFF, 4)
├── R2 losers → North ├── North (PLAY_OFF, 8)
│ └── R1 losers → Northwest │ └── Northwest (PLAY_OFF, 4)
└── R3 losers → Northeast └── Northeast (PLAY_OFF, 4)

Full COMPASS Example

const { drawDefinition } = engine.generateDrawDefinition({
drawSize: 32,
drawName: 'East',
withPlayoffs: {
roundProfiles: [{ 1: 1 }, { 2: 1 }, { 3: 1 }],
playoffAttributes: {
'0-1': { name: 'West', abbreviation: 'W' },
'0-2': { name: 'North', abbreviation: 'N' },
'0-3': { name: 'Northeast', abbreviation: 'NE' },
},
roundPlayoffs: {
1: {
roundProfiles: [{ 1: 1 }, { 2: 1 }],
playoffAttributes: {
'0-1': { name: 'South', abbreviation: 'S' },
'0-2': { name: 'Southwest', abbreviation: 'SW' },
},
roundPlayoffs: {
1: {
roundProfiles: [{ 1: 1 }],
playoffAttributes: {
'0-1': { name: 'Southeast', abbreviation: 'SE' },
},
},
},
},
2: {
roundProfiles: [{ 1: 1 }],
playoffAttributes: {
'0-1': { name: 'Northwest', abbreviation: 'NW' },
},
},
},
},
});

This produces the same 8-structure, 7-link, 72-matchUp topology as the built-in COMPASS draw type, but with full control over structure naming, depth, and which branches exist.

Partial Topologies

You can omit branches to create partial COMPASS draws. For example, a 6-structure variant (no Southwest or Southeast) simply omits those roundPlayoffs entries:

withPlayoffs: {
roundProfiles: [{ 1: 1 }, { 2: 1 }, { 3: 1 }],
roundPlayoffs: {
1: { roundProfiles: [{ 1: 1 }] }, // West → South only
2: { roundProfiles: [{ 1: 1 }] }, // North → Northwest only
},
}

Using with mocksEngine

Recursive withPlayoffs works directly in drawProfiles for both mocksEngine.generateTournamentRecord() patterns:

// Via top-level drawProfiles
const { tournamentRecord } = mocksEngine.generateTournamentRecord({
drawProfiles: [
{
drawSize: 32,
drawName: 'East',
withPlayoffs: {
/* nested roundPlayoffs tree */
},
},
],
});

// Via eventProfiles
const { tournamentRecord } = mocksEngine.generateTournamentRecord({
eventProfiles: [
{
drawProfiles: [
{
drawSize: 32,
withPlayoffs: {
/* nested roundPlayoffs tree */
},
},
],
},
],
});

See withPlayoffs API reference for the full WithPlayoffsArgs type and more examples.

note

Additional Playoff Structures

  • getAvailablePlayoffProfiles() - Valid attributes for playoff structures generation
  • generateAndPopulatePlayoffStructures() - Generates playoff structures
  • attachPlayoffStructures() - Attaches playoff structures to target drawDefinition
  • addPlayoffStructures() - Combines generation and attachment of playoff structures

Voluntary Consolation Structure

  • getEligibleVoluntaryConsolationParticipants() - Configurable method for determining eligibility
  • generateVoluntaryConsolation() - Generates matchUps for consolation structure

Feed-In Configuration