volleyballsimtypes 0.0.429 → 0.0.432
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/dist/cjs/src/service/match/match-rating.d.ts +2 -4
- package/dist/cjs/src/service/match/match-rating.js +11 -21
- package/dist/cjs/src/service/match/match-rating.test.js +12 -14
- package/dist/esm/src/service/match/match-rating.d.ts +2 -4
- package/dist/esm/src/service/match/match-rating.js +11 -21
- package/dist/esm/src/service/match/match-rating.test.js +12 -14
- package/package.json +1 -1
|
@@ -17,16 +17,14 @@ export declare class MatchRating {
|
|
|
17
17
|
static readonly C4: number;
|
|
18
18
|
static readonly C5: number;
|
|
19
19
|
static readonly SSV: SetScoreVariant;
|
|
20
|
-
static readonly
|
|
21
|
-
static readonly
|
|
20
|
+
static readonly LEAGUE_WEIGHT: number;
|
|
21
|
+
static readonly NATIONAL_WEIGHT: number;
|
|
22
22
|
static readonly QUALIFIER_WEIGHT: number;
|
|
23
23
|
static readonly TOURNAMENT_WEIGHT: number;
|
|
24
|
-
static readonly NATIONAL_WEIGHT: number;
|
|
25
24
|
readonly match: Match;
|
|
26
25
|
readonly weight: number;
|
|
27
26
|
static create(input: unknown): MatchRating;
|
|
28
27
|
private constructor();
|
|
29
|
-
private static leagueWeight;
|
|
30
28
|
private static applyBotPenalty;
|
|
31
29
|
private static computeWeight;
|
|
32
30
|
static calculateProbability(z: number): number;
|
|
@@ -21,23 +21,16 @@ class MatchRating {
|
|
|
21
21
|
this.match = match;
|
|
22
22
|
this.weight = MatchRating.computeWeight(context);
|
|
23
23
|
}
|
|
24
|
-
static leagueWeight(tier) {
|
|
25
|
-
return Math.max(MatchRating.TIER_PENALTY, MatchRating.LEAGUE_BASE - MatchRating.TIER_PENALTY * tier);
|
|
26
|
-
}
|
|
27
24
|
static applyBotPenalty(weight, homeIsBot, awayIsBot) {
|
|
28
25
|
const bots = (homeIsBot ? 1 : 0) + (awayIsBot ? 1 : 0);
|
|
29
26
|
return weight * Math.pow(0.5, bots);
|
|
30
27
|
}
|
|
31
28
|
static computeWeight(context) {
|
|
32
29
|
switch (context.kind) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
case 'PROMOTION': {
|
|
38
|
-
const base = (MatchRating.leagueWeight(context.upperTier) + MatchRating.leagueWeight(context.lowerTier)) / 2;
|
|
39
|
-
return MatchRating.applyBotPenalty(base, context.homeIsBot, context.awayIsBot);
|
|
40
|
-
}
|
|
30
|
+
// Promotion plays at the league weight (it is a league-tier playoff).
|
|
31
|
+
case 'LEAGUE':
|
|
32
|
+
case 'PROMOTION':
|
|
33
|
+
return MatchRating.applyBotPenalty(MatchRating.LEAGUE_WEIGHT, context.homeIsBot, context.awayIsBot);
|
|
41
34
|
case 'QUALIFIER':
|
|
42
35
|
return MatchRating.applyBotPenalty(MatchRating.QUALIFIER_WEIGHT, context.homeIsBot, context.awayIsBot);
|
|
43
36
|
case 'TOURNAMENT':
|
|
@@ -89,13 +82,10 @@ MatchRating.SSV = {
|
|
|
89
82
|
'1-3': -1.5,
|
|
90
83
|
'0-3': -2
|
|
91
84
|
};
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
MatchRating.
|
|
96
|
-
|
|
97
|
-
MatchRating.QUALIFIER_WEIGHT =
|
|
98
|
-
// WCC
|
|
99
|
-
MatchRating.TOURNAMENT_WEIGHT = 55;
|
|
100
|
-
// National cup, between qualifier and tournament prestige
|
|
101
|
-
MatchRating.NATIONAL_WEIGHT = 50;
|
|
85
|
+
// FIVB tournament-importance weight: FIVB's "K" in S_after = S_before + K*(R-E)/8 (note the static `K`
|
|
86
|
+
// below is the formula's /8 divisor, NOT this). One flat value per competition, FIVB-style — no per-tier
|
|
87
|
+
// or per-stage scaling, since the rating-gap expected result already rewards beating stronger teams.
|
|
88
|
+
MatchRating.LEAGUE_WEIGHT = 20;
|
|
89
|
+
MatchRating.NATIONAL_WEIGHT = 30;
|
|
90
|
+
MatchRating.QUALIFIER_WEIGHT = 40;
|
|
91
|
+
MatchRating.TOURNAMENT_WEIGHT = 50; // WCC, top of the hierarchy
|
|
@@ -37,22 +37,21 @@ const number_utils_1 = require("../utils/number-utils");
|
|
|
37
37
|
const match = (0, test_helpers_1.makeMatch30)(home, away);
|
|
38
38
|
return match_rating_1.MatchRating.create({ match, context });
|
|
39
39
|
}
|
|
40
|
-
(0, globals_1.it)('LEAGUE
|
|
41
|
-
|
|
42
|
-
(0, globals_1.expect)(
|
|
40
|
+
(0, globals_1.it)('LEAGUE → flat LEAGUE_WEIGHT (20), independent of tier', () => {
|
|
41
|
+
(0, globals_1.expect)(makeRating({ kind: 'LEAGUE', tier: 1, homeIsBot: false, awayIsBot: false }).weight).toBe(20);
|
|
42
|
+
(0, globals_1.expect)(makeRating({ kind: 'LEAGUE', tier: 7, homeIsBot: false, awayIsBot: false }).weight).toBe(20);
|
|
43
43
|
});
|
|
44
|
-
(0, globals_1.it)('
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
(0, globals_1.expect)(mr.weight).toBe(5);
|
|
44
|
+
(0, globals_1.it)('NATIONAL → weight = NATIONAL_WEIGHT (30)', () => {
|
|
45
|
+
const mr = makeRating({ kind: 'NATIONAL', homeIsBot: false, awayIsBot: false });
|
|
46
|
+
(0, globals_1.expect)(mr.weight).toBe(30);
|
|
48
47
|
});
|
|
49
|
-
(0, globals_1.it)('QUALIFIER → weight = QUALIFIER_WEIGHT (
|
|
48
|
+
(0, globals_1.it)('QUALIFIER → weight = QUALIFIER_WEIGHT (40)', () => {
|
|
50
49
|
const mr = makeRating({ kind: 'QUALIFIER', homeIsBot: false, awayIsBot: false });
|
|
51
|
-
(0, globals_1.expect)(mr.weight).toBe(
|
|
50
|
+
(0, globals_1.expect)(mr.weight).toBe(40);
|
|
52
51
|
});
|
|
53
|
-
(0, globals_1.it)('TOURNAMENT → weight = TOURNAMENT_WEIGHT (
|
|
52
|
+
(0, globals_1.it)('TOURNAMENT → weight = TOURNAMENT_WEIGHT (50)', () => {
|
|
54
53
|
const mr = makeRating({ kind: 'TOURNAMENT', homeIsBot: false, awayIsBot: false });
|
|
55
|
-
(0, globals_1.expect)(mr.weight).toBe(
|
|
54
|
+
(0, globals_1.expect)(mr.weight).toBe(50);
|
|
56
55
|
});
|
|
57
56
|
(0, globals_1.it)('each bot halves the weight', () => {
|
|
58
57
|
const noBots = makeRating({ kind: 'QUALIFIER', homeIsBot: false, awayIsBot: false });
|
|
@@ -61,10 +60,9 @@ const number_utils_1 = require("../utils/number-utils");
|
|
|
61
60
|
(0, globals_1.expect)(oneBot.weight).toBeCloseTo(noBots.weight * 0.5, 10);
|
|
62
61
|
(0, globals_1.expect)(twoBots.weight).toBeCloseTo(noBots.weight * 0.25, 10);
|
|
63
62
|
});
|
|
64
|
-
(0, globals_1.it)('PROMOTION
|
|
65
|
-
// upperTier=1 (35), lowerTier=2 (30) → avg = 32.5
|
|
63
|
+
(0, globals_1.it)('PROMOTION → league weight (20)', () => {
|
|
66
64
|
const mr = makeRating({ kind: 'PROMOTION', upperTier: 1, lowerTier: 2, homeIsBot: false, awayIsBot: false });
|
|
67
|
-
(0, globals_1.expect)(mr.weight).
|
|
65
|
+
(0, globals_1.expect)(mr.weight).toBe(20);
|
|
68
66
|
});
|
|
69
67
|
});
|
|
70
68
|
// ─── getExpectedResult ────────────────────────────────────────────────────────
|
|
@@ -17,16 +17,14 @@ export declare class MatchRating {
|
|
|
17
17
|
static readonly C4: number;
|
|
18
18
|
static readonly C5: number;
|
|
19
19
|
static readonly SSV: SetScoreVariant;
|
|
20
|
-
static readonly
|
|
21
|
-
static readonly
|
|
20
|
+
static readonly LEAGUE_WEIGHT: number;
|
|
21
|
+
static readonly NATIONAL_WEIGHT: number;
|
|
22
22
|
static readonly QUALIFIER_WEIGHT: number;
|
|
23
23
|
static readonly TOURNAMENT_WEIGHT: number;
|
|
24
|
-
static readonly NATIONAL_WEIGHT: number;
|
|
25
24
|
readonly match: Match;
|
|
26
25
|
readonly weight: number;
|
|
27
26
|
static create(input: unknown): MatchRating;
|
|
28
27
|
private constructor();
|
|
29
|
-
private static leagueWeight;
|
|
30
28
|
private static applyBotPenalty;
|
|
31
29
|
private static computeWeight;
|
|
32
30
|
static calculateProbability(z: number): number;
|
|
@@ -18,23 +18,16 @@ export class MatchRating {
|
|
|
18
18
|
this.match = match;
|
|
19
19
|
this.weight = MatchRating.computeWeight(context);
|
|
20
20
|
}
|
|
21
|
-
static leagueWeight(tier) {
|
|
22
|
-
return Math.max(MatchRating.TIER_PENALTY, MatchRating.LEAGUE_BASE - MatchRating.TIER_PENALTY * tier);
|
|
23
|
-
}
|
|
24
21
|
static applyBotPenalty(weight, homeIsBot, awayIsBot) {
|
|
25
22
|
const bots = (homeIsBot ? 1 : 0) + (awayIsBot ? 1 : 0);
|
|
26
23
|
return weight * Math.pow(0.5, bots);
|
|
27
24
|
}
|
|
28
25
|
static computeWeight(context) {
|
|
29
26
|
switch (context.kind) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
case 'PROMOTION': {
|
|
35
|
-
const base = (MatchRating.leagueWeight(context.upperTier) + MatchRating.leagueWeight(context.lowerTier)) / 2;
|
|
36
|
-
return MatchRating.applyBotPenalty(base, context.homeIsBot, context.awayIsBot);
|
|
37
|
-
}
|
|
27
|
+
// Promotion plays at the league weight (it is a league-tier playoff).
|
|
28
|
+
case 'LEAGUE':
|
|
29
|
+
case 'PROMOTION':
|
|
30
|
+
return MatchRating.applyBotPenalty(MatchRating.LEAGUE_WEIGHT, context.homeIsBot, context.awayIsBot);
|
|
38
31
|
case 'QUALIFIER':
|
|
39
32
|
return MatchRating.applyBotPenalty(MatchRating.QUALIFIER_WEIGHT, context.homeIsBot, context.awayIsBot);
|
|
40
33
|
case 'TOURNAMENT':
|
|
@@ -85,13 +78,10 @@ MatchRating.SSV = {
|
|
|
85
78
|
'1-3': -1.5,
|
|
86
79
|
'0-3': -2
|
|
87
80
|
};
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
MatchRating.
|
|
92
|
-
|
|
93
|
-
MatchRating.QUALIFIER_WEIGHT =
|
|
94
|
-
// WCC
|
|
95
|
-
MatchRating.TOURNAMENT_WEIGHT = 55;
|
|
96
|
-
// National cup, between qualifier and tournament prestige
|
|
97
|
-
MatchRating.NATIONAL_WEIGHT = 50;
|
|
81
|
+
// FIVB tournament-importance weight: FIVB's "K" in S_after = S_before + K*(R-E)/8 (note the static `K`
|
|
82
|
+
// below is the formula's /8 divisor, NOT this). One flat value per competition, FIVB-style — no per-tier
|
|
83
|
+
// or per-stage scaling, since the rating-gap expected result already rewards beating stronger teams.
|
|
84
|
+
MatchRating.LEAGUE_WEIGHT = 20;
|
|
85
|
+
MatchRating.NATIONAL_WEIGHT = 30;
|
|
86
|
+
MatchRating.QUALIFIER_WEIGHT = 40;
|
|
87
|
+
MatchRating.TOURNAMENT_WEIGHT = 50; // WCC, top of the hierarchy
|
|
@@ -35,22 +35,21 @@ describe('MatchRating weight computation', () => {
|
|
|
35
35
|
const match = makeMatch30(home, away);
|
|
36
36
|
return MatchRating.create({ match, context });
|
|
37
37
|
}
|
|
38
|
-
it('LEAGUE
|
|
39
|
-
|
|
40
|
-
expect(
|
|
38
|
+
it('LEAGUE → flat LEAGUE_WEIGHT (20), independent of tier', () => {
|
|
39
|
+
expect(makeRating({ kind: 'LEAGUE', tier: 1, homeIsBot: false, awayIsBot: false }).weight).toBe(20);
|
|
40
|
+
expect(makeRating({ kind: 'LEAGUE', tier: 7, homeIsBot: false, awayIsBot: false }).weight).toBe(20);
|
|
41
41
|
});
|
|
42
|
-
it('
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
expect(mr.weight).toBe(5);
|
|
42
|
+
it('NATIONAL → weight = NATIONAL_WEIGHT (30)', () => {
|
|
43
|
+
const mr = makeRating({ kind: 'NATIONAL', homeIsBot: false, awayIsBot: false });
|
|
44
|
+
expect(mr.weight).toBe(30);
|
|
46
45
|
});
|
|
47
|
-
it('QUALIFIER → weight = QUALIFIER_WEIGHT (
|
|
46
|
+
it('QUALIFIER → weight = QUALIFIER_WEIGHT (40)', () => {
|
|
48
47
|
const mr = makeRating({ kind: 'QUALIFIER', homeIsBot: false, awayIsBot: false });
|
|
49
|
-
expect(mr.weight).toBe(
|
|
48
|
+
expect(mr.weight).toBe(40);
|
|
50
49
|
});
|
|
51
|
-
it('TOURNAMENT → weight = TOURNAMENT_WEIGHT (
|
|
50
|
+
it('TOURNAMENT → weight = TOURNAMENT_WEIGHT (50)', () => {
|
|
52
51
|
const mr = makeRating({ kind: 'TOURNAMENT', homeIsBot: false, awayIsBot: false });
|
|
53
|
-
expect(mr.weight).toBe(
|
|
52
|
+
expect(mr.weight).toBe(50);
|
|
54
53
|
});
|
|
55
54
|
it('each bot halves the weight', () => {
|
|
56
55
|
const noBots = makeRating({ kind: 'QUALIFIER', homeIsBot: false, awayIsBot: false });
|
|
@@ -59,10 +58,9 @@ describe('MatchRating weight computation', () => {
|
|
|
59
58
|
expect(oneBot.weight).toBeCloseTo(noBots.weight * 0.5, 10);
|
|
60
59
|
expect(twoBots.weight).toBeCloseTo(noBots.weight * 0.25, 10);
|
|
61
60
|
});
|
|
62
|
-
it('PROMOTION
|
|
63
|
-
// upperTier=1 (35), lowerTier=2 (30) → avg = 32.5
|
|
61
|
+
it('PROMOTION → league weight (20)', () => {
|
|
64
62
|
const mr = makeRating({ kind: 'PROMOTION', upperTier: 1, lowerTier: 2, homeIsBot: false, awayIsBot: false });
|
|
65
|
-
expect(mr.weight).
|
|
63
|
+
expect(mr.weight).toBe(20);
|
|
66
64
|
});
|
|
67
65
|
});
|
|
68
66
|
// ─── getExpectedResult ────────────────────────────────────────────────────────
|