@vnejs/plugins.views.screens.checkers 0.2.2 → 0.2.4
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/index.d.ts +3 -0
- package/dist/index.js +25 -12
- package/dist/modules/board.d.ts +10 -0
- package/dist/modules/board.js +32 -0
- package/dist/modules/bridge.d.ts +8 -0
- package/dist/modules/bridge.js +10 -0
- package/dist/modules/click.d.ts +8 -0
- package/dist/modules/click.js +14 -0
- package/dist/modules/controller.accept.d.ts +7 -25
- package/dist/modules/controller.accept.js +12 -159
- package/dist/modules/controller.d.ts +4 -6
- package/dist/modules/controller.grid.d.ts +11 -11
- package/dist/modules/controller.grid.js +21 -25
- package/dist/modules/controller.js +8 -26
- package/dist/modules/player.interact.d.ts +9 -0
- package/dist/modules/player.interact.js +33 -0
- package/dist/modules/player.select.d.ts +9 -0
- package/dist/modules/player.select.js +29 -0
- package/dist/modules/status.d.ts +18 -0
- package/dist/modules/status.js +24 -0
- package/dist/modules/view.d.ts +5 -5
- package/dist/modules/view.js +4 -4
- package/dist/types.d.ts +9 -5
- package/dist/utils/checkers.d.ts +20 -9
- package/dist/utils/checkers.js +28 -21
- package/dist/view/index.d.ts +2 -2
- package/dist/view/index.js +12 -12
- package/package.json +12 -2
- package/src/index.ts +25 -12
- package/src/modules/board.ts +47 -0
- package/src/modules/bridge.ts +20 -0
- package/src/modules/click.ts +28 -0
- package/src/modules/controller.accept.ts +20 -189
- package/src/modules/controller.grid.ts +23 -26
- package/src/modules/controller.ts +16 -36
- package/src/modules/player.interact.ts +48 -0
- package/src/modules/player.select.ts +42 -0
- package/src/modules/status.ts +41 -0
- package/src/modules/view.ts +12 -6
- package/src/tests/player.interact.test.ts +56 -0
- package/src/tests/setup.ts +24 -10
- package/src/types.ts +23 -6
- package/src/utils/checkers.ts +50 -31
- package/src/view/index.tsx +22 -23
- package/dist/modules/checkers.d.ts +0 -9
- package/dist/modules/checkers.js +0 -37
- package/dist/utils/ai.d.ts +0 -4
- package/dist/utils/ai.js +0 -27
- package/dist/utils/board.d.ts +0 -32
- package/dist/utils/board.js +0 -199
- package/src/modules/checkers.ts +0 -52
- package/src/tests/ai.test.ts +0 -61
- package/src/tests/board.test.ts +0 -43
- package/src/tests/checkers.test.ts +0 -58
- package/src/utils/ai.ts +0 -31
- package/src/utils/board.ts +0 -252
package/src/utils/board.ts
DELETED
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
export type CheckersColor = "white" | "black";
|
|
2
|
-
|
|
3
|
-
export type CheckersPiece = {
|
|
4
|
-
id: string;
|
|
5
|
-
color: CheckersColor;
|
|
6
|
-
king: boolean;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export type CheckersPos = {
|
|
10
|
-
row: number;
|
|
11
|
-
column: number;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export type CheckersMove = {
|
|
15
|
-
from: CheckersPos;
|
|
16
|
-
to: CheckersPos;
|
|
17
|
-
captures: CheckersPos[];
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export type CheckersBoard = (CheckersPiece | null)[][];
|
|
21
|
-
|
|
22
|
-
export const BOARD_SIZE = 8;
|
|
23
|
-
|
|
24
|
-
export const oppositeColor = (color: CheckersColor): CheckersColor => (color === "white" ? "black" : "white");
|
|
25
|
-
|
|
26
|
-
export const isDarkSquare = (row: number, column: number) => (row + column) % 2 === 1;
|
|
27
|
-
|
|
28
|
-
export const inBounds = (row: number, column: number) => row >= 0 && row < BOARD_SIZE && column >= 0 && column < BOARD_SIZE;
|
|
29
|
-
|
|
30
|
-
export const cloneBoard = (board: CheckersBoard): CheckersBoard => board.map((row) => row.map((cell) => (cell ? { ...cell } : null)));
|
|
31
|
-
|
|
32
|
-
export const createInitialBoard = (): CheckersBoard => {
|
|
33
|
-
const board: CheckersBoard = Array.from({ length: BOARD_SIZE }, () => Array.from({ length: BOARD_SIZE }, () => null));
|
|
34
|
-
let nextId = 0;
|
|
35
|
-
|
|
36
|
-
for (let row = 0; row < BOARD_SIZE; row++) {
|
|
37
|
-
for (let column = 0; column < BOARD_SIZE; column++) {
|
|
38
|
-
if (!isDarkSquare(row, column)) continue;
|
|
39
|
-
if (row <= 2) board[row]![column] = { id: `piece-${nextId++}`, color: "black", king: false };
|
|
40
|
-
if (row >= 5) board[row]![column] = { id: `piece-${nextId++}`, color: "white", king: false };
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return board;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const DIAGONALS = [
|
|
48
|
-
{ dr: -1, dc: -1 },
|
|
49
|
-
{ dr: -1, dc: 1 },
|
|
50
|
-
{ dr: 1, dc: -1 },
|
|
51
|
-
{ dr: 1, dc: 1 },
|
|
52
|
-
] as const;
|
|
53
|
-
|
|
54
|
-
const forwardRowDelta = (color: CheckersColor) => (color === "white" ? -1 : 1);
|
|
55
|
-
|
|
56
|
-
const posKey = (pos: CheckersPos) => `${pos.row}:${pos.column}`;
|
|
57
|
-
|
|
58
|
-
export const samePos = (a: CheckersPos, b: CheckersPos) => a.row === b.row && a.column === b.column;
|
|
59
|
-
|
|
60
|
-
const getPiece = (board: CheckersBoard, pos: CheckersPos) => board[pos.row]?.[pos.column] ?? null;
|
|
61
|
-
|
|
62
|
-
const setPiece = (board: CheckersBoard, pos: CheckersPos, piece: CheckersPiece | null) => {
|
|
63
|
-
board[pos.row]![pos.column] = piece;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const promoteIfNeeded = (piece: CheckersPiece, row: number): CheckersPiece => {
|
|
67
|
-
if (piece.king) return piece;
|
|
68
|
-
if (piece.color === "white" && row === 0) return { ...piece, king: true };
|
|
69
|
-
if (piece.color === "black" && row === BOARD_SIZE - 1) return { ...piece, king: true };
|
|
70
|
-
return piece;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const collectSimpleMoves = (board: CheckersBoard, from: CheckersPos, piece: CheckersPiece): CheckersMove[] => {
|
|
74
|
-
const moves: CheckersMove[] = [];
|
|
75
|
-
const deltas = piece.king ? DIAGONALS : DIAGONALS.filter(({ dr }) => dr === forwardRowDelta(piece.color));
|
|
76
|
-
|
|
77
|
-
for (const { dr, dc } of deltas) {
|
|
78
|
-
if (piece.king) {
|
|
79
|
-
let row = from.row + dr;
|
|
80
|
-
let column = from.column + dc;
|
|
81
|
-
while (inBounds(row, column) && !getPiece(board, { row, column })) {
|
|
82
|
-
moves.push({ from, to: { row, column }, captures: [] });
|
|
83
|
-
row += dr;
|
|
84
|
-
column += dc;
|
|
85
|
-
}
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const row = from.row + dr;
|
|
90
|
-
const column = from.column + dc;
|
|
91
|
-
if (!inBounds(row, column) || getPiece(board, { row, column })) continue;
|
|
92
|
-
moves.push({ from, to: { row, column }, captures: [] });
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return moves;
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const collectImmediateCaptures = (board: CheckersBoard, from: CheckersPos, piece: CheckersPiece, capturedKeys: Set<string>): CheckersMove[] => {
|
|
99
|
-
const moves: CheckersMove[] = [];
|
|
100
|
-
|
|
101
|
-
for (const { dr, dc } of DIAGONALS) {
|
|
102
|
-
if (piece.king) {
|
|
103
|
-
let row = from.row + dr;
|
|
104
|
-
let column = from.column + dc;
|
|
105
|
-
let enemy: CheckersPos | null = null;
|
|
106
|
-
|
|
107
|
-
while (inBounds(row, column)) {
|
|
108
|
-
const occupant = getPiece(board, { row, column });
|
|
109
|
-
|
|
110
|
-
if (!occupant) {
|
|
111
|
-
if (enemy) {
|
|
112
|
-
moves.push({ from, to: { row, column }, captures: [enemy] });
|
|
113
|
-
}
|
|
114
|
-
row += dr;
|
|
115
|
-
column += dc;
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (enemy) break;
|
|
120
|
-
if (occupant.color === piece.color || capturedKeys.has(posKey({ row, column }))) break;
|
|
121
|
-
|
|
122
|
-
enemy = { row, column };
|
|
123
|
-
row += dr;
|
|
124
|
-
column += dc;
|
|
125
|
-
}
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const mid: CheckersPos = { row: from.row + dr, column: from.column + dc };
|
|
130
|
-
const to: CheckersPos = { row: from.row + 2 * dr, column: from.column + 2 * dc };
|
|
131
|
-
if (!inBounds(to.row, to.column) || getPiece(board, to)) continue;
|
|
132
|
-
|
|
133
|
-
const victim = getPiece(board, mid);
|
|
134
|
-
if (!victim || victim.color === piece.color || capturedKeys.has(posKey(mid))) continue;
|
|
135
|
-
|
|
136
|
-
moves.push({ from, to, captures: [mid] });
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return moves;
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
const expandCaptureChains = (
|
|
143
|
-
board: CheckersBoard,
|
|
144
|
-
from: CheckersPos,
|
|
145
|
-
piece: CheckersPiece,
|
|
146
|
-
origin: CheckersPos,
|
|
147
|
-
capturedSoFar: CheckersPos[],
|
|
148
|
-
capturedKeys: Set<string>,
|
|
149
|
-
): CheckersMove[] => {
|
|
150
|
-
const immediate = collectImmediateCaptures(board, from, piece, capturedKeys);
|
|
151
|
-
if (immediate.length === 0) {
|
|
152
|
-
return capturedSoFar.length > 0 ? [{ from: origin, to: from, captures: capturedSoFar }] : [];
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const results: CheckersMove[] = [];
|
|
156
|
-
|
|
157
|
-
for (const step of immediate) {
|
|
158
|
-
const nextBoard = cloneBoard(board);
|
|
159
|
-
setPiece(nextBoard, from, null);
|
|
160
|
-
for (const capture of step.captures) setPiece(nextBoard, capture, null);
|
|
161
|
-
const nextPiece = promoteIfNeeded(piece, step.to.row);
|
|
162
|
-
setPiece(nextBoard, step.to, nextPiece);
|
|
163
|
-
|
|
164
|
-
const nextCaptures = [...capturedSoFar, ...step.captures];
|
|
165
|
-
const nextKeys = new Set(capturedKeys);
|
|
166
|
-
for (const capture of step.captures) nextKeys.add(posKey(capture));
|
|
167
|
-
|
|
168
|
-
results.push(...expandCaptureChains(nextBoard, step.to, nextPiece, origin, nextCaptures, nextKeys));
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return results;
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
export const getMovesFrom = (board: CheckersBoard, from: CheckersPos): CheckersMove[] => {
|
|
175
|
-
const piece = getPiece(board, from);
|
|
176
|
-
if (!piece) return [];
|
|
177
|
-
|
|
178
|
-
const captures = expandCaptureChains(board, from, piece, from, [], new Set());
|
|
179
|
-
if (captures.length > 0) return captures;
|
|
180
|
-
return collectSimpleMoves(board, from, piece);
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
export const getAllMoves = (board: CheckersBoard, color: CheckersColor, onlyFrom?: CheckersPos): CheckersMove[] => {
|
|
184
|
-
const moves: CheckersMove[] = [];
|
|
185
|
-
|
|
186
|
-
for (let row = 0; row < BOARD_SIZE; row++) {
|
|
187
|
-
for (let column = 0; column < BOARD_SIZE; column++) {
|
|
188
|
-
if (onlyFrom && !samePos(onlyFrom, { row, column })) continue;
|
|
189
|
-
const piece = board[row]?.[column];
|
|
190
|
-
if (!piece || piece.color !== color) continue;
|
|
191
|
-
moves.push(...getMovesFrom(board, { row, column }));
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const hasCapture = moves.some((move) => move.captures.length > 0);
|
|
196
|
-
return hasCapture ? moves.filter((move) => move.captures.length > 0) : moves;
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
export const applyMove = (board: CheckersBoard, move: CheckersMove): CheckersBoard => {
|
|
200
|
-
const next = cloneBoard(board);
|
|
201
|
-
const piece = getPiece(next, move.from);
|
|
202
|
-
if (!piece) return next;
|
|
203
|
-
|
|
204
|
-
setPiece(next, move.from, null);
|
|
205
|
-
for (const capture of move.captures) setPiece(next, capture, null);
|
|
206
|
-
setPiece(next, move.to, promoteIfNeeded(piece, move.to.row));
|
|
207
|
-
return next;
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
/** Apply one hop of a multi-capture chain (first capture only). */
|
|
211
|
-
export const applyCaptureStep = (board: CheckersBoard, from: CheckersPos, to: CheckersPos, capture: CheckersPos): CheckersBoard => {
|
|
212
|
-
const next = cloneBoard(board);
|
|
213
|
-
const piece = getPiece(next, from);
|
|
214
|
-
if (!piece) return next;
|
|
215
|
-
|
|
216
|
-
setPiece(next, from, null);
|
|
217
|
-
setPiece(next, capture, null);
|
|
218
|
-
setPiece(next, to, promoteIfNeeded(piece, to.row));
|
|
219
|
-
return next;
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
export const listBoardPieces = (board: CheckersBoard) => {
|
|
223
|
-
const pieces: Array<CheckersPiece & CheckersPos> = [];
|
|
224
|
-
|
|
225
|
-
for (let row = 0; row < BOARD_SIZE; row++) {
|
|
226
|
-
for (let column = 0; column < BOARD_SIZE; column++) {
|
|
227
|
-
const piece = board[row]?.[column];
|
|
228
|
-
if (!piece) continue;
|
|
229
|
-
pieces.push({ ...piece, row, column });
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return pieces;
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
export const countPieces = (board: CheckersBoard, color: CheckersColor) => {
|
|
237
|
-
let count = 0;
|
|
238
|
-
for (const row of board) {
|
|
239
|
-
for (const cell of row) {
|
|
240
|
-
if (cell?.color === color) count++;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
return count;
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
export const getWinner = (board: CheckersBoard, colorToMove: CheckersColor): CheckersColor | null => {
|
|
247
|
-
if (countPieces(board, colorToMove) === 0) return oppositeColor(colorToMove);
|
|
248
|
-
if (getAllMoves(board, colorToMove).length === 0) return oppositeColor(colorToMove);
|
|
249
|
-
return null;
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
export const findMove = (moves: CheckersMove[], from: CheckersPos, to: CheckersPos) => moves.find((move) => samePos(move.from, from) && samePos(move.to, to));
|