volleyballsimtypes 0.0.514 → 0.0.516
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/api/index.d.ts +1 -2
- package/dist/cjs/src/api/tactics-player-refs.js +2 -2
- package/dist/cjs/src/data/models/tactics.d.ts +2 -4
- package/dist/cjs/src/data/models/tactics.js +0 -5
- package/dist/cjs/src/data/transformers/tactics.js +4 -36
- package/dist/cjs/src/data/transformers/tactics.test.js +9 -46
- package/dist/cjs/src/service/team/attack-priority.d.ts +3 -0
- package/dist/cjs/src/service/team/attack-priority.js +11 -0
- package/dist/cjs/src/service/team/base-config.d.ts +2 -0
- package/dist/cjs/src/service/team/default-base-config.js +59 -3
- package/dist/cjs/src/service/team/default-base-config.test.d.ts +1 -0
- package/dist/cjs/src/service/team/default-base-config.test.js +44 -0
- package/dist/cjs/src/service/team/index.d.ts +1 -0
- package/dist/cjs/src/service/team/index.js +1 -0
- package/dist/cjs/src/service/team/offensive-preference.d.ts +2 -1
- package/dist/cjs/src/service/team/schemas/tactics.z.d.ts +19 -5
- package/dist/cjs/src/service/team/schemas/tactics.z.js +32 -33
- package/dist/cjs/src/service/team/schemas/tactics.z.test.js +25 -92
- package/dist/cjs/src/service/team/schemas/team.z.d.ts +19 -5
- package/dist/cjs/src/service/team/tactics.d.ts +0 -2
- package/dist/cjs/src/service/team/tactics.js +1 -2
- package/dist/esm/src/api/index.d.ts +1 -2
- package/dist/esm/src/api/tactics-player-refs.js +2 -2
- package/dist/esm/src/data/models/tactics.d.ts +2 -4
- package/dist/esm/src/data/models/tactics.js +0 -5
- package/dist/esm/src/data/transformers/tactics.js +4 -36
- package/dist/esm/src/data/transformers/tactics.test.js +9 -46
- package/dist/esm/src/service/team/attack-priority.d.ts +3 -0
- package/dist/esm/src/service/team/attack-priority.js +8 -0
- package/dist/esm/src/service/team/base-config.d.ts +2 -0
- package/dist/esm/src/service/team/default-base-config.js +59 -3
- package/dist/esm/src/service/team/default-base-config.test.d.ts +1 -0
- package/dist/esm/src/service/team/default-base-config.test.js +42 -0
- package/dist/esm/src/service/team/index.d.ts +1 -0
- package/dist/esm/src/service/team/index.js +1 -0
- package/dist/esm/src/service/team/offensive-preference.d.ts +2 -1
- package/dist/esm/src/service/team/schemas/tactics.z.d.ts +19 -5
- package/dist/esm/src/service/team/schemas/tactics.z.js +32 -33
- package/dist/esm/src/service/team/schemas/tactics.z.test.js +25 -92
- package/dist/esm/src/service/team/schemas/team.z.d.ts +19 -5
- package/dist/esm/src/service/team/tactics.d.ts +0 -2
- package/dist/esm/src/service/team/tactics.js +1 -2
- package/package.json +1 -1
|
@@ -94,7 +94,7 @@ describe('TacticsInputSchema — per-set tactics (systemSets / offensivePreferen
|
|
|
94
94
|
{ rotationSystem: '4-2', designatedSetters: [p1, p4] }
|
|
95
95
|
],
|
|
96
96
|
offensivePreferenceSets: [
|
|
97
|
-
[{ rotation: 1,
|
|
97
|
+
[{ rotation: 1, priorities: { [p2.id]: 'HIGH', [p3.id]: 'MID' } }],
|
|
98
98
|
[]
|
|
99
99
|
]
|
|
100
100
|
}));
|
|
@@ -103,7 +103,7 @@ describe('TacticsInputSchema — per-set tactics (systemSets / offensivePreferen
|
|
|
103
103
|
it('carries the per-set fields through Tactics.create parsing', () => {
|
|
104
104
|
const res = TacticsInputSchema.safeParse(baseInput({
|
|
105
105
|
systemSets: [{ rotationSystem: '5-1', designatedSetters: [p1] }],
|
|
106
|
-
offensivePreferenceSets: [[{ rotation: 2,
|
|
106
|
+
offensivePreferenceSets: [[{ rotation: 2, priorities: { [p3.id]: 'HIGH' } }]]
|
|
107
107
|
}));
|
|
108
108
|
expect(res.success).toBe(true);
|
|
109
109
|
if (res.success) {
|
|
@@ -152,7 +152,7 @@ describe('TacticsInputSchema — per-set tactics (systemSets / offensivePreferen
|
|
|
152
152
|
});
|
|
153
153
|
it('rejects a per-set preferences list with a duplicate rotation', () => {
|
|
154
154
|
const res = TacticsInputSchema.safeParse(baseInput({
|
|
155
|
-
offensivePreferenceSets: [[{ rotation: 1,
|
|
155
|
+
offensivePreferenceSets: [[{ rotation: 1, priorities: { [p2.id]: 'HIGH' } }, { rotation: 1, priorities: { [p3.id]: 'HIGH' } }]]
|
|
156
156
|
}));
|
|
157
157
|
expect(res.success).toBe(false);
|
|
158
158
|
if (!res.success) {
|
|
@@ -162,25 +162,38 @@ describe('TacticsInputSchema — per-set tactics (systemSets / offensivePreferen
|
|
|
162
162
|
});
|
|
163
163
|
it('rejects a per-set preferences map that keys a non-starter', () => {
|
|
164
164
|
const res = TacticsInputSchema.safeParse(baseInput({
|
|
165
|
-
offensivePreferenceSets: [[{ rotation: 1,
|
|
165
|
+
offensivePreferenceSets: [[{ rotation: 1, priorities: { [reserve.id]: 'HIGH' } }]]
|
|
166
166
|
}));
|
|
167
167
|
expect(res.success).toBe(false);
|
|
168
168
|
if (!res.success) {
|
|
169
169
|
expect(res.error.issues.some(i => i.message === 'OFFENSIVE_PREFERENCE_ATTACKER_NOT_A_STARTER')).toBe(true);
|
|
170
170
|
}
|
|
171
171
|
});
|
|
172
|
-
it('
|
|
172
|
+
it('accepts offensive priorities with no sum constraint', () => {
|
|
173
173
|
const res = TacticsInputSchema.safeParse(baseInput({
|
|
174
|
-
offensivePreferences: [{ rotation: 1,
|
|
174
|
+
offensivePreferences: [{ rotation: 1, priorities: { [p2.id]: 'HIGH', [p3.id]: 'LOW' } }]
|
|
175
|
+
}));
|
|
176
|
+
expect(res.success).toBe(true);
|
|
177
|
+
});
|
|
178
|
+
it('rejects an invalid attack priority value', () => {
|
|
179
|
+
const res = TacticsInputSchema.safeParse(baseInput({
|
|
180
|
+
offensivePreferences: [{ rotation: 1, priorities: { [p2.id]: 'MEGA' } }]
|
|
175
181
|
}));
|
|
176
182
|
expect(res.success).toBe(false);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
183
|
+
});
|
|
184
|
+
it('accepts a priority / tempo / no-block keyed by a designated substitute', () => {
|
|
185
|
+
const res = TacticsInputSchema.safeParse(baseInput({
|
|
186
|
+
lineup: { 4: p4, 3: p3, 2: p2, 5: p5, 6: p6, 1: p1, bench: [reserve] },
|
|
187
|
+
designatedSubs: [{ starter: p2, bench: reserve }],
|
|
188
|
+
offensivePreferences: [{ rotation: 1, priorities: { [reserve.id]: 'HIGH' }, tempos: { [reserve.id]: 'FAST' } }],
|
|
189
|
+
blockingPreferences: [{ rotation: 1, commit: {}, noBlock: { DEF_L: [reserve.id], DEF_M: [], DEF_R: [] } }],
|
|
190
|
+
basePositions: { coords: {}, receive: {}, receiveByPlayer: { 1: { [reserve.id]: false } } }
|
|
191
|
+
}));
|
|
192
|
+
expect(res.success).toBe(true);
|
|
180
193
|
});
|
|
181
194
|
it('accepts offensive preferences with a starter-keyed tempos map', () => {
|
|
182
195
|
const res = TacticsInputSchema.safeParse(baseInput({
|
|
183
|
-
offensivePreferences: [{ rotation: 1,
|
|
196
|
+
offensivePreferences: [{ rotation: 1, priorities: { [p2.id]: 'HIGH', [p3.id]: 'MID' }, tempos: { [p2.id]: 'FAST', [p3.id]: 'HIGH' } }]
|
|
184
197
|
}));
|
|
185
198
|
expect(res.success).toBe(true);
|
|
186
199
|
if (res.success) {
|
|
@@ -189,7 +202,7 @@ describe('TacticsInputSchema — per-set tactics (systemSets / offensivePreferen
|
|
|
189
202
|
});
|
|
190
203
|
it('rejects a tempos map keyed by a non-starter', () => {
|
|
191
204
|
const res = TacticsInputSchema.safeParse(baseInput({
|
|
192
|
-
offensivePreferences: [{ rotation: 1,
|
|
205
|
+
offensivePreferences: [{ rotation: 1, priorities: { [p2.id]: 'HIGH' }, tempos: { [reserve.id]: 'FAST' } }]
|
|
193
206
|
}));
|
|
194
207
|
expect(res.success).toBe(false);
|
|
195
208
|
if (!res.success) {
|
|
@@ -198,7 +211,7 @@ describe('TacticsInputSchema — per-set tactics (systemSets / offensivePreferen
|
|
|
198
211
|
});
|
|
199
212
|
it('rejects an invalid tempo value', () => {
|
|
200
213
|
const res = TacticsInputSchema.safeParse(baseInput({
|
|
201
|
-
offensivePreferences: [{ rotation: 1,
|
|
214
|
+
offensivePreferences: [{ rotation: 1, priorities: { [p2.id]: 'HIGH' }, tempos: { [p2.id]: 'SLOW' } }]
|
|
202
215
|
}));
|
|
203
216
|
expect(res.success).toBe(false);
|
|
204
217
|
});
|
|
@@ -323,83 +336,3 @@ describe('TacticsInputSchema — blocking preferences', () => {
|
|
|
323
336
|
expect(res.success).toBe(false);
|
|
324
337
|
});
|
|
325
338
|
});
|
|
326
|
-
describe('TacticsInputSchema — serve attack shares', () => {
|
|
327
|
-
const country = makeCountry();
|
|
328
|
-
const p1 = makePlayer(country);
|
|
329
|
-
const p2 = makePlayer(country);
|
|
330
|
-
const p3 = makePlayer(country);
|
|
331
|
-
const p4 = makePlayer(country);
|
|
332
|
-
const p5 = makePlayer(country);
|
|
333
|
-
const p6 = makePlayer(country);
|
|
334
|
-
const reserve = makePlayer(country);
|
|
335
|
-
// p5 sits at zone 5 (back row) and is the configured libero replacement, so it may carry a serve share.
|
|
336
|
-
function baseInput(overrides = {}) {
|
|
337
|
-
return {
|
|
338
|
-
lineup: { 4: p4, 3: p3, 2: p2, 5: p5, 6: p6, 1: p1, bench: [] },
|
|
339
|
-
liberoReplacements: [p5],
|
|
340
|
-
...overrides
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
it('accepts a serve share keyed by a libero replacement, value in band', () => {
|
|
344
|
-
const res = TacticsInputSchema.safeParse(baseInput({ serveAttackShares: { [p5.id]: 30 } }));
|
|
345
|
-
expect(res.success).toBe(true);
|
|
346
|
-
if (res.success)
|
|
347
|
-
expect(res.data.serveAttackShares[p5.id]).toBe(30);
|
|
348
|
-
});
|
|
349
|
-
it('accepts 0 and 100 at the band edges', () => {
|
|
350
|
-
expect(TacticsInputSchema.safeParse(baseInput({ serveAttackShares: { [p5.id]: 0 } })).success).toBe(true);
|
|
351
|
-
expect(TacticsInputSchema.safeParse(baseInput({ serveAttackShares: { [p5.id]: 100 } })).success).toBe(true);
|
|
352
|
-
});
|
|
353
|
-
it('rejects a serve share keyed by a starter who is not a libero replacement', () => {
|
|
354
|
-
const res = TacticsInputSchema.safeParse(baseInput({ serveAttackShares: { [p2.id]: 30 } }));
|
|
355
|
-
expect(res.success).toBe(false);
|
|
356
|
-
if (!res.success) {
|
|
357
|
-
expect(res.error.issues.some(i => i.message === 'SERVE_ATTACK_SHARE_NOT_A_LIBERO_REPLACEMENT')).toBe(true);
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
|
-
it('rejects a serve share keyed by a reserve', () => {
|
|
361
|
-
const res = TacticsInputSchema.safeParse(baseInput({ serveAttackShares: { [reserve.id]: 30 } }));
|
|
362
|
-
expect(res.success).toBe(false);
|
|
363
|
-
if (!res.success) {
|
|
364
|
-
expect(res.error.issues.some(i => i.message === 'SERVE_ATTACK_SHARE_NOT_A_LIBERO_REPLACEMENT')).toBe(true);
|
|
365
|
-
}
|
|
366
|
-
});
|
|
367
|
-
it('rejects an out-of-band serve share (over 100)', () => {
|
|
368
|
-
const res = TacticsInputSchema.safeParse(baseInput({ serveAttackShares: { [p5.id]: 101 } }));
|
|
369
|
-
expect(res.success).toBe(false);
|
|
370
|
-
if (!res.success) {
|
|
371
|
-
expect(res.error.issues.some(i => i.message === 'SERVE_ATTACK_SHARE_OUT_OF_BAND')).toBe(true);
|
|
372
|
-
}
|
|
373
|
-
});
|
|
374
|
-
it('rejects a negative serve share', () => {
|
|
375
|
-
const res = TacticsInputSchema.safeParse(baseInput({ serveAttackShares: { [p5.id]: -1 } }));
|
|
376
|
-
expect(res.success).toBe(false);
|
|
377
|
-
if (!res.success) {
|
|
378
|
-
expect(res.error.issues.some(i => i.message === 'SERVE_ATTACK_SHARE_OUT_OF_BAND')).toBe(true);
|
|
379
|
-
}
|
|
380
|
-
});
|
|
381
|
-
it('validates independently of the offensive-preference sum-to-100 (never enters that total)', () => {
|
|
382
|
-
// The other attackers' weights sum to exactly 100; the serve share on the libero replacement is separate
|
|
383
|
-
// and must not push the rotation over 100 or otherwise fail.
|
|
384
|
-
const res = TacticsInputSchema.safeParse(baseInput({
|
|
385
|
-
offensivePreferences: [{ rotation: 1, weights: { [p2.id]: 50, [p3.id]: 50 } }],
|
|
386
|
-
serveAttackShares: { [p5.id]: 40 }
|
|
387
|
-
}));
|
|
388
|
-
expect(res.success).toBe(true);
|
|
389
|
-
});
|
|
390
|
-
it('accepts a serve share on a per-set libero replacement', () => {
|
|
391
|
-
// p6 is a back-row starter made a libero replacement only via liberoReplacementSets.
|
|
392
|
-
const res = TacticsInputSchema.safeParse(baseInput({
|
|
393
|
-
liberoReplacements: [],
|
|
394
|
-
liberoReplacementSets: [[p6]],
|
|
395
|
-
serveAttackShares: { [p6.id]: 25 }
|
|
396
|
-
}));
|
|
397
|
-
expect(res.success).toBe(true);
|
|
398
|
-
});
|
|
399
|
-
it('defaults serveAttackShares to an empty map when omitted', () => {
|
|
400
|
-
const res = TacticsInputSchema.safeParse(baseInput());
|
|
401
|
-
expect(res.success).toBe(true);
|
|
402
|
-
if (res.success)
|
|
403
|
-
expect(res.data.serveAttackShares).toEqual({});
|
|
404
|
-
});
|
|
405
|
-
});
|
|
@@ -269,23 +269,32 @@ export declare const TeamInputSchema: z.ZodObject<{
|
|
|
269
269
|
designatedSetters: z.ZodDefault<z.ZodArray<z.ZodCustom<Player, Player>>>;
|
|
270
270
|
offensivePreferences: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
271
271
|
rotation: z.ZodNumber;
|
|
272
|
-
|
|
272
|
+
priorities: z.ZodRecord<z.ZodString, z.ZodEnum<{
|
|
273
|
+
HIGH: "HIGH";
|
|
274
|
+
MID: "MID";
|
|
275
|
+
LOW: "LOW";
|
|
276
|
+
NONE: "NONE";
|
|
277
|
+
}>>;
|
|
273
278
|
tempos: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<{
|
|
274
|
-
FAST: "FAST";
|
|
275
279
|
HIGH: "HIGH";
|
|
280
|
+
FAST: "FAST";
|
|
276
281
|
}>>>;
|
|
277
282
|
}, z.core.$strip>>>;
|
|
278
|
-
serveAttackShares: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodNumber>>;
|
|
279
283
|
systemSets: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
280
284
|
rotationSystem: z.ZodEnum<typeof import("..").RotationSystemEnum>;
|
|
281
285
|
designatedSetters: z.ZodArray<z.ZodCustom<Player, Player>>;
|
|
282
286
|
}, z.core.$strip>>>;
|
|
283
287
|
offensivePreferenceSets: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
|
|
284
288
|
rotation: z.ZodNumber;
|
|
285
|
-
|
|
289
|
+
priorities: z.ZodRecord<z.ZodString, z.ZodEnum<{
|
|
290
|
+
HIGH: "HIGH";
|
|
291
|
+
MID: "MID";
|
|
292
|
+
LOW: "LOW";
|
|
293
|
+
NONE: "NONE";
|
|
294
|
+
}>>;
|
|
286
295
|
tempos: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<{
|
|
287
|
-
FAST: "FAST";
|
|
288
296
|
HIGH: "HIGH";
|
|
297
|
+
FAST: "FAST";
|
|
289
298
|
}>>>;
|
|
290
299
|
}, z.core.$strip>>>>;
|
|
291
300
|
blockingPreferences: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
@@ -312,7 +321,12 @@ export declare const TeamInputSchema: z.ZodObject<{
|
|
|
312
321
|
r: z.ZodNumber;
|
|
313
322
|
}, z.core.$strip>>>>;
|
|
314
323
|
receive: z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodBoolean>>;
|
|
324
|
+
receiveByPlayer: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodBoolean>>>;
|
|
315
325
|
blocking: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodBoolean>>>>;
|
|
326
|
+
attackContact: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
327
|
+
d: z.ZodNumber;
|
|
328
|
+
r: z.ZodNumber;
|
|
329
|
+
}, z.core.$strip>>>>;
|
|
316
330
|
}, z.core.$strip>>;
|
|
317
331
|
replaceKnockedImmediately: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodBoolean>>;
|
|
318
332
|
injuryReplacements: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
@@ -35,7 +35,6 @@ export interface TacticsOpts {
|
|
|
35
35
|
readonly rotationSystem: RotationSystemEnum;
|
|
36
36
|
readonly designatedSetters: Player[];
|
|
37
37
|
readonly offensivePreferences: OffensivePreference[];
|
|
38
|
-
readonly serveAttackShares: Record<string, number>;
|
|
39
38
|
readonly systemSets?: SystemSet[];
|
|
40
39
|
readonly offensivePreferenceSets?: OffensivePreference[][];
|
|
41
40
|
readonly replaceKnockedImmediately: Record<string, boolean>;
|
|
@@ -57,7 +56,6 @@ export declare class Tactics {
|
|
|
57
56
|
readonly rotationSystem: RotationSystemEnum;
|
|
58
57
|
readonly designatedSetters: Player[];
|
|
59
58
|
readonly offensivePreferences: OffensivePreference[];
|
|
60
|
-
readonly serveAttackShares: Record<string, number>;
|
|
61
59
|
readonly systemSets?: SystemSet[];
|
|
62
60
|
readonly offensivePreferenceSets?: OffensivePreference[][];
|
|
63
61
|
readonly replaceKnockedImmediately: Record<string, boolean>;
|
|
@@ -15,7 +15,7 @@ export class Tactics {
|
|
|
15
15
|
}
|
|
16
16
|
return new Tactics(result.data);
|
|
17
17
|
}
|
|
18
|
-
constructor({ lineup, liberoReplacements, receiveRotationOffset = false, designatedSubs = [], substitutionBand = EnergyBand.TIRED, secondLibero, liberoSub, liberoReplacementSets, rotationSystem = RotationSystemEnum.SIX_ZERO, designatedSetters = [], offensivePreferences = [],
|
|
18
|
+
constructor({ lineup, liberoReplacements, receiveRotationOffset = false, designatedSubs = [], substitutionBand = EnergyBand.TIRED, secondLibero, liberoSub, liberoReplacementSets, rotationSystem = RotationSystemEnum.SIX_ZERO, designatedSetters = [], offensivePreferences = [], systemSets, offensivePreferenceSets, replaceKnockedImmediately = {}, injuryReplacements = [], liberoInjury, blockingPreferences = [], blockingPreferenceSets, basePositions }) {
|
|
19
19
|
this.lineup = lineup;
|
|
20
20
|
this.liberoReplacements = liberoReplacements;
|
|
21
21
|
this.receiveRotationOffset = receiveRotationOffset;
|
|
@@ -27,7 +27,6 @@ export class Tactics {
|
|
|
27
27
|
this.rotationSystem = rotationSystem;
|
|
28
28
|
this.designatedSetters = designatedSetters;
|
|
29
29
|
this.offensivePreferences = offensivePreferences;
|
|
30
|
-
this.serveAttackShares = serveAttackShares;
|
|
31
30
|
this.systemSets = systemSets;
|
|
32
31
|
this.offensivePreferenceSets = offensivePreferenceSets;
|
|
33
32
|
this.replaceKnockedImmediately = replaceKnockedImmediately;
|