@cs2dak/contract 1.0.0 → 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 +7 -0
- package/package.json +3 -3
- package/src/analysis.ts +11 -5
- package/src/bracket.ts +68 -0
- package/src/cohort.ts +2 -1
- package/src/contract.test.ts +75 -10
- package/src/demo-package.ts +2 -2
- package/src/dependency-boundaries.test.ts +27 -0
- package/src/duel.ts +105 -0
- package/src/event-package.test.ts +48 -0
- package/src/event-package.ts +163 -0
- package/src/evidence.ts +22 -0
- package/src/index.ts +9 -0
- package/src/map-intelligence.ts +202 -0
- package/src/map-role.ts +317 -0
- package/src/player.ts +15 -4
- package/src/radar-field.ts +79 -0
- package/src/scoring.ts +9 -1
- package/src/trails.ts +56 -0
- package/src/upstream.ts +27 -7
- package/src/veto.ts +44 -0
- package/src/workspace.ts +184 -10
package/src/map-role.ts
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { evidenceRefSchema } from "./evidence.js";
|
|
3
|
+
|
|
4
|
+
/** Bump when aggregation or role-selection semantics change. */
|
|
5
|
+
export const MAP_ROLE_EVIDENCE_VERSION = 4;
|
|
6
|
+
export const T_RESPONSIBILITY_RESEARCH_PROJECTION_VERSION = "cs2-demo-analysis-kit/t-responsibility-research-projection-1.0";
|
|
7
|
+
|
|
8
|
+
/** The supported active-duty pool. Unknown maps are deliberately not generalized. */
|
|
9
|
+
export const supportedMapNameSchema = z.enum([
|
|
10
|
+
"de_ancient", "de_anubis", "de_dust2", "de_inferno", "de_mirage", "de_nuke", "de_overpass"
|
|
11
|
+
]);
|
|
12
|
+
export const mapRoleStatusSchema = z.enum(["ready", "mixed", "insufficient", "unknown"]);
|
|
13
|
+
export const inferredMapRoleSchema = z.enum(["awper", "anchor", "opener", "closer"]);
|
|
14
|
+
export const declaredRoleSchema = z.enum(["igl", "awper", "anchor", "opener", "closer"]);
|
|
15
|
+
export const weaponDutySchema = z.enum(["primary_awper", "secondary_awper", "situational_awper", "rifler"]);
|
|
16
|
+
export const teamResponsibilitySchema = z.enum([
|
|
17
|
+
"pack", "extremity", "late_joining", "stable_default",
|
|
18
|
+
"anchor_tendency", "component_mobile", "independent_mobile", "stable_position",
|
|
19
|
+
"mixed", "unknown",
|
|
20
|
+
]);
|
|
21
|
+
export const roleModifierSchema = z.enum(["utility_supportive", "positionally_stable", "spatially_isolated", "component_mobile"]);
|
|
22
|
+
export const tResponsibilityResearchCandidateSchema = z.enum(["pack", "lurker", "flexible"]);
|
|
23
|
+
|
|
24
|
+
export const tResponsibilityResearchFeaturesSchema = z.object({
|
|
25
|
+
dominantGroupStability: z.number().min(0).max(1).nullable(),
|
|
26
|
+
teamRelativeGroupShare: z.number().min(-1).max(1).nullable(),
|
|
27
|
+
openingIsolatedShare: z.number().min(0).max(1).nullable(),
|
|
28
|
+
isolationShare: z.number().min(0).max(1).nullable(),
|
|
29
|
+
delayedConvergenceShare: z.number().min(0).max(1).nullable(),
|
|
30
|
+
movementSync: z.number().min(-1).max(1).nullable(),
|
|
31
|
+
positionTopShare: z.number().min(0).max(1).nullable(),
|
|
32
|
+
openingLargestShare: z.number().min(0).max(1).nullable(),
|
|
33
|
+
fullLargestShare: z.number().min(0).max(1).nullable(),
|
|
34
|
+
meanTeamCentroidDistance: z.number().nonnegative().nullable(),
|
|
35
|
+
openingPathDisplacement: z.number().nonnegative().nullable(),
|
|
36
|
+
openingPathTransitions: z.number().nonnegative().nullable(),
|
|
37
|
+
openingPositionEntropy: z.number().min(0).max(1).nullable(),
|
|
38
|
+
fullPositionEntropy: z.number().min(0).max(1).nullable(),
|
|
39
|
+
rejoinsPerMinute: z.number().nonnegative().nullable(),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
/** Product-neutral declaration scope. Storage ids, namespaces and timestamps intentionally do not belong here. */
|
|
43
|
+
const declarationScopeSchema = z.object({
|
|
44
|
+
playerKey: z.string().min(1),
|
|
45
|
+
source: z.enum(["user", "self_report", "organizer", "event_package", "trusted_metadata"]),
|
|
46
|
+
mapName: supportedMapNameSchema.optional(),
|
|
47
|
+
teamKey: z.string().min(1).optional(),
|
|
48
|
+
validFrom: z.string().datetime().optional(),
|
|
49
|
+
validTo: z.string().datetime().optional(),
|
|
50
|
+
provenance: z.string().min(1),
|
|
51
|
+
}).strict();
|
|
52
|
+
|
|
53
|
+
/** A declared tactical role. priority belongs only to this kind of declaration. */
|
|
54
|
+
export const mainRoleDeclarationSchema = declarationScopeSchema.extend({
|
|
55
|
+
kind: z.literal("main_role"),
|
|
56
|
+
role: declaredRoleSchema,
|
|
57
|
+
priority: z.enum(["primary", "secondary"]),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
/** A declared weapon duty. It is deliberately independent from tactical-role priority. */
|
|
61
|
+
export const weaponDutyDeclarationSchema = declarationScopeSchema.extend({
|
|
62
|
+
kind: z.literal("weapon_duty"),
|
|
63
|
+
weaponDuty: weaponDutySchema,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
export const roleDeclarationSchema = z.discriminatedUnion("kind", [mainRoleDeclarationSchema, weaponDutyDeclarationSchema]).superRefine((value, context) => {
|
|
67
|
+
if (value.validFrom != null && value.validTo != null && value.validFrom > value.validTo) context.addIssue({ code: z.ZodIssueCode.custom, message: "validFrom must not be after validTo" });
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
export const roleEvidenceLocatorSchema = z.object({
|
|
71
|
+
matchId: z.string().min(1),
|
|
72
|
+
roundNumber: z.number().int().positive(),
|
|
73
|
+
positionGroupId: z.string().min(1).optional(),
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Same-event research projection. It is intentionally separate from
|
|
78
|
+
* PlayerMapRoleEvidence.responsibility and is not a production role conclusion.
|
|
79
|
+
*/
|
|
80
|
+
export const tResponsibilityResearchProjectionSchema = z.object({
|
|
81
|
+
version: z.literal(T_RESPONSIBILITY_RESEARCH_PROJECTION_VERSION),
|
|
82
|
+
modelId: z.literal("cologne-major-2026/parsimonious-v1-full-fit"),
|
|
83
|
+
playerKey: z.string().min(1),
|
|
84
|
+
teamKey: z.string().min(1),
|
|
85
|
+
side: z.literal("t"),
|
|
86
|
+
status: mapRoleStatusSchema,
|
|
87
|
+
candidate: tResponsibilityResearchCandidateSchema.nullable(),
|
|
88
|
+
packScore: z.number().min(0).max(1).nullable(),
|
|
89
|
+
lurkerScore: z.number().min(0).max(1).nullable(),
|
|
90
|
+
confidence: z.number().min(0).max(1).nullable(),
|
|
91
|
+
sample: z.object({
|
|
92
|
+
observedRounds: z.number().int().nonnegative(),
|
|
93
|
+
eligibleRounds: z.number().int().nonnegative(),
|
|
94
|
+
eligibleSeconds: z.number().nonnegative(),
|
|
95
|
+
matchCount: z.number().int().nonnegative(),
|
|
96
|
+
mapCount: z.number().int().nonnegative(),
|
|
97
|
+
dataQuality: z.number().min(0).max(1).nullable(),
|
|
98
|
+
}),
|
|
99
|
+
features: tResponsibilityResearchFeaturesSchema,
|
|
100
|
+
matchIds: z.array(z.string().min(1)),
|
|
101
|
+
representativeRounds: z.array(roleEvidenceLocatorSchema).max(8),
|
|
102
|
+
basis: z.array(z.string()),
|
|
103
|
+
limitations: z.array(z.string()),
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
export const mapPositionGroupEvidenceSchema = z.object({
|
|
107
|
+
positionGroupId: z.string().min(1),
|
|
108
|
+
seconds: z.number().nonnegative(),
|
|
109
|
+
share: z.number().min(0).max(1),
|
|
110
|
+
roundCount: z.number().int().nonnegative(),
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
export const awpResponsibilityEvidenceSchema = z.object({
|
|
114
|
+
duty: weaponDutySchema,
|
|
115
|
+
eligibleRounds: z.number().int().nonnegative(),
|
|
116
|
+
freezeOwnershipRounds: z.number().int().nonnegative(),
|
|
117
|
+
activeSeconds: z.number().nonnegative().nullable(),
|
|
118
|
+
shots: z.number().int().nonnegative().nullable(),
|
|
119
|
+
kills: z.number().int().nonnegative().nullable(),
|
|
120
|
+
teamActiveShare: z.number().min(0).max(1).nullable(),
|
|
121
|
+
/** Concentration/exclusivity of observed AWP active time inside this team/map/side cell. */
|
|
122
|
+
usageConcentration: z.number().min(0).max(1).nullable(),
|
|
123
|
+
matchConsistency: z.number().min(0).max(1).nullable(),
|
|
124
|
+
qualifiedLongGunRounds: z.number().int().nonnegative(),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
/** One identity × canonical team × map × side evidence cell. No role conclusion is stored here. */
|
|
128
|
+
export const playerMapRoleEvidenceSchema = z.object({
|
|
129
|
+
version: z.literal(MAP_ROLE_EVIDENCE_VERSION),
|
|
130
|
+
playerKey: z.string().min(1),
|
|
131
|
+
teamKey: z.string().min(1),
|
|
132
|
+
mapName: supportedMapNameSchema,
|
|
133
|
+
side: z.enum(["t", "ct"]),
|
|
134
|
+
status: mapRoleStatusSchema,
|
|
135
|
+
confidence: z.number().min(0).max(1),
|
|
136
|
+
sample: z.object({
|
|
137
|
+
observedRounds: z.number().int().nonnegative(),
|
|
138
|
+
eligibleRounds: z.number().int().nonnegative(),
|
|
139
|
+
eligibleSeconds: z.number().nonnegative(),
|
|
140
|
+
matchCount: z.number().int().nonnegative(),
|
|
141
|
+
dataQuality: z.number().min(0).max(1),
|
|
142
|
+
coverage: z.number().min(0).max(1).nullable(),
|
|
143
|
+
}),
|
|
144
|
+
/** Complete compact match coverage for aggregation and declaration time scope; not an evidence sample. */
|
|
145
|
+
matchIds: z.array(z.string().min(1)),
|
|
146
|
+
positionGroups: z.array(mapPositionGroupEvidenceSchema),
|
|
147
|
+
spatial: z.object({
|
|
148
|
+
dominantGroupStability: z.number().min(0).max(1).nullable(),
|
|
149
|
+
teamRelativeGroupShare: z.number().min(-1).max(1).nullable(),
|
|
150
|
+
isolationSeconds: z.number().nonnegative().nullable(),
|
|
151
|
+
isolationShare: z.number().min(0).max(1).nullable(),
|
|
152
|
+
rejoinCount: z.number().int().nonnegative().nullable(),
|
|
153
|
+
delayedConvergenceRoundShare: z.number().min(0).max(1).nullable(),
|
|
154
|
+
movementSync: z.number().min(-1).max(1).nullable(),
|
|
155
|
+
openingMainComponentShare: z.number().min(0).max(1).nullable(),
|
|
156
|
+
openingNoUniqueCoreShare: z.number().min(0).max(1).nullable(),
|
|
157
|
+
openingIsolatedShare: z.number().min(0).max(1).nullable(),
|
|
158
|
+
formationShares: z.record(z.number().min(0).max(1)),
|
|
159
|
+
}),
|
|
160
|
+
support: z.object({
|
|
161
|
+
utilityUses: z.number().int().nonnegative(),
|
|
162
|
+
openingUtilityUses: z.number().int().nonnegative(),
|
|
163
|
+
utilityUsePerRound: z.number().nonnegative(),
|
|
164
|
+
openingUtilityUsePerRound: z.number().nonnegative(),
|
|
165
|
+
}),
|
|
166
|
+
responsibility: teamResponsibilitySchema,
|
|
167
|
+
modifiers: z.array(roleModifierSchema),
|
|
168
|
+
awp: awpResponsibilityEvidenceSchema,
|
|
169
|
+
representativeRounds: z.array(roleEvidenceLocatorSchema).max(5),
|
|
170
|
+
basis: z.array(z.string()),
|
|
171
|
+
limitations: z.array(z.string()),
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
export const teamMapResponsibilityEvidenceSchema = z.object({
|
|
175
|
+
version: z.literal(MAP_ROLE_EVIDENCE_VERSION),
|
|
176
|
+
teamKey: z.string().min(1),
|
|
177
|
+
mapName: supportedMapNameSchema,
|
|
178
|
+
side: z.enum(["t", "ct"]),
|
|
179
|
+
status: mapRoleStatusSchema,
|
|
180
|
+
confidence: z.number().min(0).max(1),
|
|
181
|
+
players: z.array(playerMapRoleEvidenceSchema),
|
|
182
|
+
positionOverlap: z.array(z.object({ positionGroupId: z.string().min(1), playerKeys: z.array(z.string().min(1)).min(2), share: z.number().min(0).max(1) })),
|
|
183
|
+
positionConcentration: z.number().min(0).max(1).nullable(),
|
|
184
|
+
unstableCoverage: z.boolean(),
|
|
185
|
+
representativeRounds: z.array(roleEvidenceLocatorSchema).max(5),
|
|
186
|
+
basis: z.array(z.string()),
|
|
187
|
+
limitations: z.array(z.string()),
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
export const playerMapRoleProfileSchema = z.object({
|
|
191
|
+
version: z.literal("cs2-demo-analysis-kit/player-map-role-profile-5.0"),
|
|
192
|
+
playerKey: z.string().min(1),
|
|
193
|
+
teamKey: z.string().min(1),
|
|
194
|
+
declaredRoles: z.array(mainRoleDeclarationSchema),
|
|
195
|
+
declaredWeaponDuties: z.array(weaponDutyDeclarationSchema),
|
|
196
|
+
inferredPrimaryRole: inferredMapRoleSchema.nullable(),
|
|
197
|
+
runnerUpRole: inferredMapRoleSchema.nullable(),
|
|
198
|
+
separationMargin: z.number().min(0).max(1).nullable(),
|
|
199
|
+
roleEvidenceScores: z.object({ awper: z.number().min(0).max(1), anchor: z.number().min(0).max(1), opener: z.number().min(0).max(1), closer: z.number().min(0).max(1) }),
|
|
200
|
+
headlineRole: z.union([inferredMapRoleSchema, z.literal("IGL"), z.literal("IGL / AWPer")]).nullable(),
|
|
201
|
+
status: mapRoleStatusSchema,
|
|
202
|
+
confidence: z.number().min(0).max(1),
|
|
203
|
+
weaponDuty: weaponDutySchema.nullable(),
|
|
204
|
+
positionGroupDisplay: z.array(z.object({
|
|
205
|
+
mapName: supportedMapNameSchema,
|
|
206
|
+
side: z.enum(["t", "ct"]),
|
|
207
|
+
positionGroupId: z.string().min(1),
|
|
208
|
+
displayName: z.string().min(1),
|
|
209
|
+
officialName: z.string().nullable(),
|
|
210
|
+
resolved: z.boolean(),
|
|
211
|
+
})),
|
|
212
|
+
roleAlignments: z.array(z.object({
|
|
213
|
+
declaration: mainRoleDeclarationSchema,
|
|
214
|
+
declaredPrimary: declaredRoleSchema.nullable(),
|
|
215
|
+
declaredSecondary: z.array(declaredRoleSchema),
|
|
216
|
+
inferredPrimary: inferredMapRoleSchema.nullable(),
|
|
217
|
+
overall: z.enum(["aligned", "partially_aligned", "different_observation", "not_comparable"]),
|
|
218
|
+
tSide: z.string(),
|
|
219
|
+
ctSide: z.string(),
|
|
220
|
+
disagreementReasons: z.array(z.string()),
|
|
221
|
+
sampleLimitations: z.array(z.string()),
|
|
222
|
+
})),
|
|
223
|
+
weaponDutyAlignments: z.array(z.object({
|
|
224
|
+
declaration: weaponDutyDeclarationSchema,
|
|
225
|
+
observedWeaponDuty: weaponDutySchema.nullable(),
|
|
226
|
+
overall: z.enum(["aligned", "different_observation", "not_comparable"]),
|
|
227
|
+
sampleLimitations: z.array(z.string()),
|
|
228
|
+
})),
|
|
229
|
+
perMapEvidence: z.array(playerMapRoleEvidenceSchema),
|
|
230
|
+
evidence: z.array(evidenceRefSchema),
|
|
231
|
+
basis: z.array(z.string()),
|
|
232
|
+
limitations: z.array(z.string()),
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
export const teamMapRoleMatrixSchema = z.object({
|
|
236
|
+
version: z.literal("cs2-demo-analysis-kit/team-map-role-matrix-4.0"),
|
|
237
|
+
teamKey: z.string().min(1),
|
|
238
|
+
mapName: supportedMapNameSchema,
|
|
239
|
+
side: z.enum(["t", "ct"]),
|
|
240
|
+
status: mapRoleStatusSchema,
|
|
241
|
+
confidence: z.number().min(0).max(1),
|
|
242
|
+
players: z.array(z.object({
|
|
243
|
+
playerKey: z.string().min(1),
|
|
244
|
+
primaryPositionGroups: z.array(mapPositionGroupEvidenceSchema.extend({ displayName: z.string().min(1), officialName: z.string().nullable(), resolved: z.boolean() })),
|
|
245
|
+
responsibility: teamResponsibilitySchema,
|
|
246
|
+
modifiers: z.array(roleModifierSchema),
|
|
247
|
+
sampleRounds: z.number().int().nonnegative(),
|
|
248
|
+
confidence: z.number().min(0).max(1),
|
|
249
|
+
weaponDuty: weaponDutySchema.nullable(),
|
|
250
|
+
evidence: z.array(evidenceRefSchema),
|
|
251
|
+
})),
|
|
252
|
+
positionOverlap: teamMapResponsibilityEvidenceSchema.shape.positionOverlap,
|
|
253
|
+
positionConcentration: z.number().min(0).max(1).nullable(),
|
|
254
|
+
unstableCoverage: z.boolean(),
|
|
255
|
+
representativeRounds: z.array(evidenceRefSchema),
|
|
256
|
+
basis: z.array(z.string()),
|
|
257
|
+
limitations: z.array(z.string()),
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
const countBreakdownSchema = z.object({ key: z.string().min(1), rounds: z.number().int().nonnegative() });
|
|
261
|
+
export const doubleAwpAnalysisSchema = z.object({
|
|
262
|
+
version: z.literal("cs2-demo-analysis-kit/double-awp-analysis-2.0"),
|
|
263
|
+
teamKey: z.string().min(1),
|
|
264
|
+
side: z.enum(["t", "ct"]),
|
|
265
|
+
status: mapRoleStatusSchema,
|
|
266
|
+
qualifiedRoundCount: z.number().int().nonnegative(),
|
|
267
|
+
doubleAwpRoundCount: z.number().int().nonnegative(),
|
|
268
|
+
eligibleRoundShare: z.number().min(0).max(1).nullable(),
|
|
269
|
+
combinations: z.array(z.object({ playerKeys: z.array(z.string().min(1)).min(2), rounds: z.number().int().positive() })),
|
|
270
|
+
mapDistribution: z.array(countBreakdownSchema),
|
|
271
|
+
scorePhaseDistribution: z.array(countBreakdownSchema),
|
|
272
|
+
economyDistribution: z.array(countBreakdownSchema),
|
|
273
|
+
opponentEconomyDistribution: z.array(countBreakdownSchema),
|
|
274
|
+
wins: z.number().int().nonnegative(),
|
|
275
|
+
winRate: z.number().min(0).max(1).nullable(),
|
|
276
|
+
openingKills: z.number().int().nonnegative(),
|
|
277
|
+
openingDeaths: z.number().int().nonnegative(),
|
|
278
|
+
roundStartAwpOwnerships: z.number().int().nonnegative(),
|
|
279
|
+
activeAwpSeconds: z.number().nonnegative().nullable(),
|
|
280
|
+
doubleAwpActiveSeconds: z.number().nonnegative().nullable(),
|
|
281
|
+
awpKills: z.number().int().nonnegative().nullable(),
|
|
282
|
+
awpDamage: z.number().nonnegative().nullable(),
|
|
283
|
+
evidence: z.array(evidenceRefSchema),
|
|
284
|
+
basis: z.array(z.string()),
|
|
285
|
+
limitations: z.array(z.string()),
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
export const playerMapPoolRowSchema = z.object({
|
|
289
|
+
mapName: supportedMapNameSchema,
|
|
290
|
+
matchCount: z.number().int().nonnegative(), roundCount: z.number().int().nonnegative(), wins: z.number().int().nonnegative(), losses: z.number().int().nonnegative(), winRate: z.number().min(0).max(1).nullable(),
|
|
291
|
+
rr: z.number().nonnegative().nullable(), adr: z.number().nonnegative().nullable(), kast: z.number().min(0).max(100).nullable(),
|
|
292
|
+
openingKills: z.number().int().nonnegative(), openingDeaths: z.number().int().nonnegative(),
|
|
293
|
+
mainWeapon: z.string().nullable(), globalWeaponDuty: weaponDutySchema.nullable(), mapSideAwpUsage: z.array(z.object({ side: z.enum(["t", "ct"]), duty: weaponDutySchema, qualifiedRounds: z.number().int().nonnegative(), activeSeconds: z.number().nonnegative().nullable() })),
|
|
294
|
+
tPositionGroup: z.string().nullable(), ctPositionGroup: z.string().nullable(), tResponsibility: teamResponsibilitySchema, ctResponsibility: teamResponsibilitySchema,
|
|
295
|
+
sampleQuality: z.number().min(0).max(1), confidence: z.number().min(0).max(1), evidence: z.array(evidenceRefSchema),
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
export type SupportedMapName = z.infer<typeof supportedMapNameSchema>;
|
|
299
|
+
export type MapRoleStatus = z.infer<typeof mapRoleStatusSchema>;
|
|
300
|
+
export type InferredMapRole = z.infer<typeof inferredMapRoleSchema>;
|
|
301
|
+
export type DeclaredRole = z.infer<typeof declaredRoleSchema>;
|
|
302
|
+
export type WeaponDuty = z.infer<typeof weaponDutySchema>;
|
|
303
|
+
export type TeamResponsibility = z.infer<typeof teamResponsibilitySchema>;
|
|
304
|
+
export type RoleModifier = z.infer<typeof roleModifierSchema>;
|
|
305
|
+
export type TResponsibilityResearchCandidate = z.infer<typeof tResponsibilityResearchCandidateSchema>;
|
|
306
|
+
export type TResponsibilityResearchFeatures = z.infer<typeof tResponsibilityResearchFeaturesSchema>;
|
|
307
|
+
export type TResponsibilityResearchProjection = z.infer<typeof tResponsibilityResearchProjectionSchema>;
|
|
308
|
+
export type RoleDeclaration = z.infer<typeof roleDeclarationSchema>;
|
|
309
|
+
export type MainRoleDeclaration = z.infer<typeof mainRoleDeclarationSchema>;
|
|
310
|
+
export type WeaponDutyDeclaration = z.infer<typeof weaponDutyDeclarationSchema>;
|
|
311
|
+
export type RoleEvidenceLocator = z.infer<typeof roleEvidenceLocatorSchema>;
|
|
312
|
+
export type PlayerMapRoleEvidence = z.infer<typeof playerMapRoleEvidenceSchema>;
|
|
313
|
+
export type TeamMapResponsibilityEvidence = z.infer<typeof teamMapResponsibilityEvidenceSchema>;
|
|
314
|
+
export type PlayerMapRoleProfile = z.infer<typeof playerMapRoleProfileSchema>;
|
|
315
|
+
export type TeamMapRoleMatrix = z.infer<typeof teamMapRoleMatrixSchema>;
|
|
316
|
+
export type DoubleAwpAnalysis = z.infer<typeof doubleAwpAnalysisSchema>;
|
|
317
|
+
export type PlayerMapPoolRow = z.infer<typeof playerMapPoolRowSchema>;
|
package/src/player.ts
CHANGED
|
@@ -11,16 +11,26 @@ import { leaderboardMetricKeySchema } from "./leaderboard.js";
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
export const rrBreakdownEntrySchema = z.object({
|
|
14
|
-
key: z.enum(["combat", "trade", "clutch", "objective", "utility"]),
|
|
14
|
+
key: z.enum(["combat", "trade", "mapControl", "clutch", "objective", "utility"]),
|
|
15
15
|
label: z.string(),
|
|
16
16
|
value: z.number()
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
/** PRISM
|
|
19
|
+
/** PRISM 单根轴的展示项:行为倾向与执行效率分离,避免把角色、频率和效果混成一个分数。 */
|
|
20
20
|
export const playerStyleAxisSchema = z.object({
|
|
21
21
|
key: z.string(), // PrismAxisKey
|
|
22
22
|
label: z.string(),
|
|
23
|
-
|
|
23
|
+
/** 该轴在当前 cohort 内的行为参与度相对位置。 */
|
|
24
|
+
involvementPercentile: z.number().min(0).max(100).nullable(),
|
|
25
|
+
/** 该轴在当前 cohort 内的执行效率相对位置。 */
|
|
26
|
+
efficiencyPercentile: z.number().min(0).max(100).nullable(),
|
|
27
|
+
/** 上游融合值,仅供解释/导出,不再用作雷达形状。 */
|
|
28
|
+
combinedPercentile: z.number().min(0).max(100).nullable(),
|
|
29
|
+
/** 上游配置中可用信号权重占比,0–1。 */
|
|
30
|
+
signalCoverage: z.number().min(0).max(1),
|
|
31
|
+
/** 可供当前 cohort 排名的有效选手数;不足 5 人时不展示精确 P 值。 */
|
|
32
|
+
comparisonCount: z.number().int().nonnegative(),
|
|
33
|
+
status: z.enum(["ready", "partial", "unavailable", "insufficient"])
|
|
24
34
|
});
|
|
25
35
|
|
|
26
36
|
export const playerStyleSchema = z.object({
|
|
@@ -51,7 +61,8 @@ export const playerWeaponProfileEntrySchema = z.object({
|
|
|
51
61
|
});
|
|
52
62
|
|
|
53
63
|
export const playerSeasonProfileSchema = z.object({
|
|
54
|
-
|
|
64
|
+
/** 0.2:PRISM 轴拆分为行为倾向、执行效率,并附带信号/样本可用性。 */
|
|
65
|
+
version: z.literal("cs2-demo-analysis-kit/player-profile-0.2"),
|
|
55
66
|
weightsVersion: z.string(),
|
|
56
67
|
playerKey: z.string(),
|
|
57
68
|
name: z.string(),
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RadarField —— 雷达覆盖场合同(描述性地图控制原语)。
|
|
3
|
+
*
|
|
4
|
+
* 「基础场」逐秒 × 逐格存原始计数(不存概率、不存合成视图),因为原始计数是
|
|
5
|
+
* 加性的:合并两个 scope = 四场逐元素相加 + denom 相加。这一条承重墙撑起 per-match
|
|
6
|
+
* 缓存、scope 聚合、队伍−联赛差分、未来价值学习——全是廉价派生。
|
|
7
|
+
*
|
|
8
|
+
* 归一化(count / denom = 该秒该格被看到/占据/听到的采样占比)、信息差分(tVis−ctVis)、
|
|
9
|
+
* 对拼线(min(tVis,ctVis))、推进波前都在渲染期合成,底层场不污染。
|
|
10
|
+
*
|
|
11
|
+
* 这是 TS 侧计算产物,不跨 Python↔TS 的 v3 ZIP seam,故用手写 interface(非 Zod)。
|
|
12
|
+
* 字段列用 Int32Array:结构化克隆(worker postMessage / IndexedDB)下紧凑且无损。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 基础场名。
|
|
17
|
+
* Vis = 4:3 屏幕可见(矩形视锥 ∩ LOS ∩ 烟);Aim = 准星/注意力覆盖(30°圆锥 ∩ LOS ∩ 烟);
|
|
18
|
+
* Pres = 位置占据(最近 nav 格,无 LOS);Sound = 在该格发声会被对面听到的风险(800u 半径)。
|
|
19
|
+
*/
|
|
20
|
+
export type RadarFieldBase =
|
|
21
|
+
| "ctVis" | "tVis"
|
|
22
|
+
| "ctAim" | "tAim"
|
|
23
|
+
| "ctPres" | "tPres"
|
|
24
|
+
| "ctSound" | "tSound";
|
|
25
|
+
|
|
26
|
+
export const RADAR_FIELD_BASES: readonly RadarFieldBase[] = [
|
|
27
|
+
"ctVis", "tVis",
|
|
28
|
+
"ctAim", "tAim",
|
|
29
|
+
"ctPres", "tPres",
|
|
30
|
+
"ctSound", "tSound",
|
|
31
|
+
] as const;
|
|
32
|
+
|
|
33
|
+
export interface RadarFieldGrid {
|
|
34
|
+
/** 规则栅格边长(世界单位)。 */
|
|
35
|
+
cellSize: number;
|
|
36
|
+
/** 每格代表世界坐标 [x, y, z](z 已含靶高),与 fields 列同序。 */
|
|
37
|
+
cells: Array<[number, number, number]>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface RadarFieldScope {
|
|
41
|
+
/** league = 赛事/当前范围基线;team = 单队(identity 归并后的显示名)。 */
|
|
42
|
+
kind: "league" | "team";
|
|
43
|
+
team: string | null;
|
|
44
|
+
/** gun = 长枪局(双方 full/conversion);all = 全部回合。 */
|
|
45
|
+
economy: "gun" | "all";
|
|
46
|
+
/** 贡献的回合总数(含全部采样秒之前的回合计数)。 */
|
|
47
|
+
roundCount: number;
|
|
48
|
+
matchIds: string[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface RadarField {
|
|
52
|
+
/** 合同结构版本。 */
|
|
53
|
+
schemaVersion: number;
|
|
54
|
+
/** 算法参数指纹(锥角/采样率/格大小/眼高变了即 +1);驱动缓存失效。 */
|
|
55
|
+
computeVersion: number;
|
|
56
|
+
mapName: string;
|
|
57
|
+
/** world→radar 标定版本;变了不可与旧场合并/差分。 */
|
|
58
|
+
calibrationVersion: string;
|
|
59
|
+
/** full = 这批 LOS 射线可信(有 .tri);none = 只有锥+烟,无墙体遮挡,不可与 full 互比。 */
|
|
60
|
+
triAvailability: "full" | "none";
|
|
61
|
+
scope: RadarFieldScope;
|
|
62
|
+
grid: RadarFieldGrid;
|
|
63
|
+
/** 采样秒上界(一回合 1:55 = 115s)。 */
|
|
64
|
+
maxSec: number;
|
|
65
|
+
/**
|
|
66
|
+
* 归一化分母,按 source side 分。当前计算每秒采样一次:
|
|
67
|
+
* denomCt[sec] = scope 内、CT 侧、到达该秒的 side-frame 数;ct* 基础场除以它。
|
|
68
|
+
* denomT 同理给 t* 基础场。
|
|
69
|
+
*/
|
|
70
|
+
denomCt: Int32Array;
|
|
71
|
+
denomT: Int32Array;
|
|
72
|
+
/** fields[base][sec] = Int32Array(cells.length) 原始计数。 */
|
|
73
|
+
fields: Record<RadarFieldBase, Int32Array[]>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const RADAR_FIELD_SCHEMA_VERSION = 1;
|
|
77
|
+
|
|
78
|
+
/** 一回合 1:55 = 115s(freeze 后逐秒采样窗,含残局/换防段;已实证)。 */
|
|
79
|
+
export const RADAR_FIELD_MAX_SEC = 115;
|
package/src/scoring.ts
CHANGED
|
@@ -76,4 +76,12 @@ export const rrIndicatorsSchema: z.ZodType<RRIndicators> = z.object({
|
|
|
76
76
|
roundSwingPerKill: z.number().nullable()
|
|
77
77
|
});
|
|
78
78
|
|
|
79
|
-
export type {
|
|
79
|
+
export type {
|
|
80
|
+
CohortAccountResult,
|
|
81
|
+
PrismResult,
|
|
82
|
+
RRIndicators,
|
|
83
|
+
RRResult,
|
|
84
|
+
RRSignals,
|
|
85
|
+
RRSixAccountResult,
|
|
86
|
+
RRSixAccountWeights
|
|
87
|
+
} from "@rivalhub/rival-rating";
|
package/src/trails.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { sideSchema, teamEconomySchema, grenadeTypeSchema } from "cs2-demo-format";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 开局动线:单选手在指定经济类型回合(默认长枪局)开局窗口内的
|
|
6
|
+
* 走位轨迹 + 道具投掷。由 @cs2dak/presentation 从 replay/rounds/grenades 派生,
|
|
7
|
+
* 坐标保持 world 坐标系,radar 投影由渲染端(@cs2dak/maps)完成。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const trailPointSchema = z.object({
|
|
11
|
+
/** 距 freeze 结束的秒数。 */
|
|
12
|
+
t: z.number().nonnegative(),
|
|
13
|
+
x: z.number(),
|
|
14
|
+
y: z.number()
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const trailGrenadeSchema = z.object({
|
|
18
|
+
/** 投掷时刻(距 freeze 结束的秒数)。 */
|
|
19
|
+
t: z.number().nonnegative(),
|
|
20
|
+
x: z.number(),
|
|
21
|
+
y: z.number(),
|
|
22
|
+
grenade: grenadeTypeSchema,
|
|
23
|
+
/** 生效时刻(距 freeze 结束的秒数)。 */
|
|
24
|
+
effectT: z.number().nonnegative(),
|
|
25
|
+
/** 效果消失时刻;导出包缺失时 null,渲染端按道具类型取默认时长。 */
|
|
26
|
+
destroyT: z.number().nonnegative().nullable(),
|
|
27
|
+
/** 落点(world 坐标)。 */
|
|
28
|
+
effectX: z.number(),
|
|
29
|
+
effectY: z.number()
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const openingTrailRoundSchema = z.object({
|
|
33
|
+
matchId: z.string(),
|
|
34
|
+
roundNumber: z.number().int().positive(),
|
|
35
|
+
side: sideSchema,
|
|
36
|
+
economyType: teamEconomySchema,
|
|
37
|
+
points: z.array(trailPointSchema),
|
|
38
|
+
grenades: z.array(trailGrenadeSchema)
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export const openingTrailsModelSchema = z.object({
|
|
42
|
+
version: z.literal("cs2-demo-analysis-kit/opening-trails-0.2"),
|
|
43
|
+
matchId: z.string(),
|
|
44
|
+
mapName: z.string(),
|
|
45
|
+
steamId64: z.string(),
|
|
46
|
+
playerName: z.string(),
|
|
47
|
+
/** 该导出包是否含回放流;false 时 rounds 为空。 */
|
|
48
|
+
available: z.boolean(),
|
|
49
|
+
windowSeconds: z.number().positive(),
|
|
50
|
+
rounds: z.array(openingTrailRoundSchema)
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export type TrailPoint = z.infer<typeof trailPointSchema>;
|
|
54
|
+
export type TrailGrenade = z.infer<typeof trailGrenadeSchema>;
|
|
55
|
+
export type OpeningTrailRound = z.infer<typeof openingTrailRoundSchema>;
|
|
56
|
+
export type OpeningTrailsModel = z.infer<typeof openingTrailsModelSchema>;
|
package/src/upstream.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
economyTypeSchema,
|
|
6
6
|
teamEconomySchema,
|
|
7
7
|
vec3Schema,
|
|
8
|
+
playerIndexSchema,
|
|
8
9
|
playerRowSchema,
|
|
9
10
|
roundRowSchema,
|
|
10
11
|
killRowSchema,
|
|
@@ -15,11 +16,16 @@ import {
|
|
|
15
16
|
bombRowSchema,
|
|
16
17
|
grenadeRowSchema,
|
|
17
18
|
clutchRowSchema,
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
shotTrackSchema,
|
|
20
|
+
shotsSchema,
|
|
20
21
|
replaySchema,
|
|
21
22
|
replayRoundSchema,
|
|
22
23
|
replayPlayerTrackSchema,
|
|
24
|
+
replayProjectileSchema,
|
|
25
|
+
duelsSchema,
|
|
26
|
+
duelWindowSchema,
|
|
27
|
+
duelPlayerTrackSchema,
|
|
28
|
+
duelAnchorSchema,
|
|
23
29
|
} from "cs2-demo-format";
|
|
24
30
|
|
|
25
31
|
export {
|
|
@@ -33,8 +39,16 @@ export {
|
|
|
33
39
|
clutchesSchema,
|
|
34
40
|
damageRowSchema,
|
|
35
41
|
damagesSchema,
|
|
42
|
+
decodeDelta,
|
|
43
|
+
duelAnchorSchema,
|
|
44
|
+
duelPlayerTrackSchema,
|
|
45
|
+
duelsSchema,
|
|
46
|
+
duelWindowSchema,
|
|
36
47
|
economyTypeSchema,
|
|
37
48
|
endReasonSchema,
|
|
49
|
+
FLAG_ALIVE,
|
|
50
|
+
FLAG_HAS_BOMB,
|
|
51
|
+
FLAG_HAS_DEFUSE_KIT,
|
|
38
52
|
grenadeRowSchema,
|
|
39
53
|
grenadeTypeSchema,
|
|
40
54
|
grenadesSchema,
|
|
@@ -45,18 +59,18 @@ export {
|
|
|
45
59
|
matchSchema,
|
|
46
60
|
playerEconomiesSchema,
|
|
47
61
|
playerEconomyRowSchema,
|
|
62
|
+
playerIndexSchema,
|
|
48
63
|
playerRowSchema,
|
|
49
64
|
playersSchema,
|
|
50
65
|
playerStatsRowSchema,
|
|
51
66
|
playerStatsSchema,
|
|
52
|
-
positionRowSchema,
|
|
53
|
-
positionsSchema,
|
|
54
67
|
replaySchema,
|
|
55
68
|
replayPlayerTrackSchema,
|
|
69
|
+
replayProjectileSchema,
|
|
56
70
|
replayRoundSchema,
|
|
57
71
|
roundRowSchema,
|
|
58
72
|
roundsSchema,
|
|
59
|
-
|
|
73
|
+
shotTrackSchema,
|
|
60
74
|
shotsSchema,
|
|
61
75
|
sideSchema,
|
|
62
76
|
steamId64Schema,
|
|
@@ -72,6 +86,7 @@ export type TeamKey = z.infer<typeof teamKeySchema>;
|
|
|
72
86
|
export type EconomyType = z.infer<typeof economyTypeSchema>;
|
|
73
87
|
export type TeamEconomyType = z.infer<typeof teamEconomySchema>;
|
|
74
88
|
export type Vec3 = z.infer<typeof vec3Schema>;
|
|
89
|
+
export type PlayerIndex = z.infer<typeof playerIndexSchema>;
|
|
75
90
|
|
|
76
91
|
export type PackagePlayer = z.infer<typeof playerRowSchema>;
|
|
77
92
|
export type PackageRound = z.infer<typeof roundRowSchema>;
|
|
@@ -83,8 +98,13 @@ export type PackageBlind = z.infer<typeof blindRowSchema>;
|
|
|
83
98
|
export type PackageBomb = z.infer<typeof bombRowSchema>;
|
|
84
99
|
export type PackageGrenade = z.infer<typeof grenadeRowSchema>;
|
|
85
100
|
export type PackageClutch = z.infer<typeof clutchRowSchema>;
|
|
86
|
-
export type
|
|
87
|
-
export type
|
|
101
|
+
export type PackageShots = z.infer<typeof shotsSchema>;
|
|
102
|
+
export type PackageShotTrack = z.infer<typeof shotTrackSchema>;
|
|
88
103
|
export type Replay = z.infer<typeof replaySchema>;
|
|
89
104
|
export type ReplayRound = z.infer<typeof replayRoundSchema>;
|
|
90
105
|
export type ReplayPlayerTrack = z.infer<typeof replayPlayerTrackSchema>;
|
|
106
|
+
export type ReplayProjectile = z.infer<typeof replayProjectileSchema>;
|
|
107
|
+
export type Duels = z.infer<typeof duelsSchema>;
|
|
108
|
+
export type DuelWindow = z.infer<typeof duelWindowSchema>;
|
|
109
|
+
export type DuelPlayerTrack = z.infer<typeof duelPlayerTrackSchema>;
|
|
110
|
+
export type DuelAnchor = z.infer<typeof duelAnchorSchema>;
|
package/src/veto.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const seriesFormatSchema = z.enum(["bo1", "bo3", "bo5"]);
|
|
4
|
+
export const vetoActionTypeSchema = z.enum(["ban", "pick", "decider"]);
|
|
5
|
+
export const vetoTeamRefSchema = z.enum(["teamA", "teamB"]).nullable();
|
|
6
|
+
|
|
7
|
+
export const seriesVetoStepSchema = z.object({
|
|
8
|
+
stepOrder: z.number().int().positive(),
|
|
9
|
+
actionType: vetoActionTypeSchema,
|
|
10
|
+
mapName: z.string().min(1),
|
|
11
|
+
teamKey: vetoTeamRefSchema,
|
|
12
|
+
side: z.enum(["t", "ct"]).nullable()
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const seriesVetoSchema = z.object({
|
|
16
|
+
version: z.literal("cs2-demo-analysis-kit/series-veto-0.1"),
|
|
17
|
+
seriesId: z.string(),
|
|
18
|
+
format: seriesFormatSchema,
|
|
19
|
+
teamAName: z.string(),
|
|
20
|
+
teamBName: z.string(),
|
|
21
|
+
mapPool: z.array(z.string().min(1)),
|
|
22
|
+
maps: z.object({
|
|
23
|
+
picked: z.array(z.object({
|
|
24
|
+
mapName: z.string().min(1),
|
|
25
|
+
teamKey: z.enum(["teamA", "teamB"]).nullable()
|
|
26
|
+
})),
|
|
27
|
+
banned: z.array(z.object({
|
|
28
|
+
mapName: z.string().min(1),
|
|
29
|
+
teamKey: z.enum(["teamA", "teamB"]).nullable()
|
|
30
|
+
})),
|
|
31
|
+
decider: z.string().min(1).nullable()
|
|
32
|
+
}),
|
|
33
|
+
sideChoices: z.array(z.object({
|
|
34
|
+
mapName: z.string().min(1),
|
|
35
|
+
teamKey: z.enum(["teamA", "teamB"]).nullable(),
|
|
36
|
+
side: z.enum(["t", "ct"])
|
|
37
|
+
})),
|
|
38
|
+
steps: z.array(seriesVetoStepSchema)
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export type SeriesFormat = z.infer<typeof seriesFormatSchema>;
|
|
42
|
+
export type VetoActionType = z.infer<typeof vetoActionTypeSchema>;
|
|
43
|
+
export type SeriesVetoStep = z.infer<typeof seriesVetoStepSchema>;
|
|
44
|
+
export type SeriesVeto = z.infer<typeof seriesVetoSchema>;
|