@voltro/plugin-leaderboards 0.0.0-stage → 0.87.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/CHANGELOG.md +24329 -0
- package/LICENSE +61 -0
- package/README.md +1087 -2
- package/SECURITY.md +56 -0
- package/THIRD-PARTY-NOTICES.md +5007 -0
- package/dist/administration.js +5 -0
- package/dist/bracketAdmission.js +294 -0
- package/dist/bracketAdmissionPolicy.js +22 -0
- package/dist/bracketInstallation.js +133 -0
- package/dist/bracketMatchPorts.js +5 -0
- package/dist/bracketMatches.js +546 -0
- package/dist/brackets.d.ts +149 -0
- package/dist/brackets.js +379 -0
- package/dist/bucketStore.js +93 -0
- package/dist/buckets.js +9 -0
- package/dist/errors.d.ts +13 -0
- package/dist/errors.js +17 -0
- package/dist/glicko2.js +72 -0
- package/dist/index.d.ts +1208 -0
- package/dist/index.js +15 -0
- package/dist/inspect.js +84 -0
- package/dist/json.js +39 -0
- package/dist/leaderboard.d.ts +49 -0
- package/dist/leaderboard.js +57 -0
- package/dist/leagueSchedule.js +40 -0
- package/dist/leagues.js +31 -0
- package/dist/openskill.js +74 -0
- package/dist/plugin.js +338 -0
- package/dist/portLifetime.js +67 -0
- package/dist/ports.d.ts +294 -0
- package/dist/ports.js +7 -0
- package/dist/rankQuery.js +24 -0
- package/dist/ranking.d.ts +42 -0
- package/dist/ranking.js +71 -0
- package/dist/rating.d.ts +103 -0
- package/dist/rating.js +50 -0
- package/dist/ratingPools.js +134 -0
- package/dist/ratingStore.js +334 -0
- package/dist/ratingStrategies.js +108 -0
- package/dist/records.d.ts +116 -0
- package/dist/records.js +88 -0
- package/dist/rpc.d.ts +321 -0
- package/dist/rpc.js +306 -0
- package/dist/score.d.ts +13 -0
- package/dist/score.js +27 -0
- package/dist/server.d.ts +1477 -0
- package/dist/server.js +17 -0
- package/dist/service.js +949 -0
- package/dist/subjectFences.js +44 -0
- package/dist/tables.entity.js +188 -0
- package/dist/testing.d.ts +872 -0
- package/dist/testing.js +368 -0
- package/dist/tournament-app/tournament.advance.workflow.server.tsx +3 -0
- package/dist/tournament-app/tournament.advance.workflow.tsx +12 -0
- package/dist/tournament-app/tournament.stage.workflow.server.tsx +3 -0
- package/dist/tournament-app/tournament.stage.workflow.tsx +10 -0
- package/dist/tournamentInstallation.js +168 -0
- package/dist/tournamentPrizes.js +34 -0
- package/dist/tournamentStage.js +151 -0
- package/dist/tournamentStagePorts.js +5 -0
- package/dist/tournamentWorkflow.js +101 -0
- package/dist/tournamentWorkflowSchemas.js +54 -0
- package/dist/tournaments.d.ts +32 -0
- package/dist/tournaments.js +21 -0
- package/dist/web.d.ts +175 -0
- package/dist/web.js +17 -0
- package/package.json +113 -4
package/dist/ports.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { TournamentBracketPort as e } from "./bracketMatchPorts.js";
|
|
2
|
+
import { TournamentStagePort as t } from "./tournamentStagePorts.js";
|
|
3
|
+
import { Context as n } from "effect";
|
|
4
|
+
//#region src/bracketPorts.ts
|
|
5
|
+
var r = class extends n.Service()("@voltro/BracketAdmissionPort") {};
|
|
6
|
+
//#endregion
|
|
7
|
+
export { r as BracketAdmissionPort, e as TournamentBracketPort, t as TournamentStagePort };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { LeaderboardError as e, leaderboardError as t } from "./errors.js";
|
|
2
|
+
import { rankLeaderboardRecords as n } from "./ranking.js";
|
|
3
|
+
import { Effect as r } from "effect";
|
|
4
|
+
//#region src/rankQuery.ts
|
|
5
|
+
var i = (i) => r.try({
|
|
6
|
+
try: () => Object.freeze(n(i.records, {
|
|
7
|
+
sort: i.sort,
|
|
8
|
+
maxRecords: i.maxRecords,
|
|
9
|
+
tiebreak: i.tiebreak
|
|
10
|
+
}).map((e) => e.id)),
|
|
11
|
+
catch: (n) => n instanceof e ? n : t("unavailable")
|
|
12
|
+
}), a = (e, n) => {
|
|
13
|
+
if (!Array.isArray(e) || e.length !== n.length) throw t("invalid_record");
|
|
14
|
+
let r = new Set(n.map((e) => e.id)), i = /* @__PURE__ */ new Set(), a = [];
|
|
15
|
+
if (r.size !== n.length) throw t("invalid_record");
|
|
16
|
+
for (let n = 0; n < e.length; n++) {
|
|
17
|
+
let o = Object.getOwnPropertyDescriptor(e, String(n));
|
|
18
|
+
if (!o || !("value" in o) || typeof o.value != "string" || !r.has(o.value) || i.has(o.value)) throw t("invalid_record");
|
|
19
|
+
i.add(o.value), a.push(o.value);
|
|
20
|
+
}
|
|
21
|
+
return Object.freeze(a);
|
|
22
|
+
};
|
|
23
|
+
//#endregion
|
|
24
|
+
export { a as admitLeaderboardRankOrder, i as defaultLeaderboardRankQuery };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Equal primary score: higher subscore, then earlier submission, then binary owner/record ID. */
|
|
2
|
+
export declare const defaultLeaderboardTiebreak: LeaderboardTiebreak;
|
|
3
|
+
|
|
4
|
+
export declare const defaultPeriodSummary: (records: ReadonlyArray<RankedLeaderboardRecord>, top?: number) => Readonly<{
|
|
5
|
+
participants: number;
|
|
6
|
+
top: readonly RankedLeaderboardRecord[];
|
|
7
|
+
}>;
|
|
8
|
+
|
|
9
|
+
/** Stable FNV-1a UTF-8 partition helper. Does not enforce fixed-size cohort admission. */
|
|
10
|
+
export declare const hashBucket: (tenantId: string, periodId: string, ownerId: string, bucketCount: number) => number;
|
|
11
|
+
|
|
12
|
+
export declare const leaderboardAround: (records: ReadonlyArray<RankedLeaderboardRecord>, ownerId: string, radius?: number) => ReadonlyArray<RankedLeaderboardRecord>;
|
|
13
|
+
|
|
14
|
+
export declare interface LeaderboardRankRecord {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
readonly tenantId: string;
|
|
17
|
+
readonly board: string;
|
|
18
|
+
readonly periodId: string;
|
|
19
|
+
readonly ownerId: string;
|
|
20
|
+
readonly score: string;
|
|
21
|
+
readonly subscore: string;
|
|
22
|
+
readonly updatedAt: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
declare type LeaderboardSort = 'asc' | 'desc';
|
|
26
|
+
|
|
27
|
+
export declare type LeaderboardTiebreak = (left: LeaderboardRankRecord, right: LeaderboardRankRecord) => number;
|
|
28
|
+
|
|
29
|
+
export declare interface RankedLeaderboardRecord extends LeaderboardRankRecord {
|
|
30
|
+
readonly rank: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Whole supplied board/period snapshot. No SQL/window-query or million-row capacity claim. */
|
|
34
|
+
export declare const rankLeaderboardRecords: (input: ReadonlyArray<LeaderboardRankRecord>, options?: RankOptions) => ReadonlyArray<RankedLeaderboardRecord>;
|
|
35
|
+
|
|
36
|
+
export declare interface RankOptions {
|
|
37
|
+
readonly sort?: LeaderboardSort;
|
|
38
|
+
readonly tiebreak?: LeaderboardTiebreak;
|
|
39
|
+
readonly maxRecords?: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export { }
|
package/dist/ranking.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { leaderboardError as e } from "./errors.js";
|
|
2
|
+
import { leaderboardIdentifier as t } from "./leaderboard.js";
|
|
3
|
+
import { scoreString as n, scoreValue as r } from "./score.js";
|
|
4
|
+
//#region src/ranking.ts
|
|
5
|
+
var i = (e, t) => e < t ? -1 : +(e > t), a = (e, t) => {
|
|
6
|
+
let n = r(e.subscore), a = r(t.subscore);
|
|
7
|
+
return n > a ? -1 : n < a ? 1 : e.updatedAt - t.updatedAt || i(e.ownerId, t.ownerId) || i(e.id, t.id);
|
|
8
|
+
}, o = (o, s = {}) => {
|
|
9
|
+
let c = s.sort ?? "desc", l = s.tiebreak ?? a, u = s.maxRecords ?? 1e4;
|
|
10
|
+
if (!Array.isArray(o) || !Number.isSafeInteger(u) || u < 1 || u > 1e5 || o.length > u || !["asc", "desc"].includes(c) || typeof l != "function") throw e("invalid_input");
|
|
11
|
+
for (let t = 0; t < o.length; t++) if (!Object.hasOwn(o, t)) throw e("invalid_record");
|
|
12
|
+
let d = /* @__PURE__ */ new Set(), f = /* @__PURE__ */ new Set(), p = o[0], m = o.map((r) => {
|
|
13
|
+
if (!r || ![
|
|
14
|
+
"id",
|
|
15
|
+
"tenantId",
|
|
16
|
+
"board",
|
|
17
|
+
"periodId",
|
|
18
|
+
"ownerId"
|
|
19
|
+
].every((e) => t(r[e])) || p && (r.tenantId !== p.tenantId || r.board !== p.board || r.periodId !== p.periodId) || !Number.isSafeInteger(r.updatedAt) || r.updatedAt < 0 || r.updatedAt > 864e13 || d.has(r.id) || f.has(r.ownerId)) throw e("invalid_record");
|
|
20
|
+
return d.add(r.id), f.add(r.ownerId), Object.freeze({
|
|
21
|
+
id: r.id,
|
|
22
|
+
tenantId: r.tenantId,
|
|
23
|
+
board: r.board,
|
|
24
|
+
periodId: r.periodId,
|
|
25
|
+
ownerId: r.ownerId,
|
|
26
|
+
score: n(r.score),
|
|
27
|
+
subscore: n(r.subscore),
|
|
28
|
+
updatedAt: r.updatedAt
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
return m.sort((t, n) => {
|
|
32
|
+
let a = r(t.score), o = r(n.score);
|
|
33
|
+
if (a !== o) return c === "desc" ? a > o ? -1 : 1 : a < o ? -1 : 1;
|
|
34
|
+
let s;
|
|
35
|
+
try {
|
|
36
|
+
s = l(t, n);
|
|
37
|
+
} catch {
|
|
38
|
+
throw e("invalid_record");
|
|
39
|
+
}
|
|
40
|
+
if (!Number.isFinite(s)) throw e("invalid_record");
|
|
41
|
+
return s || i(t.ownerId, n.ownerId) || i(t.id, n.id);
|
|
42
|
+
}), Object.freeze(m.map((e, t) => Object.freeze({
|
|
43
|
+
...e,
|
|
44
|
+
rank: t + 1
|
|
45
|
+
})));
|
|
46
|
+
}, s = (n, r, i = 5) => {
|
|
47
|
+
if (!t(r) || !Number.isSafeInteger(i) || i < 0 || i > 500) throw e("invalid_input");
|
|
48
|
+
let a = n.findIndex((e) => e.ownerId === r);
|
|
49
|
+
return Object.freeze(a < 0 ? [] : n.slice(Math.max(0, a - i), a + i + 1));
|
|
50
|
+
}, c = (t, n = 10) => {
|
|
51
|
+
if (!Number.isSafeInteger(n) || n < 0 || n > 1e3) throw e("invalid_input");
|
|
52
|
+
return Object.freeze({
|
|
53
|
+
participants: t.length,
|
|
54
|
+
top: Object.freeze(t.slice(0, n))
|
|
55
|
+
});
|
|
56
|
+
}, l = (n, r, i, a) => {
|
|
57
|
+
if (![
|
|
58
|
+
n,
|
|
59
|
+
r,
|
|
60
|
+
i
|
|
61
|
+
].every(t) || !Number.isSafeInteger(a) || a < 1 || a > 1e6) throw e("invalid_input");
|
|
62
|
+
let o = 14695981039346656037n;
|
|
63
|
+
for (let e of new TextEncoder().encode(JSON.stringify([
|
|
64
|
+
n,
|
|
65
|
+
r,
|
|
66
|
+
i
|
|
67
|
+
]))) o = BigInt.asUintN(64, (o ^ BigInt(e)) * 1099511628211n);
|
|
68
|
+
return Number(o % BigInt(a));
|
|
69
|
+
};
|
|
70
|
+
//#endregion
|
|
71
|
+
export { a as defaultLeaderboardTiebreak, c as defaultPeriodSummary, l as hashBucket, s as leaderboardAround, o as rankLeaderboardRecords };
|
package/dist/rating.d.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/** Pairwise mean Elo; each team's delta is split by participation weight. */
|
|
2
|
+
export declare const elo: (options?: EloOptions) => EloRate;
|
|
3
|
+
|
|
4
|
+
export declare interface EloInput {
|
|
5
|
+
readonly teams: ReadonlyArray<ReadonlyArray<EloPlayer>>;
|
|
6
|
+
readonly ranks: ReadonlyArray<number>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export declare interface EloOptions {
|
|
10
|
+
readonly k?: number;
|
|
11
|
+
readonly scale?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export declare interface EloPlayer {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
readonly rating: number;
|
|
17
|
+
readonly weight?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export declare type EloRate = (input: EloInput) => ReadonlyArray<ReadonlyArray<EloResult>>;
|
|
21
|
+
|
|
22
|
+
export declare interface EloResult {
|
|
23
|
+
readonly id: string;
|
|
24
|
+
readonly previous: number;
|
|
25
|
+
readonly rating: number;
|
|
26
|
+
readonly delta: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Glickman's revised Illinois procedure; one complete simultaneous rating period.
|
|
30
|
+
* https://glicko.net/glicko/glicko2.pdf — independently implemented equations. */
|
|
31
|
+
export declare const glicko2: (options?: Glicko2Options) => Glicko2Rate;
|
|
32
|
+
|
|
33
|
+
export declare interface Glicko2Input {
|
|
34
|
+
readonly player: Glicko2Rating;
|
|
35
|
+
/** All opponents carry their pre-period ratings, not sequentially updated ones. */
|
|
36
|
+
readonly opponents: ReadonlyArray<Glicko2Opponent>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export declare interface Glicko2Opponent {
|
|
40
|
+
readonly rating: number;
|
|
41
|
+
readonly deviation: number;
|
|
42
|
+
readonly score: 0 | 0.5 | 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export declare interface Glicko2Options {
|
|
46
|
+
readonly tau?: number;
|
|
47
|
+
readonly epsilon?: number;
|
|
48
|
+
readonly maxIterations?: number;
|
|
49
|
+
readonly maxOpponents?: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export declare type Glicko2Rate = (input: Glicko2Input) => Glicko2Result;
|
|
53
|
+
|
|
54
|
+
export declare interface Glicko2Rating {
|
|
55
|
+
readonly rating: number;
|
|
56
|
+
readonly deviation: number;
|
|
57
|
+
readonly volatility: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export declare interface Glicko2Result extends Glicko2Rating {
|
|
61
|
+
readonly previous: Glicko2Rating;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Weng–Lin Plackett–Luce, Algorithm4 (2011), with linear Gaussian partial play.
|
|
65
|
+
* https://jmlr.org/papers/volume12/weng11a/weng11a.pdf
|
|
66
|
+
* Fractional weights enter the team mean linearly and variance quadratically;
|
|
67
|
+
* they are not the OpenSkill library's contribution/weight-normalization API. */
|
|
68
|
+
export declare const openskill: (options?: OpenSkillOptions) => OpenSkillRate;
|
|
69
|
+
|
|
70
|
+
export declare interface OpenSkillInput {
|
|
71
|
+
readonly teams: ReadonlyArray<ReadonlyArray<OpenSkillPlayer>>;
|
|
72
|
+
readonly ranks: ReadonlyArray<number>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export declare interface OpenSkillOptions {
|
|
76
|
+
readonly beta?: number;
|
|
77
|
+
readonly tau?: number;
|
|
78
|
+
readonly kappa?: number;
|
|
79
|
+
readonly ordinalZ?: number;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export declare interface OpenSkillPlayer {
|
|
83
|
+
readonly id: string;
|
|
84
|
+
readonly mu: number;
|
|
85
|
+
readonly sigma: number;
|
|
86
|
+
/** Fraction of participation. Zero retains the exact prior. */
|
|
87
|
+
readonly weight?: number;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export declare type OpenSkillRate = (input: OpenSkillInput) => ReadonlyArray<ReadonlyArray<OpenSkillResult>>;
|
|
91
|
+
|
|
92
|
+
export declare interface OpenSkillResult {
|
|
93
|
+
readonly id: string;
|
|
94
|
+
readonly previous: {
|
|
95
|
+
readonly mu: number;
|
|
96
|
+
readonly sigma: number;
|
|
97
|
+
};
|
|
98
|
+
readonly mu: number;
|
|
99
|
+
readonly sigma: number;
|
|
100
|
+
readonly ordinal: number;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export { }
|
package/dist/rating.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { leaderboardError as e } from "./errors.js";
|
|
2
|
+
import { leaderboardIdentifier as t } from "./leaderboard.js";
|
|
3
|
+
import { glicko2 as n } from "./glicko2.js";
|
|
4
|
+
import { openskill as r } from "./openskill.js";
|
|
5
|
+
//#region src/rating.ts
|
|
6
|
+
var i = (n = {}) => {
|
|
7
|
+
let r = n.k ?? 32, i = n.scale ?? 400;
|
|
8
|
+
if (!Number.isFinite(r) || r <= 0 || r > 1e6 || !Number.isFinite(i) || i <= 0 || i > 1e6) throw e("invalid_input");
|
|
9
|
+
return (n) => {
|
|
10
|
+
if (!n || !Array.isArray(n.teams) || n.teams.length < 2 || n.teams.length > 64 || !Array.isArray(n.ranks) || n.ranks.length !== n.teams.length || n.ranks.some((e) => !Number.isSafeInteger(e) || e < 1 || e > n.teams.length)) throw e("invalid_input");
|
|
11
|
+
for (let t = 0; t < n.teams.length; t++) if (!Object.hasOwn(n.teams, t) || !Object.hasOwn(n.ranks, t)) throw e("invalid_input");
|
|
12
|
+
let a = /* @__PURE__ */ new Set(), o = n.teams.map((n) => {
|
|
13
|
+
if (!Array.isArray(n) || n.length < 1 || n.length > 256) throw e("invalid_input");
|
|
14
|
+
for (let t = 0; t < n.length; t++) if (!Object.hasOwn(n, t) || !n[t]) throw e("invalid_input");
|
|
15
|
+
let r = n.map((n) => {
|
|
16
|
+
let r = n.weight ?? 1;
|
|
17
|
+
if (!t(n.id) || a.has(n.id) || !Number.isFinite(n.rating) || Math.abs(n.rating) > 1e9 || !Number.isFinite(r) || r < 0 || r > 1) throw e("invalid_input");
|
|
18
|
+
return a.add(n.id), Object.freeze({
|
|
19
|
+
id: n.id,
|
|
20
|
+
rating: n.rating,
|
|
21
|
+
weight: r
|
|
22
|
+
});
|
|
23
|
+
}), i = r.reduce((e, t) => e + t.weight, 0);
|
|
24
|
+
if (i === 0) throw e("invalid_input");
|
|
25
|
+
return {
|
|
26
|
+
members: r,
|
|
27
|
+
weight: i,
|
|
28
|
+
mean: r.reduce((e, t) => e + t.rating * t.weight / i, 0)
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
if (a.size > 4096) throw e("limit_exceeded");
|
|
32
|
+
let s = o.map(() => 0), c = r / (o.length - 1);
|
|
33
|
+
for (let e = 0; e < o.length; e++) for (let t = e + 1; t < o.length; t++) {
|
|
34
|
+
let r = o[e], a = (o[t].mean - r.mean) / i, l = a >= 0 ? 1 / (1 + 10 ** a) : 1 - 1 / (1 + 10 ** -a), u = c * ((n.ranks[e] < n.ranks[t] ? 1 : n.ranks[e] === n.ranks[t] ? .5 : 0) - l);
|
|
35
|
+
s[e] += u, s[t] -= u;
|
|
36
|
+
}
|
|
37
|
+
return Object.freeze(o.map((t, n) => Object.freeze(t.members.map((r) => {
|
|
38
|
+
let i = s[n] * r.weight / t.weight, a = r.rating + i;
|
|
39
|
+
if (!Number.isFinite(a) || Math.abs(a) > 1e9) throw e("overflow");
|
|
40
|
+
return Object.freeze({
|
|
41
|
+
id: r.id,
|
|
42
|
+
previous: r.rating,
|
|
43
|
+
rating: a,
|
|
44
|
+
delta: i
|
|
45
|
+
});
|
|
46
|
+
}))));
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
//#endregion
|
|
50
|
+
export { i as elo, n as glicko2, r as openskill };
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { leaderboardError as e } from "./errors.js";
|
|
2
|
+
import { leaderboardIdentifier as t } from "./leaderboard.js";
|
|
3
|
+
import { canonicalLeaderboardJson as n, captureLeaderboardJson as r } from "./json.js";
|
|
4
|
+
import { glicko2 as i } from "./glicko2.js";
|
|
5
|
+
import { openskill as a } from "./openskill.js";
|
|
6
|
+
import { elo as o } from "./rating.js";
|
|
7
|
+
import { Schema as s } from "effect";
|
|
8
|
+
//#region src/ratingPools.ts
|
|
9
|
+
var c = s.Number.pipe(s.check(s.isFinite()), s.check(s.isBetween({
|
|
10
|
+
minimum: -1e9,
|
|
11
|
+
maximum: 1e9
|
|
12
|
+
}))), l = s.Number.pipe(s.check(s.isFinite()), s.check(s.isBetween({
|
|
13
|
+
minimum: 1e-12,
|
|
14
|
+
maximum: 1e9
|
|
15
|
+
}))), u = s.Union([
|
|
16
|
+
s.Struct({
|
|
17
|
+
algorithm: s.Literal("elo"),
|
|
18
|
+
rating: c
|
|
19
|
+
}),
|
|
20
|
+
s.Struct({
|
|
21
|
+
algorithm: s.Literal("openskill"),
|
|
22
|
+
mu: c,
|
|
23
|
+
sigma: l
|
|
24
|
+
}),
|
|
25
|
+
s.Struct({
|
|
26
|
+
algorithm: s.Literal("glicko2"),
|
|
27
|
+
rating: c,
|
|
28
|
+
deviation: l,
|
|
29
|
+
volatility: l
|
|
30
|
+
})
|
|
31
|
+
]), d = (t, i) => {
|
|
32
|
+
let a = r(t, 1024), o = s.decodeUnknownSync(u)(a);
|
|
33
|
+
if (o.algorithm !== i || n(o) !== n(a)) throw e("invalid_record");
|
|
34
|
+
return Object.freeze(o);
|
|
35
|
+
}, f = (n = []) => {
|
|
36
|
+
if (!Array.isArray(n) || n.length > 256) throw e("invalid_input");
|
|
37
|
+
let s = /* @__PURE__ */ new Set(), c = n.map((n) => {
|
|
38
|
+
if (!n || !t(n.name) || s.has(n.name)) throw e("invalid_input");
|
|
39
|
+
s.add(n.name);
|
|
40
|
+
let c = Object.freeze(r(n.options ?? {}, 1024));
|
|
41
|
+
if (!c || typeof c != "object" || Array.isArray(c)) throw e("invalid_input");
|
|
42
|
+
let l, u;
|
|
43
|
+
switch (n.algorithm) {
|
|
44
|
+
case "elo":
|
|
45
|
+
if (u = ["k", "scale"], l = d({
|
|
46
|
+
algorithm: "elo",
|
|
47
|
+
...n.initial ?? { rating: 1e3 }
|
|
48
|
+
}, "elo"), l.algorithm !== "elo") throw e("invalid_input");
|
|
49
|
+
o(c)({
|
|
50
|
+
teams: [[{
|
|
51
|
+
id: "a",
|
|
52
|
+
rating: l.rating
|
|
53
|
+
}], [{
|
|
54
|
+
id: "b",
|
|
55
|
+
rating: l.rating
|
|
56
|
+
}]],
|
|
57
|
+
ranks: [1, 1]
|
|
58
|
+
});
|
|
59
|
+
break;
|
|
60
|
+
case "openskill":
|
|
61
|
+
if (u = [
|
|
62
|
+
"beta",
|
|
63
|
+
"tau",
|
|
64
|
+
"kappa",
|
|
65
|
+
"ordinalZ"
|
|
66
|
+
], l = d({
|
|
67
|
+
algorithm: "openskill",
|
|
68
|
+
...n.initial ?? {
|
|
69
|
+
mu: 25,
|
|
70
|
+
sigma: 25 / 3
|
|
71
|
+
}
|
|
72
|
+
}, "openskill"), l.algorithm !== "openskill") throw e("invalid_input");
|
|
73
|
+
a(c)({
|
|
74
|
+
teams: [[{
|
|
75
|
+
id: "a",
|
|
76
|
+
mu: l.mu,
|
|
77
|
+
sigma: l.sigma
|
|
78
|
+
}], [{
|
|
79
|
+
id: "b",
|
|
80
|
+
mu: l.mu,
|
|
81
|
+
sigma: l.sigma
|
|
82
|
+
}]],
|
|
83
|
+
ranks: [1, 1]
|
|
84
|
+
});
|
|
85
|
+
break;
|
|
86
|
+
case "glicko2":
|
|
87
|
+
if (u = [
|
|
88
|
+
"tau",
|
|
89
|
+
"epsilon",
|
|
90
|
+
"maxIterations",
|
|
91
|
+
"maxOpponents"
|
|
92
|
+
], l = d({
|
|
93
|
+
algorithm: "glicko2",
|
|
94
|
+
...n.initial ?? {
|
|
95
|
+
rating: 1500,
|
|
96
|
+
deviation: 350,
|
|
97
|
+
volatility: .06
|
|
98
|
+
}
|
|
99
|
+
}, "glicko2"), l.algorithm !== "glicko2") throw e("invalid_input");
|
|
100
|
+
i(c)({
|
|
101
|
+
player: l,
|
|
102
|
+
opponents: []
|
|
103
|
+
});
|
|
104
|
+
break;
|
|
105
|
+
default: throw e("invalid_input");
|
|
106
|
+
}
|
|
107
|
+
if (Object.keys(c).some((e) => !u.includes(e))) throw e("invalid_input");
|
|
108
|
+
return Object.freeze({
|
|
109
|
+
name: n.name,
|
|
110
|
+
algorithm: n.algorithm,
|
|
111
|
+
initial: l,
|
|
112
|
+
options: c
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
for (let t = 0; t < n.length; t++) if (!Object.hasOwn(n, t)) throw e("invalid_input");
|
|
116
|
+
return Object.freeze(c);
|
|
117
|
+
}, p = s.NonEmptyString.pipe(s.check(s.isMaxLength(256))), m = s.Struct({
|
|
118
|
+
tenantId: p,
|
|
119
|
+
pool: p,
|
|
120
|
+
matchId: p,
|
|
121
|
+
teams: s.Array(s.Array(s.Struct({
|
|
122
|
+
ownerId: p,
|
|
123
|
+
weight: s.optional(s.Number.pipe(s.check(s.isFinite()), s.check(s.isBetween({
|
|
124
|
+
minimum: 0,
|
|
125
|
+
maximum: 1
|
|
126
|
+
}))))
|
|
127
|
+
})).pipe(s.check(s.isMinLength(1)), s.check(s.isMaxLength(256)))).pipe(s.check(s.isMinLength(2)), s.check(s.isMaxLength(64))),
|
|
128
|
+
ranks: s.Array(s.Number.pipe(s.check(s.isInt()), s.check(s.isBetween({
|
|
129
|
+
minimum: 1,
|
|
130
|
+
maximum: 64
|
|
131
|
+
})))).pipe(s.check(s.isMinLength(2)), s.check(s.isMaxLength(64)))
|
|
132
|
+
});
|
|
133
|
+
//#endregion
|
|
134
|
+
export { m as PrepareRatingMatchSchema, u as RatingStateSchema, d as exactRatingState, f as resolveRatingPools };
|