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.
- package/LICENSE +28 -0
- package/README.md +335 -2
- package/dist/index.d.mts +278 -0
- package/dist/index.d.ts +278 -0
- package/dist/index.js +1647 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1599 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +66 -4
package/dist/index.d.ts
ADDED
|
@@ -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 };
|