group-planner 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,278 @@
1
+ /**
2
+ * Core types for competitor partitioning algorithms
3
+ */
4
+ /** Built-in IFAA catalogue. Callers can supply any other labels through SolutionSpace. */
5
+ type EquipmentClass = 'HB' | 'LB' | 'TR' | 'BHR' | 'BBR' | 'FSR' | 'BBC' | 'BL' | 'BU' | 'FSC' | 'FU';
6
+ type AgeGroup = 'C' | 'J' | 'YA' | 'A' | 'V' | 'S';
7
+ type Gender = 'F' | 'M';
8
+ type Algorithm = 'mip' | 'mip-lp' | 'clustering' | 'auto';
9
+ /**
10
+ * Labels the solver is allowed to see. When omitted, the built-in IFAA lists are used.
11
+ * Pass the host application's own age, gender, and bowstyle ids here.
12
+ */
13
+ interface SolutionSpace {
14
+ equipmentClasses: readonly string[];
15
+ ageCategories: readonly string[];
16
+ genders: readonly string[];
17
+ }
18
+ interface Competitor {
19
+ id: string;
20
+ equipmentClass: string;
21
+ ageCategory: string;
22
+ gender: string;
23
+ guardianId?: string;
24
+ }
25
+ interface PartitionOptions {
26
+ /** Algorithm to use for partitioning (default: 'auto') */
27
+ algorithm?: Algorithm;
28
+ /** Prefer grouping competitors of same equipment class together vs distributing evenly */
29
+ groupByEquipmentClass: boolean;
30
+ /** Maximum number of subsets allowed (default: 28) */
31
+ maxSubsets?: number;
32
+ /** Minimum subset size. Values below 3 are raised to 3. Size 1 and 2 are never a target. */
33
+ minSubsetSize?: number;
34
+ /** Maximum subset size (default: 6) */
35
+ maxSubsetSize?: number;
36
+ /** Preferred subset size (default: 4) */
37
+ preferredSubsetSize?: number;
38
+ /** Weight for same gender preference (default: 1.0) */
39
+ genderWeight?: number;
40
+ /** Weight for same age category preference (default: 1.0) */
41
+ ageCategoryWeight?: number;
42
+ /** Weight for equipment class grouping preference (default: 1.0) */
43
+ equipmentClassWeight?: number;
44
+ /**
45
+ * Extra weight for subsets whose size equals preferredSubsetSize.
46
+ * Zero keeps the historical size score. Raise it when full groups should outrank mix.
47
+ */
48
+ closedGroupWeight?: number;
49
+ /** When set, competitor labels are checked against these lists instead of the IFAA defaults. */
50
+ solutionSpace?: SolutionSpace;
51
+ }
52
+ interface PartitionResult {
53
+ subsets: Competitor[][];
54
+ score: number;
55
+ metadata: {
56
+ totalCompetitors: number;
57
+ totalSubsets: number;
58
+ averageSubsetSize: number;
59
+ scoreBreakdown: {
60
+ sizeScore: number;
61
+ closedGroupScore: number;
62
+ genderScore: number;
63
+ ageCategoryScore: number;
64
+ equipmentClassScore: number;
65
+ };
66
+ };
67
+ }
68
+ interface AlgorithmResult {
69
+ result: PartitionResult;
70
+ executionTime: number;
71
+ algorithmUsed: string;
72
+ }
73
+
74
+ /**
75
+ * Utility functions for competitor partitioning
76
+ */
77
+
78
+ type ResolvedPartitionOptions = Omit<Required<PartitionOptions>, 'solutionSpace'> & {
79
+ solutionSpace?: SolutionSpace;
80
+ };
81
+ /**
82
+ * Default partition options
83
+ */
84
+ declare const DEFAULT_OPTIONS: ResolvedPartitionOptions;
85
+ /**
86
+ * Validates that a partition solution meets all constraints
87
+ */
88
+ declare function validatePartition(competitors: Competitor[], subsets: Competitor[][], options?: Partial<PartitionOptions>): {
89
+ valid: boolean;
90
+ errors: string[];
91
+ };
92
+ /**
93
+ * Calculates the quality score for a partition solution
94
+ */
95
+ declare function calculateScore(subsets: Competitor[][], options?: Partial<PartitionOptions>): {
96
+ score: number;
97
+ breakdown: PartitionResult['metadata']['scoreBreakdown'];
98
+ };
99
+ /**
100
+ * Creates a partition result with metadata
101
+ */
102
+ declare function createPartitionResult(subsets: Competitor[][], options?: Partial<PartitionOptions>): PartitionResult;
103
+ /**
104
+ * Helper to get competitors by guardian relationship
105
+ */
106
+ declare function getGuardianGroups(competitors: Competitor[]): Map<string, Competitor[]>;
107
+ /**
108
+ * Groups competitors into guardian families (guardian + their dependents)
109
+ * Returns an array where each element is either a single competitor or a guardian family
110
+ */
111
+ declare function createGuardianFamilies(competitors: Competitor[]): Competitor[][];
112
+ /**
113
+ * Validates that guardian constraints are satisfied in the partition
114
+ */
115
+ declare function validateGuardianConstraints(competitors: Competitor[], subsets: Competitor[][]): {
116
+ valid: boolean;
117
+ errors: string[];
118
+ };
119
+ declare function isValidCompetitor(competitor: unknown, space?: SolutionSpace): competitor is Competitor;
120
+ /**
121
+ * Validates an array of competitors
122
+ */
123
+ declare function validateCompetitors(competitors: any[], space?: SolutionSpace): {
124
+ valid: boolean;
125
+ errors: string[];
126
+ };
127
+
128
+ /** A target smaller than this is not a group. Size 1 and size 2 are never emitted. */
129
+ declare const MINIMUM_SUBSET_SIZE = 3;
130
+ declare function smallestAllowedSubset(requestedMin: number): number;
131
+ /**
132
+ * Drops or repairs targets of size 1 or 2.
133
+ * A short group is filled from donors, merged into another group, or removed.
134
+ * Removed archers are not given a target.
135
+ */
136
+ declare function retainLegalSubsets(subsets: Competitor[][], min: number, max: number): Competitor[][];
137
+
138
+ /**
139
+ * Constants for competitor categories and validation
140
+ */
141
+
142
+ /**
143
+ * Valid equipment classes
144
+ */
145
+ declare const EQUIPMENT_CLASSES: readonly EquipmentClass[];
146
+ /**
147
+ * Valid age groups (ordered from youngest to oldest)
148
+ */
149
+ declare const AGE_GROUPS: readonly AgeGroup[];
150
+ /**
151
+ * Valid genders
152
+ */
153
+ declare const GENDERS: readonly Gender[];
154
+ /**
155
+ * Equipment class descriptions for documentation
156
+ */
157
+ declare const EQUIPMENT_CLASS_DESCRIPTIONS: Record<EquipmentClass, string>;
158
+ /**
159
+ * Age group descriptions for documentation
160
+ */
161
+ declare const AGE_GROUP_DESCRIPTIONS: Record<AgeGroup, string>;
162
+ /**
163
+ * Gender descriptions for documentation
164
+ */
165
+ declare const GENDER_DESCRIPTIONS: Record<Gender, string>;
166
+ /**
167
+ * Checks if a value is a valid equipment class
168
+ */
169
+ declare function isValidEquipmentClass(value: string, allowed?: readonly string[]): boolean;
170
+ /**
171
+ * Checks if a value is a valid age group
172
+ */
173
+ declare function isValidAgeGroup(value: string, allowed?: readonly string[]): boolean;
174
+ /**
175
+ * Checks if a value is a valid gender
176
+ */
177
+ declare function isValidGender(value: string, allowed?: readonly string[]): boolean;
178
+ /**
179
+ * Gets the age group order for comparison (lower number = younger)
180
+ */
181
+ declare function getAgeGroupOrder(ageGroup: AgeGroup): number;
182
+ /**
183
+ * Compares two age groups (-1 if a is younger, 0 if same, 1 if a is older)
184
+ */
185
+ declare function compareAgeGroups(a: AgeGroup, b: AgeGroup): number;
186
+
187
+ /**
188
+ * Mixed Integer Programming approach for competitor partitioning
189
+ *
190
+ * This algorithm formulates the partitioning problem as a MIP problem where:
191
+ * - Binary variables x[i][j] indicate if competitor i is in subset j
192
+ * - Objective function maximizes the quality score based on preferences
193
+ * - Constraints ensure valid partitioning
194
+ */
195
+
196
+ /**
197
+ * Solves the competitor partitioning problem using Mixed Integer Programming
198
+ */
199
+ declare function solveMIP(competitors: Competitor[], options?: Partial<PartitionOptions>): AlgorithmResult;
200
+
201
+ /**
202
+ * Mixed Integer Programming approach using HiGHS solver
203
+ *
204
+ * This algorithm formulates the partitioning problem as a proper MIP problem where:
205
+ * - Binary variables x[i][j] indicate if competitor i is in subset j
206
+ * - Objective function maximizes the quality score based on preferences
207
+ * - Constraints ensure valid partitioning and guardian relationships
208
+ */
209
+
210
+ /**
211
+ * Solves the competitor partitioning problem using HiGHS MIP solver
212
+ */
213
+ declare function solveMIPWithLP(competitors: Competitor[], options?: Partial<PartitionOptions>): Promise<AlgorithmResult>;
214
+
215
+ /**
216
+ * Clustering approach for competitor partitioning
217
+ *
218
+ * This algorithm uses a modified K-means clustering approach where:
219
+ * - Competitors are represented as feature vectors
220
+ * - Distance metric considers gender, age category, and equipment class
221
+ * - Clusters are formed and then adjusted to meet size constraints
222
+ * - Post-processing ensures all constraints are satisfied
223
+ */
224
+
225
+ /**
226
+ * Solves the competitor partitioning problem using clustering with guardian constraints
227
+ */
228
+ declare function solveClustering(competitors: Competitor[], options?: Partial<PartitionOptions>): AlgorithmResult;
229
+
230
+ /**
231
+ * Group Planner Library
232
+ *
233
+ * A TypeScript library for competitor partitioning algorithms.
234
+ * Provides three different approaches: Mixed Integer Programming,
235
+ * K-means Clustering, and Branch-and-bound.
236
+ */
237
+
238
+ /**
239
+ * Main solver function - single API entry point for all algorithms
240
+ *
241
+ * @param competitors - Array of competitor objects to partition
242
+ * @param options - Configuration options including algorithm selection
243
+ * @returns Algorithm result with partitioned subsets
244
+ *
245
+ * @example
246
+ * ```typescript
247
+ * // Use specific algorithm
248
+ * const result = solve(competitors, { algorithm: 'mip' });
249
+ *
250
+ * // Use auto-selection (runs all algorithms, returns best)
251
+ * const result = solve(competitors, { algorithm: 'auto' });
252
+ *
253
+ * // Use default algorithm (auto)
254
+ * const result = solve(competitors);
255
+ * ```
256
+ */
257
+ declare function solve(competitors: Competitor[], options?: Partial<PartitionOptions>): Promise<AlgorithmResult>;
258
+ /**
259
+ * Runs all algorithms and returns detailed results for comparison
260
+ *
261
+ * @param competitors - Array of competitor objects to partition
262
+ * @param options - Configuration options (algorithm option is ignored)
263
+ * @returns Object with best result and all algorithm results
264
+ *
265
+ * @example
266
+ * ```typescript
267
+ * const { bestResult, allResults } = compareAlgorithms(competitors);
268
+ * console.log('Best:', bestResult.algorithmUsed);
269
+ * allResults.forEach(r => console.log(`${r.algorithmUsed}: ${r.result.score}`));
270
+ * ```
271
+ */
272
+ declare function compareAlgorithms(competitors: Competitor[], options?: Partial<PartitionOptions>): Promise<{
273
+ bestResult: AlgorithmResult;
274
+ allResults: AlgorithmResult[];
275
+ }>;
276
+ declare const version = "0.1.0";
277
+
278
+ export { AGE_GROUPS, AGE_GROUP_DESCRIPTIONS, type AgeGroup, type Algorithm, type AlgorithmResult, type Competitor, DEFAULT_OPTIONS, EQUIPMENT_CLASSES, EQUIPMENT_CLASS_DESCRIPTIONS, type EquipmentClass, GENDERS, GENDER_DESCRIPTIONS, type Gender, MINIMUM_SUBSET_SIZE, type PartitionOptions, type PartitionResult, type SolutionSpace, calculateScore, compareAgeGroups, compareAlgorithms, createGuardianFamilies, createPartitionResult, getAgeGroupOrder, getGuardianGroups, isValidAgeGroup, isValidCompetitor, isValidEquipmentClass, isValidGender, retainLegalSubsets, smallestAllowedSubset, solve, solveClustering, solveMIP, solveMIPWithLP, validateCompetitors, validateGuardianConstraints, validatePartition, version };