Draws & Pairings
Generate tee-time pairings for competition rounds using various draw methods.
Draw types
| Type | Description | Use Case |
|---|---|---|
RANDOM | Random assignment | Social events, casual comps |
HANDICAP_ORDER | Grouped by handicap | Club competitions |
SEEDED | Manual seeding | Championships, knockouts |
TEE_TIME_ORDER | Based on existing bookings | Pre-booked competitions |
Generating draws
Basic draw
await drawsService.generateDraw({
roundId: 'round-123',
drawType: 'RANDOM',
});
With configuration
await drawsService.generateDraw({
roundId: 'round-123',
drawType: 'HANDICAP_ORDER',
groupSize: 4,
startTime: new Date('2025-12-15T07:00:00'),
intervalMinutes: 8,
});
Shotgun start
await drawsService.generateDraw({
roundId: 'round-123',
drawType: 'RANDOM',
groupSize: 4,
startingHoles: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18],
});
Draw input
interface GenerateDrawInput {
roundId: string;
drawType: 'RANDOM' | 'HANDICAP_ORDER' | 'SEEDED' | 'TEE_TIME_ORDER';
groupSize?: number; // Default: 4
startTime?: Date; // First tee time
intervalMinutes?: number; // Default: 8
startingHoles?: number[]; // For shotgun starts
}
Draw methods
Random
Entries shuffled randomly into groups.
await drawsService.generateDraw({
roundId: 'round-123',
drawType: 'RANDOM',
groupSize: 4,
startTime: new Date('2025-12-15T07:00:00'),
intervalMinutes: 8,
});
Result:
07:00 - Group 1: Player D, Player A, Player G, Player K
07:08 - Group 2: Player C, Player F, Player B, Player H
07:16 - Group 3: Player E, Player J, Player I, Player L
Handicap order
Entries sorted by handicap index, then grouped.
await drawsService.generateDraw({
roundId: 'round-123',
drawType: 'HANDICAP_ORDER',
groupSize: 4,
});
Options:
- Low handicaps first (default)
- High handicaps first (reverse)
- Mixed (snake draft)
Result (low first):
07:00 - Group 1: +2.1, 3.4, 5.2, 6.8
07:08 - Group 2: 7.1, 8.3, 9.0, 10.2
07:16 - Group 3: 11.5, 12.3, 14.1, 15.8
Seeded
Manual seed order preserved.
// Pre-set seed order
await drawsService.setSeedOrder('round-123', [
'entry-1', // #1 seed
'entry-2', // #2 seed
'entry-3', // #3 seed
// ...
]);
await drawsService.generateDraw({
roundId: 'round-123',
drawType: 'SEEDED',
groupSize: 4,
});
Use cases:
- Club championships (defending champion first)
- Qualifying rounds (top qualifiers together)
- Sponsored groups
Tee time order
Use existing tee sheet bookings as the draw.
await drawsService.generateDraw({
roundId: 'round-123',
drawType: 'TEE_TIME_ORDER',
});
Behavior:
- Find all entries linked to tee time slots
- Order by slot time
- Create pairings matching existing groups
Pairing data
interface Pairing {
id: string;
roundId: string;
pairingNumber: number; // 1, 2, 3...
teeTime: Date;
startingHole: number; // 1-18 (for shotgun)
entries: CompetitionEntry[];
}
Start types
Configure on round creation:
Tee times
Sequential start from hole 1.
{
startType: 'TEE_TIMES',
// Draw generates sequential times
}
Shotgun
All groups start simultaneously on different holes.
{
startType: 'SHOTGUN',
shotgunTime: new Date('2025-12-15T08:00:00'),
}
Draw assigns starting holes:
08:00 - Group 1 → Hole 1
08:00 - Group 2 → Hole 2
08:00 - Group 3 → Hole 3
...
08:00 - Group 18 → Hole 18
Mixed
Combination of tee times and shotgun holes.
{
startType: 'MIXED',
startingHoles: [1, 10], // Two-tee start
}
Group sizes
| Size | Format | Use Case |
|---|---|---|
| 2 | Twosomes | Matchplay, fast play |
| 3 | Threesomes | Odd numbers, faster rounds |
| 4 | Foursomes | Standard club play |
Handling odd numbers
When entries don't divide evenly:
// 14 entries with groupSize: 4
// Result: 3 groups of 4, 1 group of 2
Interval timing
Standard intervals:
| Course Type | Interval | Reasoning |
|---|---|---|
| Standard | 8 minutes | Normal pace |
| Championship | 10 minutes | More setup time |
| Fast play | 7 minutes | Quick turnaround |
| 2-tee start | 8 minutes | Both tees |
Manual pairings
Create specific pairings:
await drawsService.createPairing({
roundId: 'round-123',
pairingNumber: 1,
teeTime: new Date('2025-12-15T07:00:00'),
startingHole: 1,
entryIds: ['entry-a', 'entry-b', 'entry-c', 'entry-d'],
});
Swap players
await drawsService.swapEntries({
roundId: 'round-123',
entryId1: 'entry-a', // From pairing 1
entryId2: 'entry-e', // From pairing 3
});
Move player
await drawsService.moveEntry({
roundId: 'round-123',
entryId: 'entry-a',
toPairingNumber: 5,
});
Late entries
Add entries after draw generated:
// Option 1: Add to existing group with space
await drawsService.addToExistingPairing({
roundId: 'round-123',
entryId: 'late-entry',
pairingNumber: 12, // Has 3 players
});
// Option 2: Create new pairing at end
await drawsService.addNewPairing({
roundId: 'round-123',
entryIds: ['late-1', 'late-2'],
teeTime: new Date('2025-12-15T10:00:00'),
});
Draw output
Get draw for round
const draw = await drawsService.getDraw('round-123');
// Returns:
{
roundId: 'round-123',
startType: 'TEE_TIMES',
pairings: [
{
pairingNumber: 1,
teeTime: '2025-12-15T07:00:00',
startingHole: 1,
entries: [
{ id: 'entry-1', playerName: 'John Smith', handicap: 5.2 },
{ id: 'entry-2', playerName: 'Jane Doe', handicap: 12.4 },
// ...
],
},
// ...
],
}
Export draw
// CSV export
const csv = await drawsService.exportDraw('round-123', 'csv');
// PDF (if configured)
const pdf = await drawsService.exportDraw('round-123', 'pdf');
Clear and regenerate
// Clear existing draw
await drawsService.clearDraw('round-123');
// Regenerate with different method
await drawsService.generateDraw({
roundId: 'round-123',
drawType: 'HANDICAP_ORDER',
groupSize: 3,
});
Note: Clearing draw removes pairings but preserves entries.