-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathconfig.ts
213 lines (198 loc) · 8.86 KB
/
config.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
import * as cg from './types';
import { HeadlessState } from './state';
import { setSelected, setGoScore } from './board';
import { read as fenRead, readPocket as fenReadPocket } from './fen';
import { DrawShape, DrawBrush } from './draw';
import { configure as abaloneConfigure } from './variants/abalone/config';
export interface Config {
fen?: cg.FEN; // chess position in Forsyth notation
orientation?: cg.Orientation; // board orientation. p1 | p2 | left | right | p1vflip
myPlayerIndex?: cg.PlayerIndex; // turn of player p1 | p2
turnPlayerIndex?: cg.PlayerIndex; // turn to play. p1 | p2
check?: cg.PlayerIndex | boolean; // true for current playerIndex, false to unset
lastMove?: cg.Key[]; // squares part of the last move ["c3", "c4"]
selected?: cg.Key; // square currently selected "a1"
coordinates?: boolean; // include coords attributes
boardScores?: boolean; //include board-scores attributes
dice?: cg.Dice[]; // dice to display on the board
canUndo?: boolean; // can user undo thier last action
showUndoButton?: boolean; //show the undo button
gameButtonsActive?: boolean; // can user process game buttons (e.g. swap dice, undo)
autoCastle?: boolean; // immediately complete the castle by moving the rook after king move
viewOnly?: boolean; // don't bind events: the user will never be able to move pieces around
selectOnly?: boolean; // only allow user to select squares/pieces (multiple selection allowed)
disableContextMenu?: boolean; // because who needs a context menu on a chessboard
resizable?: boolean; // listens to chessground.resize on document.body to clear bounds cache
addPieceZIndex?: boolean; // adds z-index values to pieces (for 3D)
// pieceKey: boolean; // add a data-key attribute to piece elements
highlight?: {
lastMove?: boolean; // add last-move class to squares
check?: boolean; // add check class to squares
};
animation?: {
enabled?: boolean;
duration?: number;
};
movable?: {
free?: boolean; // all moves are valid - board editor
playerIndex?: cg.PlayerIndex | 'both'; // playerIndex that can move. p1 | p2 | both | undefined
dests?: cg.Dests; // valid moves. {"a2" ["a3" "a4"] "b1" ["a3" "c3"]}
showDests?: boolean; // whether to add the move-dest class on squares
events?: {
after?: (orig: cg.Key, dest: cg.Key, metadata: cg.MoveMetadata) => void; // called after the move has been played
afterNewPiece?: (role: cg.Role, key: cg.Key, metadata: cg.MoveMetadata) => void; // called after a new piece is dropped on the board
};
rookCastle?: boolean; // castle by moving the king to the rook
};
liftable?: {
liftDests?: cg.Key[]; // squares to remove a role/stone from
events?: {
after?: (dest: cg.Key) => void; //called after a lift have been played
};
};
premovable?: {
enabled?: boolean; // allow premoves for playerIndex that can not move
showDests?: boolean; // whether to add the premove-dest class on squares
castle?: boolean; // whether to allow king castle premoves
dests?: cg.Key[]; // premove destinations for the current selection
events?: {
set?: (orig: cg.Key, dest: cg.Key, metadata?: cg.SetPremoveMetadata) => void; // called after the premove has been set
unset?: () => void; // called after the premove has been unset
};
};
predroppable?: {
enabled?: boolean; // allow predrops for playerIndex that can not move
showDropDests?: boolean;
dropDests?: cg.Key[];
current?: {
// See corresponding type in state.ts for more comments
role: cg.Role;
key: cg.Key;
};
events?: {
set?: (role: cg.Role, key: cg.Key) => void; // called after the predrop has been set
unset?: () => void; // called after the predrop has been unset
};
};
draggable?: {
enabled?: boolean; // allow moves & premoves to use drag'n drop
distance?: number; // minimum distance to initiate a drag; in pixels
autoDistance?: boolean; // lets chessground set distance to zero when user drags pieces
centerPiece?: boolean; // center the piece on cursor at drag start
showGhost?: boolean; // show ghost of piece being dragged
deleteOnDropOff?: boolean; // delete a piece when it is dropped off the board
};
selectable?: {
// disable to enforce dragging over click-click move
enabled?: boolean;
};
events?: {
change?: () => void; // called after the situation changes on the board
// called after a piece has been moved.
// capturedPiece is undefined or like {playerIndex: 'p1'; 'role': 'queen'}
move?: (orig: cg.Key, dest: cg.Key, capturedPiece?: cg.Piece) => void;
dropNewPiece?: (piece: cg.Piece, key: cg.Key) => void;
select?: (key: cg.Key) => void; // called when a square is selected
insert?: (elements: cg.Elements) => void; // when the board DOM has been (re)inserted
selectDice?: (dice: cg.Dice[]) => void; //when the dice have been selected (to swap order)
undoButton?: () => void; //when the undo button hass been selected for backgammon
};
dropmode?: {
active?: boolean;
piece?: cg.Piece;
showDropDests?: boolean; // whether to add the move-dest class on squares for drops
dropDests?: cg.DropDests; // see corresponding state.ts type for comments
events?: {
cancel?: () => void; // at least temporary - i need to refresh pocket on cancel of drop mode (mainly to clear the highlighting of the selected pocket piece) and pocket is currently outside chessgroundx so need to provide callback here
};
};
drawable?: {
enabled?: boolean; // can draw
visible?: boolean; // can view
defaultSnapToValidMove?: boolean;
eraseOnClick?: boolean;
shapes?: DrawShape[];
autoShapes?: DrawShape[];
brushes?: DrawBrush[];
pieces?: {
baseUrl?: string;
};
onChange?: (shapes: DrawShape[]) => void; // called after drawable shapes change
};
dimensions?: cg.BoardDimensions;
variant?: cg.Variant;
chess960?: boolean;
notation?: cg.Notation;
onlyDropsVariant?: boolean;
singleClickMoveVariant?: boolean;
}
export function configure(state: HeadlessState, config: Config): void {
// don't merge destinations and autoShapes. Just override.
if (config.movable && config.movable.dests) state.movable.dests = undefined;
if (config.dropmode?.dropDests) state.dropmode.dropDests = undefined;
if (config.drawable?.autoShapes) state.drawable.autoShapes = [];
if (config.dice) state.dice = [];
merge(state, config);
if (config.dimensions) state.dimensions = config.dimensions;
// if a fen was provided, replace the pieces
if (config.fen) {
const pieces = fenRead(config.fen, state.dimensions, state.variant);
const pocketPieces = fenReadPocket(config.fen, state.variant);
// prevent to cancel() already started piece drag from pocket!
if (state.pieces.get('a0') !== undefined) pieces.set('a0', state.pieces.get('a0')!);
state.pieces = pieces;
state.pocketPieces = pocketPieces;
state.drawable.shapes = [];
}
// apply config values that could be undefined yet meaningful
if ('check' in config) setCheck(state, config.check || false);
if ('lastMove' in config && !config.lastMove) state.lastMove = undefined;
// in case of ZH drop last move, there's a single square.
// if the previous last move had two squares,
// the merge algorithm will incorrectly keep the second square.
else if (config.lastMove) state.lastMove = config.lastMove;
// fix move/premove dests
if (state.selected) setSelected(state, state.selected);
//fix go scores
setGoScore(state);
// no need for such short animations
if (!state.animation.duration || state.animation.duration < 100) state.animation.enabled = false;
if (!state.movable.rookCastle && state.movable.dests) {
const rank = state.movable.playerIndex === 'p1' ? 1 : 8,
kingStartPos = ('e' + rank) as cg.Key,
dests = state.movable.dests.get(kingStartPos),
king = state.pieces.get(kingStartPos);
if (!dests || !king || king.role !== 'k-piece') return;
state.movable.dests.set(
kingStartPos,
dests.filter(
d =>
!(d === 'a' + rank && dests.includes(('c' + rank) as cg.Key)) &&
!(d === 'h' + rank && dests.includes(('g' + rank) as cg.Key)),
),
);
}
// configure variants
if (state.variant === 'abalone') {
abaloneConfigure(state);
}
}
function setCheck(state: HeadlessState, playerIndex: cg.PlayerIndex | boolean): void {
state.check = undefined;
if (playerIndex === true) playerIndex = state.turnPlayerIndex;
if (playerIndex)
for (const [k, p] of state.pieces) {
if (p.role === 'k-piece' && p.playerIndex === playerIndex) {
state.check = k;
}
}
}
function merge(base: any, extend: any): void {
for (const key in extend) {
if (isObject(base[key]) && isObject(extend[key])) merge(base[key], extend[key]);
else base[key] = extend[key];
}
}
function isObject(o: unknown): boolean {
return typeof o === 'object';
}