@opprs/core 0.3.0 → 0.5.2-canary.760356d

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.
Files changed (2) hide show
  1. package/README.md +21 -649
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,25 +1,11 @@
1
- # OPPR - Open Pinball Player Ranking System
1
+ # @opprs/core
2
2
 
3
- A comprehensive TypeScript library for calculating pinball tournament rankings and player ratings. This library implements a complete ranking system with support for various tournament formats, player ratings, and point distribution calculations.
4
-
5
- ## Features
6
-
7
- - **Configurable Constants** - Override any calculation constant to customize the ranking system
8
- - **Base Value Calculation** - Tournament value based on number of rated players
9
- - **Tournament Value Adjustment (TVA)** - Strength indicators from player ratings and rankings
10
- - **Tournament Grading Percentage (TGP)** - Format quality assessment
11
- - **Event Boosters** - Multipliers for major championships and certified events
12
- - **Point Distribution** - Linear and dynamic point allocation
13
- - **Time Decay** - Automatic point depreciation over time
14
- - **Glicko Rating System** - Player skill rating with uncertainty
15
- - **Efficiency Tracking** - Performance metrics
16
- - **Input Validation** - Comprehensive data validation
17
- - **TypeScript First** - Full type safety and IntelliSense support
3
+ Core TypeScript library for the Open Pinball Player Ranking System (OPPRS). Provides all calculation functions for tournament rankings and player ratings.
18
4
 
19
5
  ## Installation
20
6
 
21
7
  ```bash
22
- npm install oppr
8
+ npm install @opprs/core
23
9
  ```
24
10
 
25
11
  ## Quick Start
@@ -35,31 +21,24 @@ import {
35
21
  type Player,
36
22
  type TGPConfig,
37
23
  type PlayerResult,
38
- } from 'oppr';
24
+ } from '@opprs/core';
39
25
 
40
- // Define your players
26
+ // Define players
41
27
  const players: Player[] = [
42
28
  { id: '1', rating: 1800, ranking: 1, isRated: true },
43
29
  { id: '2', rating: 1700, ranking: 5, isRated: true },
44
30
  { id: '3', rating: 1600, ranking: 10, isRated: true },
45
31
  ];
46
32
 
47
- // Calculate tournament value
33
+ // Calculate tournament value components
48
34
  const baseValue = calculateBaseValue(players);
49
35
  const ratingTVA = calculateRatingTVA(players);
50
36
  const rankingTVA = calculateRankingTVA(players);
51
37
 
52
38
  // Configure tournament format
53
39
  const tgpConfig: TGPConfig = {
54
- qualifying: {
55
- type: 'limited',
56
- meaningfulGames: 7,
57
- },
58
- finals: {
59
- formatType: 'match-play',
60
- meaningfulGames: 12,
61
- fourPlayerGroups: true, // PAPA-style 4-player groups
62
- },
40
+ qualifying: { type: 'limited', meaningfulGames: 7 },
41
+ finals: { formatType: 'match-play', meaningfulGames: 12, fourPlayerGroups: true },
63
42
  };
64
43
 
65
44
  const tgp = calculateTGP(tgpConfig);
@@ -68,7 +47,7 @@ const eventBooster = getEventBoosterMultiplier('none');
68
47
  // Calculate first place value
69
48
  const firstPlaceValue = (baseValue + ratingTVA + rankingTVA) * tgp * eventBooster;
70
49
 
71
- // Distribute points to players based on finishing positions
50
+ // Distribute points based on finishing positions
72
51
  const results: PlayerResult[] = [
73
52
  { player: players[0], position: 1 },
74
53
  { player: players[1], position: 2 },
@@ -76,630 +55,23 @@ const results: PlayerResult[] = [
76
55
  ];
77
56
 
78
57
  const distributions = distributePoints(results, firstPlaceValue);
79
-
80
- console.log(`First place gets: ${distributions[0].totalPoints.toFixed(2)} points`);
81
- ```
82
-
83
- ## Configuration
84
-
85
- The OPPR library allows you to configure all calculation constants to customize the ranking system for your specific needs. By default, the library uses the standard OPPR constants, but you can override any value globally.
86
-
87
- ### Basic Configuration
88
-
89
- ```typescript
90
- import { configureOPPR, calculateBaseValue } from 'oppr';
91
-
92
- // Configure specific constants
93
- configureOPPR({
94
- BASE_VALUE: {
95
- POINTS_PER_PLAYER: 1.0, // Override: changed from default 0.5
96
- MAX_BASE_VALUE: 64, // Override: changed from default 32
97
- },
98
- TIME_DECAY: {
99
- YEAR_1_TO_2: 0.8, // Override: changed from default 0.75
100
- },
101
- });
102
-
103
- // All function calls now use your configured values
104
- const baseValue = calculateBaseValue(players);
105
- ```
106
-
107
- ### Configuration Options
108
-
109
- You can configure any of the following constant groups:
110
-
111
- - `BASE_VALUE` - Tournament base value calculation
112
- - `TVA` - Tournament Value Adjustment (rating and ranking)
113
- - `TGP` - Tournament Grading Percentage
114
- - `EVENT_BOOSTERS` - Event multipliers
115
- - `POINT_DISTRIBUTION` - Point allocation
116
- - `TIME_DECAY` - Point depreciation
117
- - `RANKING` - Player ranking rules
118
- - `RATING` - Glicko rating system
119
- - `VALIDATION` - Input validation rules
120
-
121
- ### Partial Configuration
122
-
123
- You only need to specify the values you want to override. All other values will use the defaults:
124
-
125
- ```typescript
126
- import { configureOPPR } from 'oppr';
127
-
128
- // Only override specific nested values
129
- configureOPPR({
130
- TVA: {
131
- RATING: {
132
- MAX_VALUE: 30, // Only override this one value
133
- // All other TVA.RATING values use defaults
134
- },
135
- // TVA.RANKING uses all defaults
136
- },
137
- });
138
- ```
139
-
140
- ### Resetting Configuration
141
-
142
- ```typescript
143
- import { resetConfig } from 'oppr';
144
-
145
- // Reset all constants back to defaults
146
- resetConfig();
147
- ```
148
-
149
- ### Accessing Default Constants
150
-
151
- ```typescript
152
- import { getDefaultConfig, DEFAULT_CONSTANTS } from 'oppr';
153
-
154
- // Get the current defaults programmatically
155
- const defaults = getDefaultConfig();
156
- console.log(defaults.BASE_VALUE.POINTS_PER_PLAYER); // 0.5
157
-
158
- // Or access the constant object directly
159
- console.log(DEFAULT_CONSTANTS.BASE_VALUE.MAX_BASE_VALUE); // 32
160
- ```
161
-
162
- ### Configuration Examples
163
-
164
- #### Example 1: Higher Tournament Values
165
-
166
- ```typescript
167
- import { configureOPPR } from 'oppr';
168
-
169
- // Make tournaments worth more points
170
- configureOPPR({
171
- BASE_VALUE: {
172
- POINTS_PER_PLAYER: 1.0, // Double from 0.5
173
- MAX_BASE_VALUE: 64, // Double from 32
174
- },
175
- TVA: {
176
- RATING: {
177
- MAX_VALUE: 50, // Double from 25
178
- },
179
- RANKING: {
180
- MAX_VALUE: 100, // Double from 50
181
- },
182
- },
183
- });
184
- ```
185
-
186
- #### Example 2: Slower Time Decay
187
-
188
- ```typescript
189
- import { configureOPPR } from 'oppr';
190
-
191
- // Keep points valuable longer
192
- configureOPPR({
193
- TIME_DECAY: {
194
- YEAR_0_TO_1: 1.0, // Default
195
- YEAR_1_TO_2: 0.9, // Changed from 0.75
196
- YEAR_2_TO_3: 0.7, // Changed from 0.5
197
- YEAR_3_PLUS: 0.5, // Changed from 0.0
198
- },
199
- });
200
- ```
201
-
202
- #### Example 3: Different TGP Scaling
203
-
204
- ```typescript
205
- import { configureOPPR } from 'oppr';
206
-
207
- // Adjust TGP values
208
- configureOPPR({
209
- TGP: {
210
- BASE_GAME_VALUE: 0.05, // 5% per game instead of 4%
211
- MAX_WITHOUT_FINALS: 1.5, // 150% max instead of 100%
212
- MAX_WITH_FINALS: 2.5, // 250% max instead of 200%
213
- MULTIPLIERS: {
214
- FOUR_PLAYER_GROUPS: 2.5, // Higher multiplier
215
- },
216
- },
217
- });
218
- ```
219
-
220
- ### TypeScript Support
221
-
222
- The configuration system is fully typed. Your IDE will provide autocomplete and type checking:
223
-
224
- ```typescript
225
- import { configureOPPR, type PartialOPPRConfig } from 'oppr';
226
-
227
- const myConfig: PartialOPPRConfig = {
228
- BASE_VALUE: {
229
- POINTS_PER_PLAYER: 1.0,
230
- // TypeScript will show all available options
231
- // and catch any typos or invalid values
232
- },
233
- };
234
-
235
- configureOPPR(myConfig);
236
- ```
237
-
238
- ## Core Concepts
239
-
240
- ### Base Value
241
-
242
- The base value of a tournament is calculated based on the number of rated players (players with 5+ events):
243
-
244
- - **0.5 points per rated player**
245
- - **Maximum of 32 points** (achieved at 64+ rated players)
246
-
247
- ```typescript
248
- import { calculateBaseValue } from 'oppr';
249
-
250
- const baseValue = calculateBaseValue(players);
251
- ```
252
-
253
- ### Tournament Value Adjustment (TVA)
254
-
255
- TVA increases tournament value based on the strength of the field:
256
-
257
- #### Rating-based TVA
258
- - Uses Glicko ratings to assess player skill
259
- - Maximum contribution: **25 points**
260
- - Formula: `(rating * 0.000546875) - 0.703125`
261
-
262
- #### Ranking-based TVA
263
- - Uses world rankings to assess field strength
264
- - Maximum contribution: **50 points**
265
- - Formula: `ln(ranking) * -0.211675054 + 1.459827968`
266
-
267
- ```typescript
268
- import { calculateRatingTVA, calculateRankingTVA } from 'oppr';
269
-
270
- const ratingTVA = calculateRatingTVA(players);
271
- const rankingTVA = calculateRankingTVA(players);
272
- ```
273
-
274
- ### Tournament Grading Percentage (TGP)
275
-
276
- TGP measures the quality and completeness of a tournament format:
277
-
278
- - **Base value:** 4% per meaningful game
279
- - **Without separate qualifying:** Max 100%
280
- - **With qualifying and finals:** Max 200%
281
- - **Multipliers:**
282
- - 4-player groups: 2X
283
- - 3-player groups: 1.5X
284
- - Unlimited best game (20+ hours): 2X
285
- - Hybrid best game: 3X
286
- - Unlimited card qualifying: 4X
287
-
288
- ```typescript
289
- import { calculateTGP, type TGPConfig } from 'oppr';
290
-
291
- const tgpConfig: TGPConfig = {
292
- qualifying: {
293
- type: 'limited',
294
- meaningfulGames: 7,
295
- },
296
- finals: {
297
- formatType: 'match-play',
298
- meaningfulGames: 15,
299
- fourPlayerGroups: true,
300
- },
301
- };
302
-
303
- const tgp = calculateTGP(tgpConfig);
304
- ```
305
-
306
- ### Event Boosters
307
-
308
- Event boosters multiply the final tournament value:
309
-
310
- - **None:** 1.0X (100%)
311
- - **Certified:** 1.25X (125%)
312
- - **Certified+:** 1.5X (150%)
313
- - **Championship Series:** 1.5X (150%)
314
- - **Major:** 2.0X (200%)
315
-
316
- ```typescript
317
- import { getEventBoosterMultiplier } from 'oppr';
318
-
319
- const booster = getEventBoosterMultiplier('major'); // Returns 2.0
320
- ```
321
-
322
- ### Point Distribution
323
-
324
- Points are distributed using two components:
325
-
326
- 1. **Linear Distribution (10%):** Evenly distributed across positions
327
- 2. **Dynamic Distribution (90%):** Heavily weighted toward top finishers
328
-
329
- ```typescript
330
- import { distributePoints } from 'oppr';
331
-
332
- const distributions = distributePoints(results, firstPlaceValue);
333
- ```
334
-
335
- ### Time Decay
336
-
337
- Points decay over time to emphasize recent performance:
338
-
339
- - **0-1 years:** 100% value
340
- - **1-2 years:** 75% value
341
- - **2-3 years:** 50% value
342
- - **3+ years:** 0% value (inactive)
343
-
344
- ```typescript
345
- import { applyTimeDecay, isEventActive } from 'oppr';
346
-
347
- const eventDate = new Date('2023-01-01');
348
- const decayedPoints = applyTimeDecay(100, eventDate);
349
- const active = isEventActive(eventDate);
350
- ```
351
-
352
- ### Glicko Rating System
353
-
354
- Player ratings use the Glicko system with rating deviation (uncertainty):
355
-
356
- - **Default rating:** 1300
357
- - **Rating deviation (RD):** 10-200
358
- - **RD decay:** ~0.3 per day of inactivity
359
-
360
- ```typescript
361
- import { updateRating, type RatingUpdate } from 'oppr';
362
-
363
- const update: RatingUpdate = {
364
- currentRating: 1500,
365
- currentRD: 100,
366
- results: [
367
- { opponentRating: 1600, opponentRD: 80, score: 1 }, // Win
368
- { opponentRating: 1550, opponentRD: 90, score: 0 }, // Loss
369
- ],
370
- };
371
-
372
- const { newRating, newRD } = updateRating(update);
373
- ```
374
-
375
- ## Constants and Calibration Rationale
376
-
377
- This section explains why each constant in the system has its current value. The constants are carefully calibrated to create a balanced, mathematically sound ranking system.
378
-
379
- ### Design Philosophy
380
-
381
- Most constants are chosen to:
382
- 1. **Cap maximum contributions** at specific thresholds
383
- 2. **Create mathematical relationships** between different components
384
- 3. **Follow established rating systems** (like Glicko)
385
- 4. **Reflect real-world competitive difficulty**
386
-
387
- Many constants are interdependent - changing one often requires adjusting others to maintain system balance.
388
-
389
- ### Base Value Constants
390
-
391
- | Constant | Value | Rationale |
392
- |----------|-------|-----------|
393
- | `POINTS_PER_PLAYER` | `0.5` | Chosen so 64 rated players yields exactly 32 points (0.5 × 64 = 32) |
394
- | `MAX_BASE_VALUE` | `32` | Reasonable cap for tournament base value |
395
- | `MAX_PLAYER_COUNT` | `64` | Player count where max is reached (32 ÷ 0.5 = 64) |
396
- | `RATED_PLAYER_THRESHOLD` | `5` | Five events provides sufficient history to be "rated" |
397
-
398
- **Key Insight:** The 0.5 coefficient creates perfect linear scaling where the cap is reached at a reasonable tournament size.
399
-
400
- ### Tournament Value Adjustment (TVA) Constants
401
-
402
- #### Rating-based TVA
403
-
404
- | Constant | Value | Rationale |
405
- |----------|-------|-----------|
406
- | `MAX_VALUE` | `25` | Ensures rating TVA contributes 1/3 of the 75-point total TVA cap |
407
- | `COEFFICIENT` | `0.000546875` | Reverse-engineered so 64 players rated 2000 contribute exactly 25 points |
408
- | `OFFSET` | `0.703125` | Paired with coefficient: `(2000 × 0.000546875) - 0.703125 ≈ 0.39` per player |
409
- | `PERFECT_RATING` | `2000` | Reference rating for "perfect" player |
410
- | `MIN_EFFECTIVE_RATING` | `1285.71` | Where formula crosses zero: `(1285.71 × 0.000546875) - 0.703125 ≈ 0` |
411
-
412
- **Formula:** `(rating * 0.000546875) - 0.703125`
413
-
414
- The coefficients ensure 64 perfect players contribute exactly 25 points: 64 × 0.39 ≈ 25 ✓
415
-
416
- #### Ranking-based TVA
417
-
418
- | Constant | Value | Rationale |
419
- |----------|-------|-----------|
420
- | `MAX_VALUE` | `50` | Largest component of TVA (2/3 of 75-point cap) |
421
- | `COEFFICIENT` | `-0.211675054` | Calibrated so top 64 ranked players sum to exactly 50 points |
422
- | `OFFSET` | `1.459827968` | Creates logarithmic decay favoring top-ranked players |
423
-
424
- **Formula:** `ln(ranking) * -0.211675054 + 1.459827968`
425
-
426
- - Rank #1: ~1.46 points
427
- - Rank #2: ~1.31 points
428
- - Sum of ranks 1-64: ~50 points
429
-
430
- **Key Insight:** The logarithmic formula heavily rewards top-ranked players, while the rating formula is more linear.
431
-
432
- #### General TVA
433
-
434
- | Constant | Value | Rationale |
435
- |----------|-------|-----------|
436
- | `MAX_PLAYERS_CONSIDERED` | `64` | Limits calculation scope; prevents diminishing returns from large fields |
437
-
438
- ### TGP (Tournament Grading Percentage) Constants
439
-
440
- #### Base Values
441
-
442
- | Constant | Value | Rationale |
443
- |----------|-------|-----------|
444
- | `BASE_GAME_VALUE` | `0.04` (4%) | Base unit chosen so 25 meaningful games = 100% TGP |
445
- | `MAX_WITHOUT_FINALS` | `1.0` (100%) | Standard cap for simple tournaments |
446
- | `MAX_WITH_FINALS` | `2.0` (200%) | Allows qualifying + finals to each contribute up to 100% |
447
- | `MAX_GAMES_FOR_200_PERCENT` | `50` | 50 games × 4% = 200% (matches the math) |
448
-
449
- #### Format Multipliers
450
-
451
- These reflect **competitive difficulty**:
452
-
453
- | Format | Multiplier | Effective % | Rationale |
454
- |--------|------------|-------------|-----------|
455
- | Four-player groups | `2.0` | 8% per game | Most competitive format (PAPA-style) |
456
- | Three-player groups | `1.5` | 6% per game | Less competitive than 4-player |
457
- | Unlimited best game | `2.0` | 8% per game | Requires 20+ hours of qualifying |
458
- | Hybrid best game | `3.0` | 12% per game | Combines multiple competitive elements |
459
- | Unlimited card | `4.0` | 16% per game | Highest difficulty: unlimited practice + card format |
460
-
461
- **Key Insight:** Higher multipliers = harder formats = more TGP value per game
462
-
463
- #### Ball Count Adjustments
464
-
465
- | Ball Count | Multiplier | Rationale |
466
- |------------|------------|-----------|
467
- | 1-ball | `0.33` (33%) | Less meaningful competition than standard 3-ball |
468
- | 2-ball | `0.66` (66%) | Linear scaling between 1 and 3-ball |
469
- | 3+ ball | `1.0` (100%) | Standard competitive format |
470
-
471
- #### Unlimited Qualifying
472
-
473
- | Constant | Value | Rationale |
474
- |----------|-------|-----------|
475
- | `PERCENT_PER_HOUR` | `0.01` (1%) | Rewards longer qualifying periods |
476
- | `MAX_BONUS` | `0.2` (20%) | Caps at 20% bonus (achieved at 20 hours) |
477
- | `MIN_HOURS_FOR_MULTIPLIER` | `20` | Must run 20+ hours to qualify for format multipliers |
478
-
479
- #### Finals Requirements
480
-
481
- | Constant | Value | Rationale |
482
- |----------|-------|-----------|
483
- | `MIN_FINALISTS_PERCENT` | `0.1` (10%) | At least 10% must advance to ensure finals are meaningful |
484
- | `MAX_FINALISTS_PERCENT` | `0.5` (50%) | Maximum 50% prevents finals from being too inclusive |
485
-
486
- ### Event Booster Constants
487
-
488
- | Booster Type | Multiplier | Rationale |
489
- |--------------|------------|-----------|
490
- | None | `1.0` (100%) | Standard events, no adjustment |
491
- | Certified | `1.25` (125%) | 25% boost for meeting certification requirements (24+ finalists, valid format) |
492
- | Certified+ | `1.5` (150%) | 50% boost requires 128+ rated players |
493
- | Championship Series | `1.5` (150%) | Same as Certified+ for series events |
494
- | Major | `2.0` (200%) | 100% boost doubles the value of major championships |
495
-
496
- **Key Insight:** These create tiers that incentivize higher-quality tournaments.
497
-
498
- ### Point Distribution Constants
499
-
500
- | Constant | Value | Rationale |
501
- |----------|-------|-----------|
502
- | `LINEAR_PERCENTAGE` | `0.1` (10%) | Everyone gets some points |
503
- | `DYNAMIC_PERCENTAGE` | `0.9` (90%) | Heavily rewards top finishers |
504
- | `POSITION_EXPONENT` | `0.7` | Creates curve less steep than linear but more aggressive than logarithmic |
505
- | `VALUE_EXPONENT` | `3` | Cubic function creates exponential decay from 1st to last place |
506
- | `MAX_DYNAMIC_PLAYERS` | `64` | Caps denominator so small tournaments don't over-penalize lower finishers |
507
-
508
- **Formula:**
509
- ```typescript
510
- power((1 - power(((Position - 1) / min(RatedPlayerCount/2, 64)), 0.7)), 3) * 0.9 * FirstPlaceValue
511
58
  ```
512
59
 
513
- **Key Insight:** The 10/90 split ensures everyone gets participation points while creating significant reward for top performance. The exponents were tuned empirically to create a "fair" distribution curve.
514
-
515
- ### Time Decay Constants
516
-
517
- | Time Period | Multiplier | Rationale |
518
- |-------------|------------|-----------|
519
- | 0-1 years | `1.0` (100%) | Recent performance at full value |
520
- | 1-2 years | `0.75` (75%) | 25% annual decay begins |
521
- | 2-3 years | `0.5` (50%) | Continues progressive decay |
522
- | 3+ years | `0.0` (0%) | Complete removal after 3 years |
523
-
524
- | Constant | Value | Rationale |
525
- |----------|-------|-----------|
526
- | `DAYS_PER_YEAR` | `365` | Standard year length for calculations |
527
-
528
- **Key Insight:** The 3-year window and 25% annual decay steps are standard in ranking systems, emphasizing recent performance while gradually phasing out older results.
529
-
530
- ### Ranking System Constants
531
-
532
- | Constant | Value | Rationale |
533
- |----------|-------|-----------|
534
- | `TOP_EVENTS_COUNT` | `15` | Top 15 events count toward ranking (similar to IFPA) |
535
- | `ENTRY_RANKING_PERCENTILE` | `0.1` (10th) | New players start at 10th percentile (reasonable pessimistic assumption) |
536
-
537
- ### Glicko Rating System Constants
538
-
539
- | Constant | Value | Rationale |
540
- |----------|-------|-----------|
541
- | `DEFAULT_RATING` | `1300` | Standard Glicko starting rating (slightly below average) |
542
- | `MIN_RD` | `10` | Minimum uncertainty for highly active players |
543
- | `MAX_RD` | `200` | Maximum uncertainty (new/inactive players) |
544
- | `RD_DECAY_PER_DAY` | `0.3` | ~90 days of inactivity returns to max uncertainty (0.3 × 300 ≈ 90) |
545
- | `OPPONENTS_RANGE` | `32` | Limits calculation to 32 players above/below (performance optimization) |
546
- | `Q` | `Math.LN10 / 400` | **Mathematical constant from Glicko formula** (≈ 0.00575646) |
547
-
548
- **Key Insight:** These are standard Glicko parameters based on Mark Glickman's research, not arbitrary choices. The Q value is a mathematical constant: `ln(10) / 400`.
549
-
550
- ### Validation Constants
551
-
552
- | Constant | Value | Rationale |
553
- |----------|-------|-----------|
554
- | `MIN_PLAYERS` | `3` | Absolute minimum for competitive validity |
555
- | `MIN_PRIVATE_PLAYERS` | `16` | Higher bar for private tournaments |
556
- | `MAX_GAMES_PER_MACHINE` | `3` | Prevents over-reliance on single machines |
557
- | `MIN_PARTICIPATION_PERCENT` | `0.5` (50%) | Data quality threshold for including results |
558
-
559
- ### Mathematical Interdependencies
560
-
561
- Several constants are **mathematically linked**:
562
-
563
- 1. **Base Value:** `0.5 × 64 = 32` (points per player × max players = max value)
564
- 2. **Rating TVA:** Coefficients ensure 64 perfect players = 25 points
565
- 3. **Ranking TVA:** Logarithmic coefficients ensure top 64 = 50 points
566
- 4. **TGP:** `0.04 × 50 = 2.0` (base value × max games = max TGP)
567
- 5. **Glicko Q:** `ln(10) / 400` is a mathematical constant, not arbitrary
568
-
569
- **Warning:** The system is highly calibrated. Changing one constant often requires adjusting others to maintain balance.
570
-
571
- ### Summary
572
-
573
- Most constants fall into three categories:
574
-
575
- 1. **Mathematical calibrations** (TVA coefficients, Glicko Q) - Derived from formulas
576
- 2. **Empirical balance tuning** (TGP multipliers, point distribution exponents) - Adjusted to feel "fair"
577
- 3. **Standard values** (Glicko defaults, 3-year decay) - Industry best practices
578
-
579
- Together, these constants create a comprehensive ranking system where tournament value scales appropriately with field strength, format difficulty, and competitive level.
580
-
581
- ## API Reference
582
-
583
- ### Types
584
-
585
- ```typescript
586
- interface Player {
587
- id: string;
588
- rating: number;
589
- ranking: number;
590
- isRated: boolean;
591
- ratingDeviation?: number;
592
- eventCount?: number;
593
- }
594
-
595
- interface TGPConfig {
596
- qualifying: {
597
- type: 'unlimited' | 'limited' | 'hybrid' | 'none';
598
- meaningfulGames: number;
599
- hours?: number;
600
- fourPlayerGroups?: boolean;
601
- threePlayerGroups?: boolean;
602
- multiMatchplay?: boolean;
603
- };
604
- finals: {
605
- formatType: TournamentFormatType;
606
- meaningfulGames: number;
607
- fourPlayerGroups?: boolean;
608
- threePlayerGroups?: boolean;
609
- };
610
- ballCountAdjustment?: number;
611
- }
612
-
613
- interface PlayerResult {
614
- player: Player;
615
- position: number;
616
- optedOut?: boolean;
617
- }
618
-
619
- interface PointDistribution {
620
- player: Player;
621
- position: number;
622
- linearPoints: number;
623
- dynamicPoints: number;
624
- totalPoints: number;
625
- }
626
- ```
627
-
628
- ### Functions
629
-
630
- #### Base Value
631
- - `calculateBaseValue(players: Player[]): number`
632
- - `countRatedPlayers(players: Player[]): number`
633
- - `isPlayerRated(eventCount: number): boolean`
634
-
635
- #### TVA
636
- - `calculateRatingTVA(players: Player[]): number`
637
- - `calculateRankingTVA(players: Player[]): number`
638
- - `calculateTotalTVA(players: Player[]): { ratingTVA, rankingTVA, totalTVA }`
639
-
640
- #### TGP
641
- - `calculateTGP(config: TGPConfig): number`
642
- - `calculateQualifyingTGP(config: TGPConfig): number`
643
- - `calculateFinalsTGP(config: TGPConfig): number`
644
-
645
- #### Event Boosters
646
- - `getEventBoosterMultiplier(type: EventBoosterType): number`
647
- - `qualifiesForCertified(...): boolean`
648
- - `qualifiesForCertifiedPlus(...): boolean`
649
-
650
- #### Point Distribution
651
- - `distributePoints(results: PlayerResult[], firstPlaceValue: number): PointDistribution[]`
652
- - `calculatePlayerPoints(position, playerCount, ratedPlayerCount, firstPlaceValue): number`
653
-
654
- #### Time Decay
655
- - `applyTimeDecay(points: number, eventDate: Date): number`
656
- - `isEventActive(eventDate: Date): boolean`
657
- - `getDecayMultiplier(ageInYears: number): number`
658
-
659
- #### Rating
660
- - `updateRating(update: RatingUpdate): RatingResult`
661
- - `simulateTournamentMatches(position, results): MatchResult[]`
662
-
663
- #### Efficiency
664
- - `calculateOverallEfficiency(events: PlayerEvent[]): number`
665
- - `getEfficiencyStats(events: PlayerEvent[]): EfficiencyStats`
666
-
667
- ## Development
668
-
669
- ```bash
670
- # Install dependencies
671
- npm install
672
-
673
- # Run tests
674
- npm test
675
-
676
- # Run tests with coverage
677
- npm run test:coverage
678
-
679
- # Build the library
680
- npm run build
60
+ ## Features
681
61
 
682
- # Lint and format
683
- npm run lint
684
- npm run format
685
- ```
62
+ - **Base Value** - Tournament value based on rated player count
63
+ - **TVA (Tournament Value Adjustment)** - Field strength from ratings and rankings
64
+ - **TGP (Tournament Grading Percentage)** - Format quality assessment
65
+ - **Event Boosters** - Multipliers for majors and championships
66
+ - **Point Distribution** - Linear and dynamic point allocation
67
+ - **Time Decay** - Point depreciation over time
68
+ - **Glicko Ratings** - Player skill ratings with uncertainty
69
+ - **Configuration** - Override any calculation constant via `configureOPPR()`
686
70
 
687
- ## Testing
71
+ ## Documentation
688
72
 
689
- The library includes comprehensive unit and integration tests with 95%+ coverage:
690
-
691
- ```bash
692
- npm run test:coverage
693
- ```
73
+ For detailed documentation including API reference, configuration options, and calibration rationale, see the [OPPRS Documentation](https://thatguyinabeanie.github.io/OPPR/).
694
74
 
695
75
  ## License
696
76
 
697
- MIT License - see LICENSE file for details
698
-
699
- ## Contributing
700
-
701
- Contributions are welcome! Please ensure all tests pass and maintain the existing code style.
702
-
703
- ## Acknowledgments
704
-
705
- This library implements a ranking system based on tournament ranking principles for competitive pinball events.
77
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opprs/core",
3
- "version": "0.3.0",
3
+ "version": "0.5.2-canary.760356d",
4
4
  "description": "Open Pinball Player Ranking System - A TypeScript library for calculating pinball tournament rankings and ratings",
5
5
  "keywords": [
6
6
  "pinball",