@cs2dak/cohort 0.2.1 → 1.0.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.
Files changed (2) hide show
  1. package/package.json +3 -3
  2. package/src/index.ts +59 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cs2dak/cohort",
3
- "version": "0.2.1",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "exports": {
@@ -8,8 +8,8 @@
8
8
  },
9
9
  "dependencies": {
10
10
  "@rivalhub/rival-rating": "^0.1.0",
11
- "@cs2dak/contract": "0.2.1",
12
- "@cs2dak/core": "0.2.1"
11
+ "@cs2dak/contract": "1.0.0",
12
+ "@cs2dak/core": "1.0.0"
13
13
  },
14
14
  "files": [
15
15
  "src/index.ts"
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  deriveAccountSignalsV2,
3
+ derivePlayerWeaponHighlights,
3
4
  deriveRRIndicators,
4
5
  computeAccountRatingsV2
5
6
  } from "@cs2dak/core";
@@ -9,6 +10,7 @@ import {
9
10
  type AccountSignalsV2,
10
11
  type DemoPackage,
11
12
  type RRIndicators,
13
+ type PlayerWeaponHighlightFacts,
12
14
  type SeasonCohortBundle,
13
15
  type TeamKey,
14
16
  type ValueAccountsWeights
@@ -57,6 +59,7 @@ interface PlayerAccumulator {
57
59
  mapCount: number;
58
60
  signals: AccountSignalsV2[];
59
61
  indicators: RRIndicators[];
62
+ weaponHighlights: PlayerWeaponHighlightFacts[];
60
63
  perMatch: Array<{ matchId: string; steamId64: string; accountRR: number; rrV1: number }>;
61
64
  }
62
65
 
@@ -73,6 +76,7 @@ export function buildSeasonCohort(
73
76
  for (const demo of demos) {
74
77
  const signals = deriveAccountSignalsV2(demo.pkg);
75
78
  const indicators = deriveRRIndicators(demo.pkg);
79
+ const weaponHighlights = derivePlayerWeaponHighlights(demo.pkg);
76
80
  const matchAccounts = computeAccountRatingsV2(demo.pkg);
77
81
  const matchAccountBySteamId = new Map(matchAccounts.map((row) => [row.signals.steamId64, row.rr]));
78
82
  const rrBySteamId = new Map(indicators.map((row) => [row.steamId64, computeRR(row, rrWeights)]));
@@ -94,6 +98,7 @@ export function buildSeasonCohort(
94
98
  mapCount: 0,
95
99
  signals: [],
96
100
  indicators: [],
101
+ weaponHighlights: [],
97
102
  perMatch: []
98
103
  }));
99
104
 
@@ -105,6 +110,8 @@ export function buildSeasonCohort(
105
110
  acc.mapCount += 1;
106
111
  acc.signals.push(signal);
107
112
  acc.indicators.push(indicator);
113
+ const weaponHighlight = weaponHighlights.find((row) => row.steamId64 === player.steamId64);
114
+ if (weaponHighlight) acc.weaponHighlights.push(weaponHighlight);
108
115
  acc.perMatch.push({
109
116
  matchId: demo.matchId,
110
117
  steamId64: player.steamId64,
@@ -140,9 +147,17 @@ export function buildSeasonCohort(
140
147
  const prismResults = new Map(computePrism(prismInputs, prismWeights).map((row) => [row.steamId64, row]));
141
148
 
142
149
  return seasonCohortBundleSchema.parse({
143
- version: "cs2-demo-analysis-kit/season-0.1",
150
+ version: "cs2-demo-analysis-kit/cohort-1.0",
144
151
  matchCount: demos.length,
145
152
  weightsVersion: `${rrWeights.version}+${valueWeights.version}+${prismWeights.version}`,
153
+ provenance: {
154
+ cohortVersion: "cs2-demo-analysis-kit/cohort-1.0",
155
+ sourceSchemaVersion: "cs2-demo-format/2.0",
156
+ matches: demos.map((demo) => ({
157
+ matchId: demo.matchId,
158
+ sourceDemoHash: demo.pkg.manifest.demo?.hash ?? null
159
+ }))
160
+ },
146
161
  players: seasonRows
147
162
  .map((row) => {
148
163
  const balanced = balancedByKey.get(row.acc.playerKey)!;
@@ -159,6 +174,7 @@ export function buildSeasonCohort(
159
174
  rrV1: round(row.rrV1.rr, 3),
160
175
  rrV1Percentile: round(rrToPercentile(rrV1Scores, row.rrV1.rr), 1),
161
176
  indicators: row.indicators,
177
+ weaponHighlights: aggregateWeaponHighlights(row.acc.playerKey, row.acc.weaponHighlights),
162
178
  accountRR: round(balanced.rr, 3),
163
179
  accountRRRaw: round(balanced.rrRaw, 3),
164
180
  accountBreakdown: {
@@ -174,7 +190,6 @@ export function buildSeasonCohort(
174
190
  perMatch: row.acc.perMatch.sort((a, b) => a.matchId.localeCompare(b.matchId))
175
191
  };
176
192
  })
177
- .sort((a, b) => b.accountRR - a.accountRR || b.rrV1 - a.rrV1 || a.name.localeCompare(b.name))
178
193
  });
179
194
  }
180
195
 
@@ -189,7 +204,7 @@ function aggregateAccountSignals(steamId64: string, rows: AccountSignalsV2[]): A
189
204
  return {
190
205
  steamId64,
191
206
  rounds: sum(rows, (row) => row.rounds),
192
- sourceVersion: "cs2-demo-analysis-kit/season-0.1",
207
+ sourceVersion: "cs2-demo-analysis-kit/cohort-1.0",
193
208
  combat: {
194
209
  kills: sum(rows, (row) => row.combat.kills),
195
210
  deaths: sum(rows, (row) => row.combat.deaths),
@@ -333,6 +348,47 @@ function aggregateRRIndicators(steamId64: string, rows: RRIndicators[]): RRIndic
333
348
  };
334
349
  }
335
350
 
351
+ function aggregateWeaponHighlights(
352
+ steamId64: string,
353
+ rows: PlayerWeaponHighlightFacts[]
354
+ ): PlayerWeaponHighlightFacts {
355
+ const weapons = new Map<string, PlayerWeaponHighlightFacts["weapons"][number]>();
356
+ for (const row of rows) {
357
+ for (const weapon of row.weapons) {
358
+ const current = weapons.get(weapon.weapon) ?? {
359
+ weapon: weapon.weapon,
360
+ kills: 0,
361
+ headshotKills: 0,
362
+ tradeKills: 0,
363
+ noScopeKills: 0,
364
+ throughSmokeKills: 0,
365
+ wallbangKills: 0,
366
+ penetratedObjects: 0
367
+ };
368
+ current.kills += weapon.kills;
369
+ current.headshotKills += weapon.headshotKills;
370
+ current.tradeKills += weapon.tradeKills;
371
+ current.noScopeKills += weapon.noScopeKills;
372
+ current.throughSmokeKills += weapon.throughSmokeKills;
373
+ current.wallbangKills += weapon.wallbangKills;
374
+ current.penetratedObjects += weapon.penetratedObjects;
375
+ weapons.set(weapon.weapon, current);
376
+ }
377
+ }
378
+ return {
379
+ steamId64,
380
+ totalKills: sum(rows, (row) => row.totalKills),
381
+ weapons: [...weapons.values()]
382
+ .sort((a, b) => b.kills - a.kills || a.weapon.localeCompare(b.weapon)),
383
+ highlights: {
384
+ wallbangKills: sumNullable(rows.map((row) => row.highlights.wallbangKills)),
385
+ noScopeKills: sumNullable(rows.map((row) => row.highlights.noScopeKills)),
386
+ throughSmokeKills: sumNullable(rows.map((row) => row.highlights.throughSmokeKills)),
387
+ collateralKills: sumNullable(rows.map((row) => row.highlights.collateralKills))
388
+ }
389
+ };
390
+ }
391
+
336
392
  // 账户平衡的数学(标准化 + 残差化)已迁入 rival-rating 的 computeCohortAccountsRR(公式归属)。
337
393
  // 本层只负责把 std(rrV1) 作为 targetStd 传进去,对齐 v2 与 HLTV 逆向的量纲。
338
394
  function stdev(xs: number[]): number {