volleyballsimtypes 0.0.393 → 0.0.395
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 +8 -1
- package/dist/cjs/src/data/models/tactics.d.ts +12 -2
- package/dist/cjs/src/data/models/tactics.js +15 -0
- package/dist/cjs/src/data/transformers/tactics.js +12 -0
- package/dist/cjs/src/service/team/base-position.d.ts +25 -0
- package/dist/cjs/src/service/team/base-position.js +113 -0
- package/dist/cjs/src/service/team/base-position.test.d.ts +1 -0
- package/dist/cjs/src/service/team/base-position.test.js +175 -0
- package/dist/cjs/src/service/team/index.d.ts +4 -0
- package/dist/cjs/src/service/team/index.js +4 -0
- package/dist/cjs/src/service/team/lineup-function.d.ts +13 -0
- package/dist/cjs/src/service/team/lineup-function.js +67 -0
- package/dist/cjs/src/service/team/offensive-preference.d.ts +5 -0
- package/dist/cjs/src/service/team/offensive-preference.js +2 -0
- package/dist/cjs/src/service/team/rotation-system.d.ts +8 -0
- package/dist/cjs/src/service/team/rotation-system.js +25 -0
- package/dist/cjs/src/service/team/schemas/tactics.z.d.ts +7 -0
- package/dist/cjs/src/service/team/schemas/tactics.z.js +67 -1
- package/dist/cjs/src/service/team/schemas/team.z.d.ts +6 -0
- package/dist/cjs/src/service/team/tactics.d.ts +8 -0
- package/dist/cjs/src/service/team/tactics.js +5 -1
- package/dist/esm/src/api/index.d.ts +8 -1
- package/dist/esm/src/data/models/tactics.d.ts +12 -2
- package/dist/esm/src/data/models/tactics.js +15 -0
- package/dist/esm/src/data/transformers/tactics.js +12 -0
- package/dist/esm/src/service/team/base-position.d.ts +25 -0
- package/dist/esm/src/service/team/base-position.js +108 -0
- package/dist/esm/src/service/team/base-position.test.d.ts +1 -0
- package/dist/esm/src/service/team/base-position.test.js +173 -0
- package/dist/esm/src/service/team/index.d.ts +4 -0
- package/dist/esm/src/service/team/index.js +4 -0
- package/dist/esm/src/service/team/lineup-function.d.ts +13 -0
- package/dist/esm/src/service/team/lineup-function.js +60 -0
- package/dist/esm/src/service/team/offensive-preference.d.ts +5 -0
- package/dist/esm/src/service/team/offensive-preference.js +1 -0
- package/dist/esm/src/service/team/rotation-system.d.ts +8 -0
- package/dist/esm/src/service/team/rotation-system.js +21 -0
- package/dist/esm/src/service/team/schemas/tactics.z.d.ts +7 -0
- package/dist/esm/src/service/team/schemas/tactics.z.js +67 -1
- package/dist/esm/src/service/team/schemas/team.z.d.ts +6 -0
- package/dist/esm/src/service/team/tactics.d.ts +8 -0
- package/dist/esm/src/service/team/tactics.js +5 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BoxScore, DataProps, DeclineStatus, Division as _Division, League as _League, Match as _Match, MatchSet as _MatchSet, Player as _Player, PlayerPosition, Qualifier as _Qualifier, QualifierMatch as _QualifierMatch, Rally as _Rally, RallyEvent as _RallyEvent, Season as _Season, Standing as _Standing, Team as _Team, Tournament as _Tournament, TournamentMatch as _TournamentMatch, National as _National, NationalMatch as _NationalMatch, Country as _Country, CourtPosition, ConditionLogic, PinchCondition, SubBand } from '../service';
|
|
1
|
+
import { BoxScore, DataProps, DeclineStatus, Division as _Division, League as _League, Match as _Match, MatchSet as _MatchSet, Player as _Player, PlayerPosition, Qualifier as _Qualifier, QualifierMatch as _QualifierMatch, Rally as _Rally, RallyEvent as _RallyEvent, Season as _Season, Standing as _Standing, Team as _Team, Tournament as _Tournament, TournamentMatch as _TournamentMatch, National as _National, NationalMatch as _NationalMatch, Country as _Country, CourtPosition, ConditionLogic, PinchCondition, RotationSystemEnum, SubBand } from '../service';
|
|
2
2
|
export type Rally = DataProps<_Rally> & {
|
|
3
3
|
homePlayerPosition: PlayerPosition[];
|
|
4
4
|
awayPlayerPosition: PlayerPosition[];
|
|
@@ -64,6 +64,10 @@ export interface ApiDesignatedSub {
|
|
|
64
64
|
conditions?: PinchCondition[];
|
|
65
65
|
conditionLogic?: ConditionLogic;
|
|
66
66
|
}
|
|
67
|
+
export interface ApiOffensivePreference {
|
|
68
|
+
rotation: number;
|
|
69
|
+
order: string[];
|
|
70
|
+
}
|
|
67
71
|
export interface Tactics {
|
|
68
72
|
lineup: StartingLineup;
|
|
69
73
|
substitutionTolerance: number;
|
|
@@ -72,6 +76,9 @@ export interface Tactics {
|
|
|
72
76
|
receiveRotationOffset: boolean;
|
|
73
77
|
designatedSubs: ApiDesignatedSub[];
|
|
74
78
|
substitutionBand: SubBand;
|
|
79
|
+
rotationSystem: RotationSystemEnum;
|
|
80
|
+
designatedSetters: string[];
|
|
81
|
+
offensivePreferences: ApiOffensivePreference[];
|
|
75
82
|
}
|
|
76
83
|
export type Team = Omit<DataProps<_Team>, 'roster' | '_rating' | 'rating' | 'tactics'> & {
|
|
77
84
|
roster: Player[];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as Sequelize from 'sequelize';
|
|
2
2
|
import { Model, Optional } from 'sequelize';
|
|
3
3
|
import { PlayerId, TeamId, TeamModel } from '.';
|
|
4
|
-
import { ConditionLogic, CourtPosition, PinchCondition, SubBand } from '../../service';
|
|
4
|
+
import { ConditionLogic, CourtPosition, PinchCondition, RotationSystemEnum, SubBand } from '../../service';
|
|
5
5
|
export interface TacticsLineupAttributes {
|
|
6
6
|
[CourtPosition.LIBERO_ZONE]?: PlayerId;
|
|
7
7
|
[CourtPosition.LEFT_BACK]: PlayerId;
|
|
@@ -21,6 +21,10 @@ export interface DesignatedSubAttributes {
|
|
|
21
21
|
conditions?: PinchCondition[];
|
|
22
22
|
conditionLogic?: ConditionLogic;
|
|
23
23
|
}
|
|
24
|
+
export interface OffensivePreferenceAttributes {
|
|
25
|
+
rotation: number;
|
|
26
|
+
order: PlayerId[];
|
|
27
|
+
}
|
|
24
28
|
export interface TacticsAttributes {
|
|
25
29
|
team_id: string;
|
|
26
30
|
substitution_tolerance: number;
|
|
@@ -30,10 +34,13 @@ export interface TacticsAttributes {
|
|
|
30
34
|
receive_rotation_offset: boolean;
|
|
31
35
|
designated_subs: DesignatedSubAttributes[];
|
|
32
36
|
substitution_band: SubBand;
|
|
37
|
+
rotation_system: RotationSystemEnum;
|
|
38
|
+
designated_setters: PlayerId[];
|
|
39
|
+
offensive_preferences: OffensivePreferenceAttributes[];
|
|
33
40
|
}
|
|
34
41
|
export type TacticsPk = 'team_id';
|
|
35
42
|
export type TacticsId = TacticsModel[TacticsPk];
|
|
36
|
-
export type TacticsOptionalAttributes = 'substitution_tolerance' | 'lineup' | 'libero_replacements' | 'pinch_server_subs' | 'receive_rotation_offset' | 'designated_subs' | 'substitution_band';
|
|
43
|
+
export type TacticsOptionalAttributes = 'substitution_tolerance' | 'lineup' | 'libero_replacements' | 'pinch_server_subs' | 'receive_rotation_offset' | 'designated_subs' | 'substitution_band' | 'rotation_system' | 'designated_setters' | 'offensive_preferences';
|
|
37
44
|
export type TacticsCreationAttributes = Optional<TacticsAttributes, TacticsOptionalAttributes>;
|
|
38
45
|
export declare class TacticsModel extends Model<TacticsAttributes, TacticsCreationAttributes> implements TacticsAttributes {
|
|
39
46
|
team_id: string;
|
|
@@ -44,6 +51,9 @@ export declare class TacticsModel extends Model<TacticsAttributes, TacticsCreati
|
|
|
44
51
|
receive_rotation_offset: boolean;
|
|
45
52
|
designated_subs: DesignatedSubAttributes[];
|
|
46
53
|
substitution_band: SubBand;
|
|
54
|
+
rotation_system: RotationSystemEnum;
|
|
55
|
+
designated_setters: PlayerId[];
|
|
56
|
+
offensive_preferences: OffensivePreferenceAttributes[];
|
|
47
57
|
team: TeamModel;
|
|
48
58
|
getTeam: Sequelize.BelongsToGetAssociationMixin<TeamModel>;
|
|
49
59
|
setTeam: Sequelize.BelongsToSetAssociationMixin<TeamModel, TeamId>;
|
|
@@ -49,6 +49,21 @@ class TacticsModel extends sequelize_1.Model {
|
|
|
49
49
|
type: sequelize_1.DataTypes.STRING,
|
|
50
50
|
allowNull: false,
|
|
51
51
|
defaultValue: 'TIRED'
|
|
52
|
+
},
|
|
53
|
+
rotation_system: {
|
|
54
|
+
type: sequelize_1.DataTypes.STRING,
|
|
55
|
+
allowNull: false,
|
|
56
|
+
defaultValue: '6-0'
|
|
57
|
+
},
|
|
58
|
+
designated_setters: {
|
|
59
|
+
type: sequelize_1.DataTypes.ARRAY(sequelize_1.DataTypes.UUID),
|
|
60
|
+
allowNull: false,
|
|
61
|
+
defaultValue: []
|
|
62
|
+
},
|
|
63
|
+
offensive_preferences: {
|
|
64
|
+
type: sequelize_1.DataTypes.JSONB,
|
|
65
|
+
allowNull: false,
|
|
66
|
+
defaultValue: []
|
|
52
67
|
}
|
|
53
68
|
}, {
|
|
54
69
|
sequelize,
|
|
@@ -49,6 +49,12 @@ function transformToAttributes(tactics, teamId) {
|
|
|
49
49
|
fatigueBand: ds.fatigueBand,
|
|
50
50
|
conditions: ds.conditions,
|
|
51
51
|
conditionLogic: ds.conditionLogic
|
|
52
|
+
})),
|
|
53
|
+
rotation_system: tactics.rotationSystem,
|
|
54
|
+
designated_setters: tactics.designatedSetters.map((s) => s.id),
|
|
55
|
+
offensive_preferences: tactics.offensivePreferences.map(p => ({
|
|
56
|
+
rotation: p.rotation,
|
|
57
|
+
order: p.order.map((a) => a.id)
|
|
52
58
|
}))
|
|
53
59
|
};
|
|
54
60
|
}
|
|
@@ -74,6 +80,12 @@ function transformToObject(model, roster) {
|
|
|
74
80
|
fatigueBand: d.fatigueBand,
|
|
75
81
|
conditions: d.conditions,
|
|
76
82
|
conditionLogic: d.conditionLogic
|
|
83
|
+
})),
|
|
84
|
+
rotationSystem: model.rotation_system,
|
|
85
|
+
designatedSetters: (model.designated_setters ?? []).map((id) => findPlayer(id, roster)),
|
|
86
|
+
offensivePreferences: (model.offensive_preferences ?? []).map(p => ({
|
|
87
|
+
rotation: p.rotation,
|
|
88
|
+
order: p.order.map((id) => findPlayer(id, roster))
|
|
77
89
|
}))
|
|
78
90
|
});
|
|
79
91
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { CourtPosition } from '../match';
|
|
2
|
+
import { Role } from '../player';
|
|
3
|
+
import { RotationSystemEnum } from './rotation-system';
|
|
4
|
+
export declare enum BaseContext {
|
|
5
|
+
SERVE_RECEIVE = "SERVE_RECEIVE",
|
|
6
|
+
BASE = "BASE"
|
|
7
|
+
}
|
|
8
|
+
export interface BasePlayer {
|
|
9
|
+
readonly playerId: string;
|
|
10
|
+
readonly position: CourtPosition;
|
|
11
|
+
readonly roles: Role[];
|
|
12
|
+
}
|
|
13
|
+
export interface BaseResolverInput {
|
|
14
|
+
readonly system: RotationSystemEnum;
|
|
15
|
+
readonly designatedSetterIds: string[];
|
|
16
|
+
readonly players: BasePlayer[];
|
|
17
|
+
readonly liberoId?: string;
|
|
18
|
+
}
|
|
19
|
+
export type BaseMap = Map<string, CourtPosition>;
|
|
20
|
+
export interface ResolvedBases {
|
|
21
|
+
readonly serveReceive: BaseMap;
|
|
22
|
+
readonly base: BaseMap;
|
|
23
|
+
}
|
|
24
|
+
export declare function rallySetterId(system: RotationSystemEnum, designatedSetterIds: string[], players: BasePlayer[]): string | undefined;
|
|
25
|
+
export declare function resolveBases(input: BaseResolverInput): ResolvedBases;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BaseContext = void 0;
|
|
4
|
+
exports.rallySetterId = rallySetterId;
|
|
5
|
+
exports.resolveBases = resolveBases;
|
|
6
|
+
const match_1 = require("../match");
|
|
7
|
+
const player_1 = require("../player");
|
|
8
|
+
const rotation_system_1 = require("./rotation-system");
|
|
9
|
+
const lineup_function_1 = require("./lineup-function");
|
|
10
|
+
// The rally phase a base lookup is resolved against. Two contexts are enough in the 6-zone model:
|
|
11
|
+
// - SERVE_RECEIVE: first contact when receiving serve. The passers (libero + outsides) hold the back row; the
|
|
12
|
+
// setter(s) and the opposite are hidden in the front (not passers). Serves are aimed at the back-row passers,
|
|
13
|
+
// so the setter / penetrating setter / opposite never receive by default (they can be deliberately hunted).
|
|
14
|
+
// - BASE: open play (offense AND defense). In the discrete 6-zone model these coincide: every player is at their
|
|
15
|
+
// one specialist lane whether attacking or digging. There is no visual layer and no benefit to splitting them.
|
|
16
|
+
var BaseContext;
|
|
17
|
+
(function (BaseContext) {
|
|
18
|
+
BaseContext["SERVE_RECEIVE"] = "SERVE_RECEIVE";
|
|
19
|
+
BaseContext["BASE"] = "BASE";
|
|
20
|
+
})(BaseContext || (exports.BaseContext = BaseContext = {}));
|
|
21
|
+
// The specialist zone a function occupies given its current row. Outsides play left (z4 front / z5 back), middles
|
|
22
|
+
// the middle (z3 / z6), setter and opposite the right (z2 / z1). Because each same-zone pair (setter+opposite, the
|
|
23
|
+
// two middles, the two outsides) sits 3 rotations apart, exactly one is front and one is back at any rotation, so
|
|
24
|
+
// the six players always map to the six distinct zones (a bijection).
|
|
25
|
+
function specialistZone(fn, front) {
|
|
26
|
+
switch (fn) {
|
|
27
|
+
case player_1.RoleEnum.OUTSIDE_HITTER: return front ? match_1.CourtPosition.LEFT_FRONT : match_1.CourtPosition.LEFT_BACK;
|
|
28
|
+
case player_1.RoleEnum.MIDDLE_BLOCKER: return front ? match_1.CourtPosition.MIDDLE_FRONT : match_1.CourtPosition.MIDDLE_BACK;
|
|
29
|
+
case player_1.RoleEnum.OPPOSITE_HITTER:
|
|
30
|
+
case player_1.RoleEnum.SETTER: return front ? match_1.CourtPosition.RIGHT_FRONT : match_1.CourtPosition.RIGHT_BACK;
|
|
31
|
+
default: return front ? match_1.CourtPosition.MIDDLE_FRONT : match_1.CourtPosition.MIDDLE_BACK;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Who takes the second touch this rally: 5-1 the single setter; 4-2 the front-row setter; 6-2 the back-row
|
|
35
|
+
// (penetrating) setter. Returns undefined for 6-0 or when no designated setter is on court (the sim picks a
|
|
36
|
+
// front-row setter by stat in that case).
|
|
37
|
+
function rallySetterId(system, designatedSetterIds, players) {
|
|
38
|
+
const setters = designatedSetterIds
|
|
39
|
+
.map(id => players.find(p => p.playerId === id))
|
|
40
|
+
.filter((p) => p != null);
|
|
41
|
+
if (setters.length === 0)
|
|
42
|
+
return undefined;
|
|
43
|
+
switch (system) {
|
|
44
|
+
case rotation_system_1.RotationSystemEnum.FIVE_ONE:
|
|
45
|
+
return setters[0].playerId;
|
|
46
|
+
case rotation_system_1.RotationSystemEnum.FOUR_TWO:
|
|
47
|
+
return (setters.find(s => (0, match_1.isFrontRow)(s.position)) ?? setters[0]).playerId;
|
|
48
|
+
case rotation_system_1.RotationSystemEnum.SIX_TWO:
|
|
49
|
+
return (setters.find(s => !(0, match_1.isFrontRow)(s.position)) ?? setters[0]).playerId;
|
|
50
|
+
default:
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function identityBases(players) {
|
|
55
|
+
const make = () => new Map(players.map(p => [p.playerId, p.position]));
|
|
56
|
+
return { serveReceive: make(), base: make() };
|
|
57
|
+
}
|
|
58
|
+
// Resolve the two context base maps for the six on-court players. 6-0 (or a misconfigured lineup) keeps the
|
|
59
|
+
// rotational layout in both contexts (= pre-feature behavior). Each returned map is a bijection over {1..6}; the
|
|
60
|
+
// sim asserts this after applying it.
|
|
61
|
+
function resolveBases(input) {
|
|
62
|
+
const { system, designatedSetterIds, players, liberoId } = input;
|
|
63
|
+
if (system === rotation_system_1.RotationSystemEnum.SIX_ZERO)
|
|
64
|
+
return identityBases(players);
|
|
65
|
+
const mainSetter = players.find(p => p.playerId === designatedSetterIds[0]);
|
|
66
|
+
if (mainSetter == null)
|
|
67
|
+
return identityBases(players);
|
|
68
|
+
// BASE (open play): every player releases to their specialist lane.
|
|
69
|
+
const base = new Map();
|
|
70
|
+
for (const p of players) {
|
|
71
|
+
const fn = (0, lineup_function_1.slotFunctionAt)(system, mainSetter.position, p.position);
|
|
72
|
+
base.set(p.playerId, specialistZone(fn, (0, match_1.isFrontRow)(p.position)));
|
|
73
|
+
}
|
|
74
|
+
return { serveReceive: buildServeReceive(system, designatedSetterIds, players, mainSetter, liberoId), base };
|
|
75
|
+
}
|
|
76
|
+
// The serve-receive base. Only the back row handles the serve, so the three back-row zones go to the actual
|
|
77
|
+
// passers (the libero + the two outside-hitter slots) and the three front zones hide the non-passers (the
|
|
78
|
+
// setter(s), the opposite, and a middle). This keeps the opposite -- which shares the right lane with the setter
|
|
79
|
+
// in open play -- out of the passing lanes; a server can still deliberately hunt the hidden setter or opposite
|
|
80
|
+
// (handled sim-side). Without a libero on court the back-row middle fills the third passing slot. Always a
|
|
81
|
+
// bijection over {1..6} (three front + three back, distinct zones).
|
|
82
|
+
function buildServeReceive(system, designatedSetterIds, players, mainSetter, liberoId) {
|
|
83
|
+
// Zone order, declared in-function so CourtPosition is read at call time, not module load: the service<->match
|
|
84
|
+
// import cycle leaves CourtPosition unbound while this module is first evaluated, so a module-level array would
|
|
85
|
+
// crash. Hidden players fill the front (rally-setter first -> RIGHT_FRONT); passers fill the back (libero first
|
|
86
|
+
// -> MIDDLE_BACK).
|
|
87
|
+
const frontZones = [match_1.CourtPosition.RIGHT_FRONT, match_1.CourtPosition.LEFT_FRONT, match_1.CourtPosition.MIDDLE_FRONT];
|
|
88
|
+
const backZones = [match_1.CourtPosition.MIDDLE_BACK, match_1.CourtPosition.LEFT_BACK, match_1.CourtPosition.RIGHT_BACK];
|
|
89
|
+
const setterId = rallySetterId(system, designatedSetterIds, players);
|
|
90
|
+
const fnOf = (p) => (0, lineup_function_1.slotFunctionAt)(system, mainSetter.position, p.position);
|
|
91
|
+
const passers = [];
|
|
92
|
+
const middles = [];
|
|
93
|
+
const hidden = []; // setter(s) + opposite
|
|
94
|
+
for (const p of players) {
|
|
95
|
+
if (p.playerId === liberoId || fnOf(p) === player_1.RoleEnum.OUTSIDE_HITTER)
|
|
96
|
+
passers.push(p);
|
|
97
|
+
else if (fnOf(p) === player_1.RoleEnum.MIDDLE_BLOCKER)
|
|
98
|
+
middles.push(p);
|
|
99
|
+
else
|
|
100
|
+
hidden.push(p);
|
|
101
|
+
}
|
|
102
|
+
// Fill to three passers from the middles (back-row first, so a front-row middle stays hidden); the rest hide.
|
|
103
|
+
middles.sort((a, b) => Number((0, match_1.isFrontRow)(a.position)) - Number((0, match_1.isFrontRow)(b.position)));
|
|
104
|
+
while (passers.length < 3 && middles.length > 0)
|
|
105
|
+
passers.push(middles.shift());
|
|
106
|
+
hidden.push(...middles);
|
|
107
|
+
hidden.sort((a, b) => Number(b.playerId === setterId) - Number(a.playerId === setterId));
|
|
108
|
+
passers.sort((a, b) => Number(b.playerId === liberoId) - Number(a.playerId === liberoId));
|
|
109
|
+
const sr = new Map();
|
|
110
|
+
hidden.forEach((p, i) => sr.set(p.playerId, frontZones[i]));
|
|
111
|
+
passers.forEach((p, i) => sr.set(p.playerId, backZones[i]));
|
|
112
|
+
return sr;
|
|
113
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const match_1 = require("../match");
|
|
4
|
+
const player_1 = require("../player");
|
|
5
|
+
const rotation_system_1 = require("./rotation-system");
|
|
6
|
+
const lineup_function_1 = require("./lineup-function");
|
|
7
|
+
const base_position_1 = require("./base-position");
|
|
8
|
+
// Balanced 5-1 at rotation 1 (setter serving from RIGHT_BACK = 1): S, OH1, MB1, OPP, OH2, MB2 around the court.
|
|
9
|
+
function baseLineup() {
|
|
10
|
+
return [
|
|
11
|
+
{ playerId: 'S', position: match_1.CourtPosition.RIGHT_BACK, roles: [player_1.RoleEnum.SETTER] },
|
|
12
|
+
{ playerId: 'OH1', position: match_1.CourtPosition.RIGHT_FRONT, roles: [player_1.RoleEnum.OUTSIDE_HITTER] },
|
|
13
|
+
{ playerId: 'MB1', position: match_1.CourtPosition.MIDDLE_FRONT, roles: [player_1.RoleEnum.MIDDLE_BLOCKER] },
|
|
14
|
+
{ playerId: 'OPP', position: match_1.CourtPosition.LEFT_FRONT, roles: [player_1.RoleEnum.OPPOSITE_HITTER] },
|
|
15
|
+
{ playerId: 'OH2', position: match_1.CourtPosition.LEFT_BACK, roles: [player_1.RoleEnum.OUTSIDE_HITTER] },
|
|
16
|
+
{ playerId: 'MB2', position: match_1.CourtPosition.MIDDLE_BACK, roles: [player_1.RoleEnum.MIDDLE_BLOCKER] }
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
// 5-1 with the libero on court in the back-row middle slot (replacing MB2).
|
|
20
|
+
function lineupWithLibero() {
|
|
21
|
+
return {
|
|
22
|
+
players: [
|
|
23
|
+
{ playerId: 'S', position: match_1.CourtPosition.RIGHT_BACK, roles: [player_1.RoleEnum.SETTER] },
|
|
24
|
+
{ playerId: 'OH1', position: match_1.CourtPosition.RIGHT_FRONT, roles: [player_1.RoleEnum.OUTSIDE_HITTER] },
|
|
25
|
+
{ playerId: 'MB1', position: match_1.CourtPosition.MIDDLE_FRONT, roles: [player_1.RoleEnum.MIDDLE_BLOCKER] },
|
|
26
|
+
{ playerId: 'OPP', position: match_1.CourtPosition.LEFT_FRONT, roles: [player_1.RoleEnum.OPPOSITE_HITTER] },
|
|
27
|
+
{ playerId: 'OH2', position: match_1.CourtPosition.LEFT_BACK, roles: [player_1.RoleEnum.OUTSIDE_HITTER] },
|
|
28
|
+
{ playerId: 'LIB', position: match_1.CourtPosition.MIDDLE_BACK, roles: [player_1.RoleEnum.LIBERO] }
|
|
29
|
+
],
|
|
30
|
+
liberoId: 'LIB'
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function rotate(players, times) {
|
|
34
|
+
let out = players;
|
|
35
|
+
for (let i = 0; i < times; i++)
|
|
36
|
+
out = out.map(p => ({ ...p, position: (0, match_1.rotatePosition)(p.position) }));
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
function isBijection(m) {
|
|
40
|
+
const zones = [...m.values()];
|
|
41
|
+
return zones.length === 6 && new Set(zones).size === 6 && zones.every(z => z >= 1 && z <= 6);
|
|
42
|
+
}
|
|
43
|
+
const SETTERS = {
|
|
44
|
+
[rotation_system_1.RotationSystemEnum.FIVE_ONE]: ['S'],
|
|
45
|
+
[rotation_system_1.RotationSystemEnum.FOUR_TWO]: ['S', 'OPP'],
|
|
46
|
+
[rotation_system_1.RotationSystemEnum.SIX_TWO]: ['S', 'OPP'],
|
|
47
|
+
[rotation_system_1.RotationSystemEnum.SIX_ZERO]: []
|
|
48
|
+
};
|
|
49
|
+
describe('resolveBases() bijection', () => {
|
|
50
|
+
for (const system of Object.values(rotation_system_1.RotationSystemEnum)) {
|
|
51
|
+
for (let r = 0; r < 6; r++) {
|
|
52
|
+
it(`${system} rotation ${r}: all three contexts are bijections over {1..6}`, () => {
|
|
53
|
+
const players = rotate(baseLineup(), r);
|
|
54
|
+
const bases = (0, base_position_1.resolveBases)({ system, designatedSetterIds: SETTERS[system], players });
|
|
55
|
+
expect(isBijection(bases.serveReceive)).toBe(true);
|
|
56
|
+
expect(isBijection(bases.base)).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
describe('resolveBases() 6-0', () => {
|
|
62
|
+
it('keeps the rotational layout in every context (identity)', () => {
|
|
63
|
+
const players = rotate(baseLineup(), 3);
|
|
64
|
+
const bases = (0, base_position_1.resolveBases)({ system: rotation_system_1.RotationSystemEnum.SIX_ZERO, designatedSetterIds: [], players });
|
|
65
|
+
for (const p of players) {
|
|
66
|
+
expect(bases.base.get(p.playerId)).toBe(p.position);
|
|
67
|
+
expect(bases.serveReceive.get(p.playerId)).toBe(p.position);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
describe('resolveBases() serve-receive hides the setter', () => {
|
|
72
|
+
it('5-1 setter is always at RIGHT_FRONT in serve-receive, every rotation', () => {
|
|
73
|
+
for (let r = 0; r < 6; r++) {
|
|
74
|
+
const players = rotate(baseLineup(), r);
|
|
75
|
+
const bases = (0, base_position_1.resolveBases)({ system: rotation_system_1.RotationSystemEnum.FIVE_ONE, designatedSetterIds: ['S'], players });
|
|
76
|
+
expect(bases.serveReceive.get('S')).toBe(match_1.CourtPosition.RIGHT_FRONT);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe('resolveBases() serve-receive hides the opposite (regression: opposites were receiving most serves)', () => {
|
|
81
|
+
it('5-1 opposite is hidden in the front, never in the serve-receive back row, every rotation', () => {
|
|
82
|
+
for (let r = 0; r < 6; r++) {
|
|
83
|
+
const players = rotate(baseLineup(), r);
|
|
84
|
+
const bases = (0, base_position_1.resolveBases)({ system: rotation_system_1.RotationSystemEnum.FIVE_ONE, designatedSetterIds: ['S'], players });
|
|
85
|
+
expect((0, match_1.isFrontRow)(bases.serveReceive.get('OPP'))).toBe(true);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
it('5-1 serve-receive back row holds exactly the passers -- never the setter or the opposite', () => {
|
|
89
|
+
for (let r = 0; r < 6; r++) {
|
|
90
|
+
const players = rotate(baseLineup(), r);
|
|
91
|
+
const bases = (0, base_position_1.resolveBases)({ system: rotation_system_1.RotationSystemEnum.FIVE_ONE, designatedSetterIds: ['S'], players });
|
|
92
|
+
const backRow = [...bases.serveReceive.entries()].filter(([, z]) => !(0, match_1.isFrontRow)(z)).map(([id]) => id);
|
|
93
|
+
expect(backRow).toHaveLength(3);
|
|
94
|
+
expect(backRow).not.toContain('S');
|
|
95
|
+
expect(backRow).not.toContain('OPP');
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
it('the on-court libero is always a passer (back row) while the opposite stays hidden', () => {
|
|
99
|
+
const { players, liberoId } = lineupWithLibero();
|
|
100
|
+
const bases = (0, base_position_1.resolveBases)({ system: rotation_system_1.RotationSystemEnum.FIVE_ONE, designatedSetterIds: ['S'], players, liberoId });
|
|
101
|
+
expect((0, match_1.isFrontRow)(bases.serveReceive.get(liberoId))).toBe(false);
|
|
102
|
+
expect((0, match_1.isFrontRow)(bases.serveReceive.get('OPP'))).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
describe('rallySetterId()', () => {
|
|
106
|
+
it('5-1 returns the single setter in every rotation', () => {
|
|
107
|
+
for (let r = 0; r < 6; r++) {
|
|
108
|
+
const players = rotate(baseLineup(), r);
|
|
109
|
+
expect((0, base_position_1.rallySetterId)(rotation_system_1.RotationSystemEnum.FIVE_ONE, ['S'], players)).toBe('S');
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
it('4-2 returns the front-row setter, 6-2 returns the back-row setter', () => {
|
|
113
|
+
for (let r = 0; r < 6; r++) {
|
|
114
|
+
const players = rotate(baseLineup(), r);
|
|
115
|
+
const front = players.find(p => (p.playerId === 'S' || p.playerId === 'OPP') && p.position >= 2 && p.position <= 4);
|
|
116
|
+
const back = players.find(p => (p.playerId === 'S' || p.playerId === 'OPP') && (p.position === 1 || p.position === 5 || p.position === 6));
|
|
117
|
+
expect((0, base_position_1.rallySetterId)(rotation_system_1.RotationSystemEnum.FOUR_TWO, ['S', 'OPP'], players)).toBe(front?.playerId);
|
|
118
|
+
expect((0, base_position_1.rallySetterId)(rotation_system_1.RotationSystemEnum.SIX_TWO, ['S', 'OPP'], players)).toBe(back?.playerId);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
it('6-0 has no rally setter', () => {
|
|
122
|
+
expect((0, base_position_1.rallySetterId)(rotation_system_1.RotationSystemEnum.SIX_ZERO, [], baseLineup())).toBeUndefined();
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
describe('getLineupFunctions()', () => {
|
|
126
|
+
it('assigns the balanced 5-1 functions', () => {
|
|
127
|
+
const fns = (0, lineup_function_1.getLineupFunctions)(rotation_system_1.RotationSystemEnum.FIVE_ONE, ['S'], baseLineup());
|
|
128
|
+
expect(fns.get('S')).toBe(player_1.RoleEnum.SETTER);
|
|
129
|
+
expect(fns.get('OH1')).toBe(player_1.RoleEnum.OUTSIDE_HITTER);
|
|
130
|
+
expect(fns.get('MB1')).toBe(player_1.RoleEnum.MIDDLE_BLOCKER);
|
|
131
|
+
expect(fns.get('OPP')).toBe(player_1.RoleEnum.OPPOSITE_HITTER);
|
|
132
|
+
expect(fns.get('OH2')).toBe(player_1.RoleEnum.OUTSIDE_HITTER);
|
|
133
|
+
expect(fns.get('MB2')).toBe(player_1.RoleEnum.MIDDLE_BLOCKER);
|
|
134
|
+
});
|
|
135
|
+
it('maps the opposite slot to SETTER in 6-2/4-2 (the second setter)', () => {
|
|
136
|
+
const fns = (0, lineup_function_1.getLineupFunctions)(rotation_system_1.RotationSystemEnum.SIX_TWO, ['S', 'OPP'], baseLineup());
|
|
137
|
+
expect(fns.get('OPP')).toBe(player_1.RoleEnum.SETTER);
|
|
138
|
+
expect(fns.get('S')).toBe(player_1.RoleEnum.SETTER);
|
|
139
|
+
});
|
|
140
|
+
it('is rotation-invariant', () => {
|
|
141
|
+
const r0 = (0, lineup_function_1.getLineupFunctions)(rotation_system_1.RotationSystemEnum.FIVE_ONE, ['S'], baseLineup());
|
|
142
|
+
const r3 = (0, lineup_function_1.getLineupFunctions)(rotation_system_1.RotationSystemEnum.FIVE_ONE, ['S'], rotate(baseLineup(), 3));
|
|
143
|
+
for (const id of ['S', 'OH1', 'MB1', 'OPP', 'OH2', 'MB2']) {
|
|
144
|
+
expect(r3.get(id)).toBe(r0.get(id));
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
it('6-0 assigns no functions', () => {
|
|
148
|
+
expect((0, lineup_function_1.getLineupFunctions)(rotation_system_1.RotationSystemEnum.SIX_ZERO, [], baseLineup()).size).toBe(0);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
describe('out-of-position penalty', () => {
|
|
152
|
+
it('penalises a specialist played in a slot whose role they lack', () => {
|
|
153
|
+
// A pure middle blocker placed in the opposite slot (function OPPOSITE_HITTER) is out of position.
|
|
154
|
+
expect((0, lineup_function_1.isOutOfPosition)([player_1.RoleEnum.MIDDLE_BLOCKER], player_1.RoleEnum.OPPOSITE_HITTER)).toBe(true);
|
|
155
|
+
expect((0, lineup_function_1.outOfPositionPenalty)([player_1.RoleEnum.MIDDLE_BLOCKER], player_1.RoleEnum.OPPOSITE_HITTER)).toBeCloseTo(lineup_function_1.OUT_OF_POSITION_PENALTY);
|
|
156
|
+
});
|
|
157
|
+
it('does not penalise a versatile player who holds the slot role', () => {
|
|
158
|
+
expect((0, lineup_function_1.isOutOfPosition)([player_1.RoleEnum.MIDDLE_BLOCKER, player_1.RoleEnum.OPPOSITE_HITTER], player_1.RoleEnum.OPPOSITE_HITTER)).toBe(false);
|
|
159
|
+
expect((0, lineup_function_1.outOfPositionPenalty)([player_1.RoleEnum.MIDDLE_BLOCKER, player_1.RoleEnum.OPPOSITE_HITTER], player_1.RoleEnum.OPPOSITE_HITTER)).toBe(1);
|
|
160
|
+
});
|
|
161
|
+
it('never penalises when there is no slot function (6-0 / unknown)', () => {
|
|
162
|
+
expect((0, lineup_function_1.isOutOfPosition)([player_1.RoleEnum.MIDDLE_BLOCKER], undefined)).toBe(false);
|
|
163
|
+
expect((0, lineup_function_1.outOfPositionPenalty)([player_1.RoleEnum.MIDDLE_BLOCKER], undefined)).toBe(1);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
describe('slotFunctionAt()', () => {
|
|
167
|
+
it('is rotation-invariant for each player', () => {
|
|
168
|
+
const setterAtR0 = match_1.CourtPosition.RIGHT_BACK;
|
|
169
|
+
const setterAtR3 = (0, match_1.rotatePosition)((0, match_1.rotatePosition)((0, match_1.rotatePosition)(setterAtR0)));
|
|
170
|
+
// OPP sits opposite the setter (offset 3): OPPOSITE in 5-1.
|
|
171
|
+
expect((0, lineup_function_1.slotFunctionAt)(rotation_system_1.RotationSystemEnum.FIVE_ONE, setterAtR0, match_1.CourtPosition.LEFT_FRONT)).toBe(player_1.RoleEnum.OPPOSITE_HITTER);
|
|
172
|
+
const oppAtR3 = (0, match_1.rotatePosition)((0, match_1.rotatePosition)((0, match_1.rotatePosition)(match_1.CourtPosition.LEFT_FRONT)));
|
|
173
|
+
expect((0, lineup_function_1.slotFunctionAt)(rotation_system_1.RotationSystemEnum.FIVE_ONE, setterAtR3, oppAtR3)).toBe(player_1.RoleEnum.OPPOSITE_HITTER);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
@@ -6,3 +6,7 @@ export * from './team-name';
|
|
|
6
6
|
export * from './energy-band';
|
|
7
7
|
export * from './pinch-condition';
|
|
8
8
|
export * from './designated-sub';
|
|
9
|
+
export * from './rotation-system';
|
|
10
|
+
export * from './lineup-function';
|
|
11
|
+
export * from './base-position';
|
|
12
|
+
export * from './offensive-preference';
|
|
@@ -22,3 +22,7 @@ __exportStar(require("./team-name"), exports);
|
|
|
22
22
|
__exportStar(require("./energy-band"), exports);
|
|
23
23
|
__exportStar(require("./pinch-condition"), exports);
|
|
24
24
|
__exportStar(require("./designated-sub"), exports);
|
|
25
|
+
__exportStar(require("./rotation-system"), exports);
|
|
26
|
+
__exportStar(require("./lineup-function"), exports);
|
|
27
|
+
__exportStar(require("./base-position"), exports);
|
|
28
|
+
__exportStar(require("./offensive-preference"), exports);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { CourtPosition } from '../match';
|
|
2
|
+
import { Role, RoleEnum } from '../player';
|
|
3
|
+
import { RotationSystemEnum } from './rotation-system';
|
|
4
|
+
export type LineupFunction = RoleEnum;
|
|
5
|
+
export declare const OUT_OF_POSITION_PENALTY: 0.85;
|
|
6
|
+
export interface LineupSlot {
|
|
7
|
+
readonly playerId: string;
|
|
8
|
+
readonly position: CourtPosition;
|
|
9
|
+
}
|
|
10
|
+
export declare function slotFunctionAt(system: RotationSystemEnum, setterPosition: CourtPosition, position: CourtPosition): RoleEnum;
|
|
11
|
+
export declare function getLineupFunctions(system: RotationSystemEnum, designatedSetterIds: string[], starters: LineupSlot[]): Map<string, RoleEnum>;
|
|
12
|
+
export declare function isOutOfPosition(roles: Role[], fn: RoleEnum | undefined): boolean;
|
|
13
|
+
export declare function outOfPositionPenalty(roles: Role[], fn: RoleEnum | undefined): number;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OUT_OF_POSITION_PENALTY = void 0;
|
|
4
|
+
exports.slotFunctionAt = slotFunctionAt;
|
|
5
|
+
exports.getLineupFunctions = getLineupFunctions;
|
|
6
|
+
exports.isOutOfPosition = isOutOfPosition;
|
|
7
|
+
exports.outOfPositionPenalty = outOfPositionPenalty;
|
|
8
|
+
const player_1 = require("../player");
|
|
9
|
+
const rotation_system_1 = require("./rotation-system");
|
|
10
|
+
// Flat multiplier applied to EVERY action a player performs while played out of their preferred position.
|
|
11
|
+
// Roles are a player's preference/specialisation (decoupled from raw stats), so this is the single standalone
|
|
12
|
+
// signal for "played out of position" and is not double-counting any stat disadvantage. Single tunable knob.
|
|
13
|
+
exports.OUT_OF_POSITION_PENALTY = 0.85;
|
|
14
|
+
// The balanced-lineup function pattern by rotational offset from the main setter (in serving order):
|
|
15
|
+
// 0 setter, 1 outside, 2 middle, 3 opposite (the second setter in 4-2/6-2), 4 outside, 5 middle.
|
|
16
|
+
// Offset is rotation-invariant (the whole team rotates together), so a player's slot function is constant for the
|
|
17
|
+
// match until a substitution changes the personnel.
|
|
18
|
+
const PATTERN = [
|
|
19
|
+
player_1.RoleEnum.SETTER,
|
|
20
|
+
player_1.RoleEnum.OUTSIDE_HITTER,
|
|
21
|
+
player_1.RoleEnum.MIDDLE_BLOCKER,
|
|
22
|
+
player_1.RoleEnum.OPPOSITE_HITTER,
|
|
23
|
+
player_1.RoleEnum.OUTSIDE_HITTER,
|
|
24
|
+
player_1.RoleEnum.MIDDLE_BLOCKER
|
|
25
|
+
];
|
|
26
|
+
// The slot function for a player at `position`, given the main setter sits at `setterPosition`, in `system`.
|
|
27
|
+
// In 4-2/6-2 the opposite slot (offset 3) is the second setter, so it maps to SETTER rather than OPPOSITE.
|
|
28
|
+
function slotFunctionAt(system, setterPosition, position) {
|
|
29
|
+
const offset = (((position - setterPosition) % 6) + 6) % 6;
|
|
30
|
+
const fn = PATTERN[offset];
|
|
31
|
+
if (fn === player_1.RoleEnum.OPPOSITE_HITTER &&
|
|
32
|
+
(system === rotation_system_1.RotationSystemEnum.FOUR_TWO || system === rotation_system_1.RotationSystemEnum.SIX_TWO)) {
|
|
33
|
+
return player_1.RoleEnum.SETTER;
|
|
34
|
+
}
|
|
35
|
+
return fn;
|
|
36
|
+
}
|
|
37
|
+
// Map each starter's id -> the role their lineup slot requires. Empty for 6-0 (no functions => no penalty), or if
|
|
38
|
+
// the main setter is not among the starters (misconfigured -> degrade gracefully to no penalty).
|
|
39
|
+
// Designated setters are forced to SETTER so a slightly off lineup never penalises a correctly-rostered setter
|
|
40
|
+
// (validation enforces the opposite-slot rule upstream).
|
|
41
|
+
function getLineupFunctions(system, designatedSetterIds, starters) {
|
|
42
|
+
const functions = new Map();
|
|
43
|
+
if (system === rotation_system_1.RotationSystemEnum.SIX_ZERO)
|
|
44
|
+
return functions;
|
|
45
|
+
const mainSetter = starters.find(s => s.playerId === designatedSetterIds[0]);
|
|
46
|
+
if (mainSetter == null)
|
|
47
|
+
return functions;
|
|
48
|
+
for (const slot of starters) {
|
|
49
|
+
functions.set(slot.playerId, slotFunctionAt(system, mainSetter.position, slot.position));
|
|
50
|
+
}
|
|
51
|
+
for (const id of designatedSetterIds) {
|
|
52
|
+
if (functions.has(id))
|
|
53
|
+
functions.set(id, player_1.RoleEnum.SETTER);
|
|
54
|
+
}
|
|
55
|
+
return functions;
|
|
56
|
+
}
|
|
57
|
+
// True when a player's slot requires a role they do not hold (i.e. they are played out of position). A missing
|
|
58
|
+
// function (6-0, or unknown player) is never out of position.
|
|
59
|
+
function isOutOfPosition(roles, fn) {
|
|
60
|
+
if (fn == null)
|
|
61
|
+
return false;
|
|
62
|
+
return !roles.includes(fn);
|
|
63
|
+
}
|
|
64
|
+
// The action multiplier for a player given their slot function: penalised if out of position, else neutral.
|
|
65
|
+
function outOfPositionPenalty(roles, fn) {
|
|
66
|
+
return isOutOfPosition(roles, fn) ? exports.OUT_OF_POSITION_PENALTY : 1;
|
|
67
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RotationSystemEnum = void 0;
|
|
4
|
+
exports.setterCountFor = setterCountFor;
|
|
5
|
+
// The offensive rotation system a team runs. Drives base-positioning, who sets each rally (the rally-setter),
|
|
6
|
+
// and the out-of-position penalty. '6-0' (treat '6-6' as an alias) is the neutral default: no designated setter,
|
|
7
|
+
// base positions = the rotational layout, so an unconfigured team plays exactly as before this feature.
|
|
8
|
+
var RotationSystemEnum;
|
|
9
|
+
(function (RotationSystemEnum) {
|
|
10
|
+
RotationSystemEnum["FIVE_ONE"] = "5-1";
|
|
11
|
+
RotationSystemEnum["FOUR_TWO"] = "4-2";
|
|
12
|
+
RotationSystemEnum["SIX_TWO"] = "6-2";
|
|
13
|
+
RotationSystemEnum["SIX_ZERO"] = "6-0";
|
|
14
|
+
})(RotationSystemEnum || (exports.RotationSystemEnum = RotationSystemEnum = {}));
|
|
15
|
+
// How many designated setters each system expects: 5-1 has one; 4-2 and 6-2 have two (main + the opposite slot,
|
|
16
|
+
// which acts as the second setter); 6-0 has none.
|
|
17
|
+
function setterCountFor(system) {
|
|
18
|
+
switch (system) {
|
|
19
|
+
case RotationSystemEnum.FIVE_ONE: return 1;
|
|
20
|
+
case RotationSystemEnum.FOUR_TWO: return 2;
|
|
21
|
+
case RotationSystemEnum.SIX_TWO: return 2;
|
|
22
|
+
case RotationSystemEnum.SIX_ZERO: return 0;
|
|
23
|
+
default: return 0;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { Player } from '../../player';
|
|
3
3
|
import { EnergyBand } from '../energy-band';
|
|
4
|
+
import { RotationSystemEnum } from '../rotation-system';
|
|
4
5
|
export declare const TacticsInputSchema: z.ZodObject<{
|
|
5
6
|
lineup: z.ZodObject<{
|
|
6
7
|
4: z.ZodCustom<Player, Player>;
|
|
@@ -47,5 +48,11 @@ export declare const TacticsInputSchema: z.ZodObject<{
|
|
|
47
48
|
conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
|
|
48
49
|
}, z.core.$strip>>>;
|
|
49
50
|
substitutionBand: z.ZodDefault<z.ZodUnion<readonly [z.ZodEnum<typeof EnergyBand>, z.ZodLiteral<"NEVER">]>>;
|
|
51
|
+
rotationSystem: z.ZodDefault<z.ZodEnum<typeof RotationSystemEnum>>;
|
|
52
|
+
designatedSetters: z.ZodDefault<z.ZodArray<z.ZodCustom<Player, Player>>>;
|
|
53
|
+
offensivePreferences: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
54
|
+
rotation: z.ZodNumber;
|
|
55
|
+
order: z.ZodArray<z.ZodCustom<Player, Player>>;
|
|
56
|
+
}, z.core.$strip>>>;
|
|
50
57
|
}, z.core.$strip>;
|
|
51
58
|
export type TacticsInput = z.infer<typeof TacticsInputSchema>;
|