wows-compute 0.0.0-stage → 1.1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 wows-devkit contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,128 @@
1
- # Temporary Holding Version
1
+ # wows-compute
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 《战舰世界》对局数据的纯计算内核。无文件 IO、无网络、无运行时状态:输入是已经解析好的赛前名单或赛后回放结果,输出是可直接持久化或展示的 `MatchRecord`、评级与统计。
4
+
5
+ 已提供:`./match`、评级、时间、`./recent`、`./aggregate`、`./metrics`、`./strength`(含概览归纳)。
6
+
7
+ ## 安装
8
+
9
+ ```bash
10
+ pnpm add wows-compute wows-number-api-sdk
11
+ ```
12
+
13
+ ESM-only,Node.js 22.18+。
14
+
15
+ ## 入口
16
+
17
+ | 入口 | 内容 |
18
+ | --- | --- |
19
+ | `wows-compute` | PR / PR*、等级、近期统计、聚合、概览归纳、游戏日与周界 |
20
+ | `wows-compute/match` | `ArenaRoster`、`MatchRecord`、合并、赛前赛后关联、组队 |
21
+ | `wows-compute/metrics` | 字段字典与派生指标 |
22
+ | `wows-compute/strength` | 散点原料、概览归纳、参考线、角色配对 |
23
+
24
+ ## 快速上手
25
+
26
+ ```ts
27
+ import { fromReplay, groupDivisions } from 'wows-compute/match'
28
+
29
+ const record = fromReplay(parsedReplay)
30
+ // record.arenaId、record.outcome、record.quality、record.participants
31
+
32
+ const divisions = groupDivisions(record.participants)
33
+ ```
34
+
35
+ `fromReplay` 接受 `wows-replay-parser` 的 `ParsedReplay` 结构,不依赖它的包,只有类型层面的结构兼容。
36
+
37
+ ### 平局
38
+
39
+ 事实层保留 `outcome: 'draw'`;统计层用 `statOutcome('draw')` 映射为 `loss`,即平局双输。`winnerTeamId` 为负数表示平局,`0` 是合法队伍。
40
+
41
+ ### 时间
42
+
43
+ `battleTime` 优先取结果包的 `startDt`(UTC)。回放元数据的 `dateTime` 是录制者本机时间,跨区不可靠,只在没有结果包时作为回退。
44
+
45
+ ### 评级
46
+
47
+ ```ts
48
+ import { accountPrEstimated, accountPrPooled, levelOf, shipPr } from 'wows-compute'
49
+
50
+ const ship = shipPr(stats, expected) // 精确,exact: true
51
+ const account = accountPrEstimated(totals, expected) // PR*,exact: false
52
+ const pooled = accountPrPooled(ships, expected, sample) // 精确加权,exact: true
53
+
54
+ levelOf('pr', 1200) // { levelKey: 'average', floor: 1100, ... }
55
+ ```
56
+
57
+ `exact: false` 是产品承诺:调用方必须把它显示为 `PR*`,不能与精确 PR 不加区分地混排。偏差方向通过 `bias` 输出;没有 `shipMix` 时是 `unknown`,不猜。
58
+
59
+ PR 公式、预期值和分档阈值全部复用 `wows-number-api-sdk`,本包不复制常量。
60
+
61
+ ### 近期与聚合
62
+
63
+ ```ts
64
+ import { aggregateTeam, recentMatches } from 'wows-compute'
65
+
66
+ const recent = recentMatches(records, { subject: { kind: 'self' }, last: 20 }, expected)
67
+ const team = aggregateTeam(records, teamId, {
68
+ weighting: 'equal', // 监控页:每个席位一票
69
+ prAggregation: 'mean',
70
+ expected,
71
+ })
72
+ ```
73
+
74
+ - 近期只统计“已解析并入库”的对局,不扫描历史回放、不补缺口。
75
+ - `winRate` 是百分数,例如 `54.3`。
76
+ - 平局计入 `battles`,胜率为 `wins / battles`;`losses` 只计实际失败。
77
+ - 记录总览用 `weighting: 'battles'` + `prAggregation: 'pooled'`;监控页用 `equal` + `mean`。
78
+ - 分母为 0 返回 `null`;`coverage` 的 `resolved + hidden + bot + failed` 恒等于 `total`。
79
+
80
+ ### 指标字典
81
+
82
+ ```ts
83
+ import { METRICS, deriveMetrics } from 'wows-compute/metrics'
84
+
85
+ deriveMetrics(player.result.fields) // { damage, frags, potentialDamage, ... }
86
+ ```
87
+
88
+ 指标只做字段语义包装,不复制原始数据。新增字段必须能在回放字段映射中找到,测试会先失败。
89
+
90
+ ### 对局强度原料
91
+
92
+ ```ts
93
+ import { buildPlayerPoints, pairByRole, referenceLines } from 'wows-compute/strength'
94
+
95
+ const points = buildPlayerPoints(roster, ratings, catalog)
96
+ const reference = referenceLines(points)
97
+ const matchups = pairByRole(allyPoints, enemyPoints)
98
+ ```
99
+
100
+ 只输出散点原料、参考线和逐角色差值,不输出综合强度分或预测。
101
+
102
+ ### 队伍概览
103
+
104
+ `summarizePlayers` / `summarizeRoster` 对已经标准化的 `PlayerPoint` 做加权归纳,输入不是对局记录:
105
+
106
+ ```ts
107
+ import { buildPlayerPoints, summarizeRoster } from 'wows-compute/strength'
108
+
109
+ const points = buildPlayerPoints(roster, ratings, catalog)
110
+ const { ally, enemy, unassigned } = summarizeRoster(points)
111
+ // ally.players、ally.battles、ally.winRate / avgDamage / avgFrags / pr、ally.coverage
112
+ ```
113
+
114
+ - 与 `aggregate*` 的分工:`aggregate*` 从逐局 `MatchRecord` 推导统计;overview 只归纳已算好的值,赛前赛后可复用。
115
+ - 默认 `weighting: 'equal'`(一人一票),可改为 `'battles'` 按生涯场次加权。
116
+ - 每项均值只使用非 null 值,`0` 是合法值,分母为 0 返回 `null`。
117
+ - `coverage` 的单位是席位:`total` 含 Bot,`players` 只算真人,且 `resolved + hidden + bot + failed === total`。
118
+ - 概览 PR 是对账号 PR 的均值,`prExact` 恒为 `false`,展示时必须标 `PR*`。
119
+
120
+ ## 约束
121
+
122
+ - 不读写文件、不访问网络、不 import `node:*`。
123
+ - 不扫描历史回放、不推断缺失对局、不做存储假设。
124
+ - 只读已解析的数据;需要回放解析时使用 `wows-replay-parser`。
125
+
126
+ ## License
127
+
128
+ MIT
@@ -0,0 +1,286 @@
1
+ import { c as MatchRecord, i as ArenaRoster, o as MatchGroup, p as Relation } from "./types-DwmuyfXs.mjs";
2
+ import { ExpectedValuesDocument, ShipBattleStats, SingleBattleInput } from "wows-number-api-sdk";
3
+ //#region src/rating/index.d.ts
4
+ interface SampleDescriptor {
5
+ kind: 'ingested' | 'lifetime' | 'match';
6
+ matches: number;
7
+ /** ISO timestamps; present for date windows. */
8
+ from?: string;
9
+ to?: string;
10
+ /** When querying another player: matches shared with the local player. */
11
+ sharedWith?: number;
12
+ players?: number;
13
+ }
14
+ interface EstimateBias {
15
+ direction: 'underestimate' | 'overestimate' | 'mixed' | 'unknown';
16
+ reason: 'ship-mix' | 'insufficient-data';
17
+ /** Relative baseline difference: pooled baseline / global average - 1. */
18
+ delta?: {
19
+ damage?: number;
20
+ frags?: number;
21
+ wins?: number;
22
+ };
23
+ }
24
+ interface RatedValue {
25
+ value: number;
26
+ basis: 'ship' | 'account';
27
+ exact: boolean;
28
+ sample: SampleDescriptor;
29
+ /** Expected-values version, taken from `expected.time`. */
30
+ baselineVersion: string;
31
+ /** Only present when `exact === false`. */
32
+ bias?: EstimateBias;
33
+ }
34
+ interface AccountTotals {
35
+ battles: number;
36
+ damage: number;
37
+ frags: number;
38
+ wins: number;
39
+ }
40
+ interface ShipMixEntry {
41
+ shipId: number | string;
42
+ battles: number;
43
+ }
44
+ interface AccountPrOptions {
45
+ shipMix?: readonly ShipMixEntry[];
46
+ }
47
+ /** Personal Rating for one ship, using that ship's own expected values. */
48
+ declare function shipPr(stats: ShipBattleStats, expected: ExpectedValuesDocument, sample?: SampleDescriptor): RatedValue | null;
49
+ /** Personal Rating for a single battle. */
50
+ declare function battlePr(battle: SingleBattleInput, expected: ExpectedValuesDocument, sample?: SampleDescriptor): RatedValue | null;
51
+ /**
52
+ * Fast account PR*: account totals against the unweighted average of all
53
+ * expected values.
54
+ *
55
+ * This is an estimate, not a different formula: the account is treated as one
56
+ * synthetic ship. `exact: false` is a product promise — UI must mark it `PR*`.
57
+ */
58
+ declare function accountPrEstimated(account: AccountTotals, expected: ExpectedValuesDocument, options?: AccountPrOptions): RatedValue | null;
59
+ /**
60
+ * Exact weighted account PR: pools per-ship totals and per-ship baselines.
61
+ *
62
+ * Ships without expected values are skipped by `calculatePR`, not counted
63
+ * as zero.
64
+ */
65
+ declare function accountPrPooled(ships: readonly ShipBattleStats[], expected: ExpectedValuesDocument, sample: SampleDescriptor): RatedValue | null;
66
+ //#endregion
67
+ //#region src/aggregate/index.d.ts
68
+ type Weighting = 'equal' | 'battles';
69
+ type PrAggregation = 'mean' | 'pooled';
70
+ interface Coverage {
71
+ total: number;
72
+ resolved: number;
73
+ hidden: number;
74
+ bot: number;
75
+ failed: number;
76
+ }
77
+ interface AggregatedValue {
78
+ value: number | null;
79
+ sample: SampleDescriptor;
80
+ coverage: Coverage;
81
+ }
82
+ interface AggregateOptions {
83
+ weighting: Weighting;
84
+ prAggregation: PrAggregation;
85
+ expected?: ExpectedValuesDocument;
86
+ matchGroups?: readonly MatchGroup[];
87
+ from?: number;
88
+ to?: number;
89
+ }
90
+ interface PlayerAggregate {
91
+ accountId: number;
92
+ name: string | null;
93
+ battles: number;
94
+ wins: number;
95
+ losses: number;
96
+ draws: number;
97
+ damage: number;
98
+ frags: number;
99
+ winRate: AggregatedValue;
100
+ avgDamage: AggregatedValue;
101
+ avgFrags: AggregatedValue;
102
+ pr: AggregatedValue;
103
+ }
104
+ interface TeamAggregate {
105
+ teamId: number | null;
106
+ players: number;
107
+ coverage: Coverage;
108
+ values: {
109
+ winRate: AggregatedValue;
110
+ avgDamage: AggregatedValue;
111
+ avgFrags: AggregatedValue;
112
+ pr: AggregatedValue;
113
+ };
114
+ }
115
+ interface ShipAggregate {
116
+ shipId: number;
117
+ battles: number;
118
+ wins: number;
119
+ losses: number;
120
+ draws: number;
121
+ damage: number;
122
+ frags: number;
123
+ winRate: AggregatedValue;
124
+ avgDamage: AggregatedValue;
125
+ avgFrags: AggregatedValue;
126
+ pr: AggregatedValue;
127
+ }
128
+ interface DailyPoint {
129
+ day: string;
130
+ matches: number;
131
+ battles: number;
132
+ wins: number;
133
+ losses: number;
134
+ draws: number;
135
+ winRate: number | null;
136
+ avgDamage: number | null;
137
+ avgFrags: number | null;
138
+ pr: AggregatedValue;
139
+ }
140
+ interface DailyOptions {
141
+ timeZone: string;
142
+ boundaryHour?: number;
143
+ expected?: ExpectedValuesDocument;
144
+ matchGroups?: readonly MatchGroup[];
145
+ from?: number;
146
+ to?: number;
147
+ }
148
+ /** Per-player aggregates over the selected records. Ordered by battles, then account id. */
149
+ declare function aggregatePlayers(records: readonly MatchRecord[], options: AggregateOptions): PlayerAggregate[];
150
+ /** One team's aggregate; coverage only counts that team's participants. */
151
+ declare function aggregateTeam(records: readonly MatchRecord[], teamId: number | null, options: AggregateOptions): TeamAggregate;
152
+ /** Per-ship aggregates over the selected records. Ordered by battles, then ship id. */
153
+ declare function aggregateShips(records: readonly MatchRecord[], options: AggregateOptions): ShipAggregate[];
154
+ /** Self performance per game day, ordered oldest first. */
155
+ declare function dailySeries(records: readonly MatchRecord[], options: DailyOptions): DailyPoint[];
156
+ //#endregion
157
+ //#region src/strength/index.d.ts
158
+ type ShipRole = 'submarine' | 'aircraft-carrier' | 'destroyer' | 'battleship' | 'cruiser-functional' | 'cruiser-firepower' | 'cruiser' | 'unknown';
159
+ /** Display / analysis order; not a composite score. */
160
+ declare const ROLE_ORDER: readonly ShipRole[];
161
+ interface ShipCatalogLike {
162
+ role?: (shipId: number) => ShipRole | null;
163
+ tier?: (shipId: number) => number | null;
164
+ }
165
+ interface PlayerRatingSnapshot {
166
+ pr?: RatedValue | null;
167
+ /** Lifetime win rate in percent, e.g. `54.3`. */
168
+ winRate?: number | null;
169
+ battles?: number | null;
170
+ /** Lifetime average damage; `null` when unavailable. */
171
+ avgDamage?: number | null;
172
+ /** Lifetime average frags; `null` when unavailable. */
173
+ avgFrags?: number | null;
174
+ /** Platform-flagged hidden profile, distinct from "no data". */
175
+ hidden?: boolean;
176
+ }
177
+ type PlayerRatingValue = RatedValue | PlayerRatingSnapshot;
178
+ type PlayerRatings = ReadonlyMap<number, PlayerRatingValue>;
179
+ interface PlayerPoint {
180
+ accountId: number | null;
181
+ name: string | null;
182
+ /** `null` for pre-battle rosters; relations are known, team ids are not. */
183
+ teamId: number | null;
184
+ shipId: number | null;
185
+ shipRole: ShipRole;
186
+ tier: number | null;
187
+ divisionId: number | null;
188
+ /** Percentage, e.g. `54.3`. */
189
+ winRate: number | null;
190
+ pr: number | null;
191
+ prExact: boolean;
192
+ battles: number | null;
193
+ isBot: boolean;
194
+ /** Roster relation; used to group points into sides. */
195
+ relation: Relation;
196
+ avgDamage: number | null;
197
+ avgFrags: number | null;
198
+ hidden: boolean;
199
+ }
200
+ interface ReferenceLines {
201
+ winRateMedian: number | null;
202
+ prMedian: number | null;
203
+ }
204
+ type Quadrant = 'q1' | 'q2' | 'q3' | 'q4' | 'unknown';
205
+ interface RoleWeightProfile {
206
+ weights?: Partial<Record<ShipRole, number>>;
207
+ }
208
+ interface RoleMatchup {
209
+ role: ShipRole;
210
+ allyCount: number;
211
+ enemyCount: number;
212
+ allyMedianPr: number | null;
213
+ enemyMedianPr: number | null;
214
+ allyMedianWinRate: number | null;
215
+ enemyMedianWinRate: number | null;
216
+ /** Ally median minus enemy median; `null` when either side lacks values. */
217
+ prDiff: number | null;
218
+ winRateDiff: number | null;
219
+ weight: number | null;
220
+ sample: {
221
+ ally: number;
222
+ enemy: number;
223
+ };
224
+ coverage: Coverage;
225
+ }
226
+ /** Maps a roster to scatter-plot points. Bots and hidden players stay in the list. */
227
+ declare function buildPlayerPoints(roster: ArenaRoster, ratings: PlayerRatings, catalog?: ShipCatalogLike): PlayerPoint[];
228
+ declare function median(values: readonly number[]): number | null;
229
+ declare function referenceLines(points: readonly PlayerPoint[]): ReferenceLines;
230
+ /**
231
+ * Quadrants relative to the reference lines.
232
+ *
233
+ * q1 high win rate + high PR, q2 low win rate + high PR,
234
+ * q3 low + low, q4 high win rate + low PR.
235
+ */
236
+ declare function quadrantOf(point: PlayerPoint, reference: ReferenceLines): Quadrant;
237
+ /**
238
+ * Per-role ally/enemy comparison.
239
+ *
240
+ * Returns differences and samples only; no composite score and no prediction.
241
+ */
242
+ declare function pairByRole(ally: readonly PlayerPoint[], enemy: readonly PlayerPoint[], profile?: RoleWeightProfile): RoleMatchup[];
243
+ interface SummaryOptions {
244
+ /**
245
+ * `equal` (default): one player, one vote.
246
+ * `battles`: weight by the player's lifetime battles.
247
+ */
248
+ weighting?: Weighting;
249
+ }
250
+ interface PlayerSummary {
251
+ /** All slots on the side, including bots. */
252
+ total: number;
253
+ /** Non-bot slots. */
254
+ players: number;
255
+ /** Sum of non-null lifetime battles for non-bot players. */
256
+ battles: number;
257
+ /** Counts slots, not participant-match samples. */
258
+ coverage: Coverage;
259
+ /** Percentage, e.g. `54.3`. */
260
+ winRate: number | null;
261
+ avgDamage: number | null;
262
+ avgFrags: number | null;
263
+ pr: number | null;
264
+ /**
265
+ * Always false: a mean of account PR values is not an official team PR.
266
+ * UI must mark it `PR*`.
267
+ */
268
+ prExact: false;
269
+ }
270
+ interface RosterSummary {
271
+ ally: PlayerSummary;
272
+ enemy: PlayerSummary;
273
+ /** Slots that cannot be assigned to a side (`relation === 'unknown'`). */
274
+ unassigned: number;
275
+ }
276
+ /**
277
+ * Reduces normalized player points into one side's overview.
278
+ *
279
+ * Averages only over non-null values; `0` is a valid value. Empty input
280
+ * returns an all-zero summary rather than throwing.
281
+ */
282
+ declare function summarizePlayers(points: readonly PlayerPoint[], options?: SummaryOptions): PlayerSummary;
283
+ /** Groups points by `relation` and summarizes both sides. */
284
+ declare function summarizeRoster(points: readonly PlayerPoint[], options?: SummaryOptions): RosterSummary;
285
+ //#endregion
286
+ export { TeamAggregate as A, SampleDescriptor as B, AggregatedValue as C, PlayerAggregate as D, DailyPoint as E, dailySeries as F, shipPr as G, accountPrEstimated as H, AccountPrOptions as I, AccountTotals as L, aggregatePlayers as M, aggregateShips as N, PrAggregation as O, aggregateTeam as P, EstimateBias as R, AggregateOptions as S, DailyOptions as T, accountPrPooled as U, ShipMixEntry as V, battlePr as W, pairByRole as _, PlayerSummary as a, summarizePlayers as b, ReferenceLines as c, RosterSummary as d, ShipCatalogLike as f, median as g, buildPlayerPoints as h, PlayerRatings as i, Weighting as j, ShipAggregate as k, RoleMatchup as l, SummaryOptions as m, PlayerRatingSnapshot as n, Quadrant as o, ShipRole as p, PlayerRatingValue as r, ROLE_ORDER as s, PlayerPoint as t, RoleWeightProfile as u, quadrantOf as v, Coverage as w, summarizeRoster as x, referenceLines as y, RatedValue as z };
@@ -0,0 +1,48 @@
1
+ import { S as StatOutcome, a as Division, c as MatchRecord, h as ReplayFacts, i as ArenaRoster, o as MatchGroup, r as ArenaKeyInput, s as MatchParticipant, t as ArenaInfoLike, u as Outcome } from "./types-DwmuyfXs.mjs";
2
+ //#region src/match/index.d.ts
3
+ interface FromArenaInfoOptions {
4
+ timeZone?: string;
5
+ }
6
+ interface FromReplayOptions {
7
+ timeZone?: string;
8
+ ingestedAt?: number;
9
+ parsedAt?: number;
10
+ source?: MatchRecord['source'];
11
+ /** Include the raw battle-results payload for later remapping. */
12
+ includeRaw?: boolean;
13
+ }
14
+ /** Draw counts as a loss for both teams in every statistical view. */
15
+ declare function statOutcome(outcome: Outcome): StatOutcome | null;
16
+ /**
17
+ * Resolves a team's outcome.
18
+ *
19
+ * `winnerTeamId` is a real team id (`0` included). Negative values mean a
20
+ * draw; `null` means the result packet carried no verdict.
21
+ */
22
+ declare function outcomeOf(teamId: number | null, winnerTeamId: number | null): Outcome;
23
+ declare function matchGroupOf(value: unknown): MatchGroup;
24
+ declare function isBot(accountId: number | null, name: string | null): boolean;
25
+ /**
26
+ * Stable match key. Prefers the numeric arena id, then a deterministic derived
27
+ * key, then an opaque unknown marker.
28
+ */
29
+ declare function arenaKeyOf(input: ArenaKeyInput): string;
30
+ declare function fromArenaInfo(input: ArenaInfoLike, options?: FromArenaInfoOptions): ArenaRoster;
31
+ declare function fromReplay(facts: ReplayFacts, options?: FromReplayOptions): MatchRecord;
32
+ /**
33
+ * Combines two parses of the same match.
34
+ *
35
+ * Higher quality wins; within the same quality the newer parse wins. Fields
36
+ * missing from the winner are filled from the other record.
37
+ */
38
+ declare function mergeMatchRecords(a: MatchRecord, b: MatchRecord): MatchRecord;
39
+ /**
40
+ * Links a pre-battle roster to a finished record.
41
+ *
42
+ * Returns `null` unless exactly one roster matches; ambiguity is never guessed.
43
+ */
44
+ declare function matchRosterToRecord(rosters: readonly ArenaRoster[], record: MatchRecord): ArenaRoster | null;
45
+ /** Groups participants by `teamId + prebattle_id`; solo players are omitted. */
46
+ declare function groupDivisions(participants: readonly MatchParticipant[]): Division[];
47
+ //#endregion
48
+ export { fromReplay as a, matchGroupOf as c, outcomeOf as d, statOutcome as f, fromArenaInfo as i, matchRosterToRecord as l, FromReplayOptions as n, groupDivisions as o, arenaKeyOf as r, isBot as s, FromArenaInfoOptions as t, mergeMatchRecords as u };
@@ -0,0 +1,82 @@
1
+ import { A as TeamAggregate, B as SampleDescriptor, C as AggregatedValue, D as PlayerAggregate, E as DailyPoint, F as dailySeries, G as shipPr, H as accountPrEstimated, I as AccountPrOptions, L as AccountTotals, M as aggregatePlayers, N as aggregateShips, O as PrAggregation, P as aggregateTeam, R as EstimateBias, S as AggregateOptions, T as DailyOptions, U as accountPrPooled, V as ShipMixEntry, W as battlePr, a as PlayerSummary, b as summarizePlayers, d as RosterSummary, j as Weighting, k as ShipAggregate, m as SummaryOptions, w as Coverage, x as summarizeRoster, z as RatedValue } from "./index-CkKx_Wtz.mjs";
2
+ import { c as MatchRecord, o as MatchGroup } from "./types-DwmuyfXs.mjs";
3
+ import { ExpectedValuesDocument, ExpectedValuesDocument as ExpectedValuesDocument$1, PR_BANDS, PR_CATEGORY_LABELS, PersonalRatingCategory, PersonalRatingCategory as PersonalRatingCategory$1, ShipBattleStats, ShipExpectedValues, SingleBattleInput, WIN_RATE_BANDS, prCategory, winRateCategory } from "wows-number-api-sdk";
4
+ //#region src/levels/index.d.ts
5
+ export type LevelKind = 'pr' | 'win-rate';
6
+ export interface LevelResult {
7
+ levelKey: PersonalRatingCategory$1;
8
+ /** Inclusive lower bound of the hit band; `null` for the lowest band. */
9
+ floor: number | null;
10
+ /** Lower bound of the next band; `null` for the highest band. */
11
+ nextFloor: number | null;
12
+ deltaToFloor: number | null;
13
+ deltaToNext: number | null;
14
+ }
15
+ /** Level key plus thresholds. Labels and colors belong to the application. */
16
+ export declare function levelOf(kind: LevelKind, value: number): LevelResult | null;
17
+ //#endregion
18
+ //#region src/recent/index.d.ts
19
+ export type RecentSubject = {
20
+ kind: 'self';
21
+ } | {
22
+ kind: 'participant';
23
+ accountId: number;
24
+ };
25
+ export interface RecentQuery {
26
+ subject: RecentSubject;
27
+ /** Window size in matches; defaults to 20. */
28
+ last?: number;
29
+ from?: number;
30
+ to?: number;
31
+ matchGroups?: readonly MatchGroup[];
32
+ /** `loss` (default) counts a draw as a loss; `exclude` drops draws entirely. */
33
+ drawPolicy?: 'loss' | 'exclude';
34
+ }
35
+ export interface RecentStats {
36
+ battles: number;
37
+ wins: number;
38
+ losses: number;
39
+ draws: number;
40
+ /** Percentage, e.g. `54.3`; `null` when there are no battles. */
41
+ winRate: number | null;
42
+ damage: number;
43
+ frags: number;
44
+ avgDamage: number | null;
45
+ avgFrags: number | null;
46
+ pr: RatedValue | null;
47
+ }
48
+ export interface RecentResult {
49
+ matches: MatchRecord[];
50
+ sample: SampleDescriptor;
51
+ stats: RecentStats;
52
+ }
53
+ /**
54
+ * Statistics over ingested matches only.
55
+ *
56
+ * No gap filling, no lifetime totals, no historical replay scan: a match is in
57
+ * the window only when it was successfully parsed and stored by the caller.
58
+ */
59
+ export declare function recentMatches(records: readonly MatchRecord[], query: RecentQuery, expected?: ExpectedValuesDocument$1): RecentResult;
60
+ //#endregion
61
+ //#region src/time/index.d.ts
62
+ /**
63
+ * Parses the game's `DD.MM.YYYY HH:mm:ss` format in an explicit time zone.
64
+ *
65
+ * The value itself carries no time zone: it is the recorder's local time, so
66
+ * the caller must decide which zone the replay belongs to. Returns `null` for
67
+ * malformed or impossible dates.
68
+ */
69
+ export declare function parseBattleTime(value: string, timeZone: string): number | null;
70
+ /**
71
+ * Local "game day" label (`YYYY-MM-DD`).
72
+ *
73
+ * Days roll over at `boundaryHour` instead of midnight, so a battle at 02:00
74
+ * still belongs to the previous game day.
75
+ */
76
+ export declare function gameDay(timestamp: number, timeZone: string, boundaryHour?: number): string;
77
+ /** Start of the game week (Monday at `boundaryHour`, local time) containing `timestamp`. */
78
+ export declare function weekBoundary(timestamp: number, timeZone: string, boundaryHour?: number): number;
79
+ /** Inclusive range check; omitted bounds are open. */
80
+ export declare function inRange(timestamp: number, from?: number, to?: number): boolean;
81
+ //#endregion
82
+ export { AccountPrOptions, AccountTotals, AggregateOptions, AggregatedValue, Coverage, DailyOptions, DailyPoint, EstimateBias, type ExpectedValuesDocument, PR_BANDS, PR_CATEGORY_LABELS, type PersonalRatingCategory, PlayerAggregate, type PlayerSummary, PrAggregation, RatedValue, type RosterSummary, SampleDescriptor, ShipAggregate, type ShipBattleStats, type ShipExpectedValues, ShipMixEntry, type SingleBattleInput, type SummaryOptions, TeamAggregate, WIN_RATE_BANDS, Weighting, accountPrEstimated, accountPrPooled, aggregatePlayers, aggregateShips, aggregateTeam, battlePr, dailySeries, prCategory, shipPr, summarizePlayers, summarizeRoster, winRateCategory };