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.
Link Structure
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')
};
};
Link Types
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
Example: Compass Draw Links
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
- Qualifying is a stage, not a separate draw
- Multiple qualifying structures can exist in the same QUALIFYING stage
- Different qualifying structures can feed into different rounds of the main draw
- Fed participants at each round can come from different qualifying structures
- 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.
Related Policies and Methods
Additional Playoff Structures
getAvailablePlayoffProfiles()- Valid attributes for playoff structures generationgenerateAndPopulatePlayoffStructures()- Generates playoff structuresattachPlayoffStructures()- Attaches playoff structures to target drawDefinitionaddPlayoffStructures()- Combines generation and attachment of playoff structures
Voluntary Consolation Structure
getEligibleVoluntaryConsolationParticipants()- Configurable method for determining eligibilitygenerateVoluntaryConsolation()- Generates matchUps for consolation structure
Feed-In Configuration
- Feed-In Policy - Configure consolation feed patterns
- Progression Policy - Control automatic qualifier placement
Related Documentation
- Draw Types - Pre-defined draw types and stages
- Draw Generation - Creating and configuring draws
- Actions - Managing draw structures and participants
- Feed-In Policy - Detailed feed pattern configuration
- Generation Governor - Complete API reference