@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/dist/modules/view.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { ModuleView } from "@vnejs/module.components";
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
export declare class
|
|
2
|
+
import type { CheckersViewPluginConstants, CheckersViewPluginEvents, CheckersViewPluginParams, CheckersViewPluginSettings } from "../types.js";
|
|
3
|
+
import type { CheckersViewState } from "../utils/checkers.js";
|
|
4
|
+
export declare class CheckersViewView extends ModuleView<CheckersViewPluginEvents, CheckersViewPluginConstants, CheckersViewPluginSettings, CheckersViewPluginParams, CheckersViewState> {
|
|
5
5
|
name: string;
|
|
6
|
-
EVENT_UPDATE_VIEW: "vne:
|
|
6
|
+
EVENT_UPDATE_VIEW: "vne:checkers_view:update";
|
|
7
7
|
LOC_LABEL: null;
|
|
8
8
|
TRANSITION: number;
|
|
9
|
-
renderFunc: import("@vnejs/module.components").ViewRenderFunc<
|
|
9
|
+
renderFunc: import("@vnejs/module.components").ViewRenderFunc<CheckersViewState>;
|
|
10
10
|
}
|
package/dist/modules/view.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ModuleView } from "@vnejs/module.components";
|
|
2
2
|
import { render } from "../view/index.js";
|
|
3
|
-
export class
|
|
4
|
-
name = "
|
|
5
|
-
EVENT_UPDATE_VIEW = this.EVENTS.
|
|
3
|
+
export class CheckersViewView extends ModuleView {
|
|
4
|
+
name = "checkers_view.view";
|
|
5
|
+
EVENT_UPDATE_VIEW = this.EVENTS.CHECKERS_VIEW.UPDATE;
|
|
6
6
|
LOC_LABEL = null;
|
|
7
|
-
TRANSITION = this.PARAMS.
|
|
7
|
+
TRANSITION = this.PARAMS.CHECKERS_VIEW.TRANSITION;
|
|
8
8
|
renderFunc = render;
|
|
9
9
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import type { ModuleComponentsConstants, ModuleComponentsEvents, ModuleComponentsParams, ModuleComponentsSettings } from "@vnejs/module.components";
|
|
2
|
+
import type { Constants as AiConstants, Params as AiParams, PluginName as AiPluginName, SubscribeEvents as AiSubscribeEvents } from "@vnejs/contracts.ai";
|
|
2
3
|
import type { Constants as ControlsConstants, PluginName as ControlsPluginName } from "@vnejs/contracts.controls";
|
|
3
4
|
import type { Constants as ScenarioConstants, PluginName as ScenarioPluginName, SubscribeEvents as ScenarioSubscribeEvents } from "@vnejs/contracts.core.scenario";
|
|
4
5
|
import type { PluginName as LogsPluginName, SubscribeEvents as LogsSubscribeEvents } from "@vnejs/contracts.core.logs";
|
|
5
|
-
import type {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export type
|
|
6
|
+
import type { PluginName as GridPluginName, SubscribeEvents as GridSubscribeEvents } from "@vnejs/contracts.common.grid";
|
|
7
|
+
import type { PluginName as TurnsPluginName, SubscribeEvents as TurnsSubscribeEvents } from "@vnejs/contracts.common.turn";
|
|
8
|
+
import type { Constants as CheckersConstants, Params as CheckersParams, PluginName as CheckersPluginName, SubscribeEvents as CheckersSubscribeEvents } from "@vnejs/contracts.game.checkers";
|
|
9
|
+
import type { Params, PluginName, SubscribeEvents } from "@vnejs/contracts.views.screens.checkers";
|
|
10
|
+
export type CheckersViewPluginEvents = ModuleComponentsEvents & Record<PluginName, SubscribeEvents> & Record<CheckersPluginName, CheckersSubscribeEvents> & Record<GridPluginName, GridSubscribeEvents> & Record<TurnsPluginName, TurnsSubscribeEvents> & Record<AiPluginName, AiSubscribeEvents> & Record<ScenarioPluginName, ScenarioSubscribeEvents> & Record<LogsPluginName, LogsSubscribeEvents>;
|
|
11
|
+
export type CheckersViewPluginConstants = ModuleComponentsConstants & Record<CheckersPluginName, CheckersConstants> & Record<AiPluginName, AiConstants> & Record<ControlsPluginName, ControlsConstants> & Record<ScenarioPluginName, ScenarioConstants>;
|
|
12
|
+
export type CheckersViewPluginSettings = ModuleComponentsSettings;
|
|
13
|
+
export type CheckersViewPluginParams = ModuleComponentsParams & Record<PluginName, Params> & Record<CheckersPluginName, CheckersParams> & Record<AiPluginName, AiParams>;
|
package/dist/utils/checkers.d.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { GridChangedPayload } from "@vnejs/contracts.common.grid";
|
|
2
|
+
import type { CheckersMovePayload, CheckersPosPayload } from "@vnejs/contracts.game.checkers";
|
|
3
|
+
export declare const BOARD_SIZE = 8;
|
|
4
|
+
export type CheckersColor = "white" | "black";
|
|
5
|
+
export type CheckersPiece = {
|
|
6
|
+
id: string;
|
|
7
|
+
color: CheckersColor;
|
|
8
|
+
king: boolean;
|
|
9
|
+
};
|
|
10
|
+
export type CheckersPos = CheckersPosPayload;
|
|
11
|
+
export type CheckersMove = CheckersMovePayload;
|
|
12
|
+
export type CheckersBoard = (CheckersPiece | null)[][];
|
|
2
13
|
export type CheckersGridItem = {
|
|
3
14
|
row: number;
|
|
4
15
|
column: number;
|
|
@@ -8,7 +19,7 @@ export type CheckersGridItem = {
|
|
|
8
19
|
isLegalTarget?: boolean;
|
|
9
20
|
isFocus?: boolean;
|
|
10
21
|
};
|
|
11
|
-
export type
|
|
22
|
+
export type CheckersViewState = {
|
|
12
23
|
isShow: boolean;
|
|
13
24
|
isForce: boolean;
|
|
14
25
|
board?: CheckersBoard;
|
|
@@ -22,16 +33,13 @@ export type CheckersPluginState = {
|
|
|
22
33
|
gridItems?: CheckersGridItem[];
|
|
23
34
|
gridIndex?: number;
|
|
24
35
|
gridsCount?: number;
|
|
25
|
-
ai?: {
|
|
26
|
-
white: string;
|
|
27
|
-
black: string;
|
|
28
|
-
};
|
|
29
36
|
};
|
|
30
|
-
export type
|
|
37
|
+
export type CheckersClickPayload = {
|
|
31
38
|
row?: number;
|
|
32
39
|
column?: number;
|
|
33
40
|
};
|
|
34
|
-
export declare const
|
|
41
|
+
export declare const isDarkSquare: (row: number, column: number) => boolean;
|
|
42
|
+
export declare const samePos: (a: CheckersPos, b: CheckersPos) => boolean;
|
|
35
43
|
export declare const buildGridItems: ({ board, selected, legalMoves, gridRow, gridColumn, }: {
|
|
36
44
|
board: CheckersBoard;
|
|
37
45
|
selected?: CheckersPos | null;
|
|
@@ -39,4 +47,7 @@ export declare const buildGridItems: ({ board, selected, legalMoves, gridRow, gr
|
|
|
39
47
|
gridRow?: number | null;
|
|
40
48
|
gridColumn?: number | null;
|
|
41
49
|
}) => CheckersGridItem[];
|
|
42
|
-
export declare const
|
|
50
|
+
export declare const listBoardPieces: (board: CheckersBoard) => (CheckersPiece & CheckersPosPayload)[];
|
|
51
|
+
export declare const emptyBoard: () => CheckersBoard;
|
|
52
|
+
export declare const statusForTurn: (turn?: CheckersColor, winner?: CheckersColor | null) => "" | "Black to move" | "Black wins" | "White to move" | "White wins";
|
|
53
|
+
export declare const fromGridChanged: (payload?: GridChangedPayload) => Partial<CheckersViewState>;
|
package/dist/utils/checkers.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
export const
|
|
3
|
-
|
|
4
|
-
return winner === "white" ? "White wins" : "Black wins";
|
|
5
|
-
return turn === "white" ? "White to move" : "Black to move";
|
|
6
|
-
};
|
|
1
|
+
export const BOARD_SIZE = 8;
|
|
2
|
+
export const isDarkSquare = (row, column) => (row + column) % 2 === 1;
|
|
3
|
+
export const samePos = (a, b) => a.row === b.row && a.column === b.column;
|
|
7
4
|
export const buildGridItems = ({ board, selected = null, legalMoves = [], gridRow = null, gridColumn = null, }) => {
|
|
8
5
|
const legalKeys = new Set(legalMoves.map((move) => `${move.to.row}:${move.to.column}`));
|
|
9
6
|
const items = [];
|
|
@@ -22,19 +19,29 @@ export const buildGridItems = ({ board, selected = null, legalMoves = [], gridRo
|
|
|
22
19
|
}
|
|
23
20
|
return items;
|
|
24
21
|
};
|
|
25
|
-
export const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
22
|
+
export const listBoardPieces = (board) => {
|
|
23
|
+
const pieces = [];
|
|
24
|
+
for (let row = 0; row < BOARD_SIZE; row++) {
|
|
25
|
+
for (let column = 0; column < BOARD_SIZE; column++) {
|
|
26
|
+
const piece = board[row]?.[column];
|
|
27
|
+
if (!piece)
|
|
28
|
+
continue;
|
|
29
|
+
pieces.push({ ...piece, row, column });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return pieces;
|
|
33
|
+
};
|
|
34
|
+
export const emptyBoard = () => Array.from({ length: BOARD_SIZE }, () => Array.from({ length: BOARD_SIZE }, () => null));
|
|
35
|
+
export const statusForTurn = (turn, winner = null) => {
|
|
36
|
+
if (winner)
|
|
37
|
+
return winner === "white" ? "White wins" : "Black wins";
|
|
38
|
+
if (!turn)
|
|
39
|
+
return "";
|
|
40
|
+
return turn === "white" ? "White to move" : "Black to move";
|
|
40
41
|
};
|
|
42
|
+
export const fromGridChanged = (payload = {}) => ({
|
|
43
|
+
board: payload.cells,
|
|
44
|
+
// selection is view-only — reset on every game model change
|
|
45
|
+
selected: null,
|
|
46
|
+
legalMoves: [],
|
|
47
|
+
});
|
package/dist/view/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { ViewRenderFunc } from "@vnejs/module.components";
|
|
2
|
-
import type {
|
|
3
|
-
export declare const render: ViewRenderFunc<
|
|
2
|
+
import type { CheckersViewState } from "../utils/checkers.js";
|
|
3
|
+
export declare const render: ViewRenderFunc<CheckersViewState>;
|
package/dist/view/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Flex, PositionBox, Screen, Text, createRenderFunc, useCallback, useEffect, useMemo, useState, useStoreState } from "@vnejs/uis.react";
|
|
3
3
|
import { getVneLength } from "@vnejs/uis.utils";
|
|
4
|
-
import { listBoardPieces } from "../utils/
|
|
4
|
+
import { listBoardPieces } from "../utils/checkers.js";
|
|
5
5
|
const SELECTED_OPACITY = 0.7;
|
|
6
6
|
const GRAVEYARD_STACK = 0.55;
|
|
7
7
|
const cellBackground = (item) => {
|
|
@@ -37,13 +37,13 @@ const kingMarkLook = () => ({
|
|
|
37
37
|
});
|
|
38
38
|
const CheckersScreen = ({ store, onMount, PARAMS, emit, EVENTS }) => {
|
|
39
39
|
const { isShow = false, isForce = false, board, gridItems = [], gridRow = null, gridColumn = null, selected = null, statusText = "", } = useStoreState(store, onMount);
|
|
40
|
-
const transition = PARAMS.
|
|
41
|
-
const propsByView = PARAMS.
|
|
42
|
-
const rows = PARAMS.
|
|
43
|
-
const columns = PARAMS.
|
|
44
|
-
const cellSize = PARAMS.
|
|
45
|
-
const cellGap = PARAMS.
|
|
46
|
-
const graveyardGap = PARAMS.
|
|
40
|
+
const transition = PARAMS.CHECKERS_VIEW.TRANSITION;
|
|
41
|
+
const propsByView = PARAMS.CHECKERS_VIEW.VIEW_PROPS;
|
|
42
|
+
const rows = PARAMS.CHECKERS_VIEW.ROWS;
|
|
43
|
+
const columns = PARAMS.CHECKERS_VIEW.COLUMNS;
|
|
44
|
+
const cellSize = PARAMS.CHECKERS_VIEW.CELL_SIZE;
|
|
45
|
+
const cellGap = PARAMS.CHECKERS_VIEW.CELL_GAP;
|
|
46
|
+
const graveyardGap = PARAMS.CHECKERS_VIEW.GRAVEYARD_GAP;
|
|
47
47
|
const boardSize = columns * cellSize + (columns - 1) * cellGap;
|
|
48
48
|
const playfieldWidth = cellSize + graveyardGap + boardSize + graveyardGap + cellSize;
|
|
49
49
|
const boardPieces = useMemo(() => (board ? listBoardPieces(board) : []), [board]);
|
|
@@ -169,12 +169,12 @@ const CheckersScreen = ({ store, onMount, PARAMS, emit, EVENTS }) => {
|
|
|
169
169
|
}
|
|
170
170
|
return `translate(calc(${boardOffsetX} + ${piece.column} * ${stepLen}), calc(${piece.row} * ${stepLen}))`;
|
|
171
171
|
}, [blackGraveyardX, boardOffsetX, cellLen, stepLen]);
|
|
172
|
-
const
|
|
173
|
-
void emit(EVENTS.
|
|
174
|
-
}, [emit, EVENTS.
|
|
172
|
+
const onClick = useCallback((row, column) => {
|
|
173
|
+
void emit(EVENTS.CHECKERS_VIEW.CLICK, { row, column });
|
|
174
|
+
}, [emit, EVENTS.CHECKERS_VIEW.CLICK]);
|
|
175
175
|
return (_jsx(Screen, { ...propsScreen, children: _jsx(PositionBox, { ...propsByView.position, children: _jsxs(Flex, { ...propsByView.flex, children: [_jsx(Text, { ...propsStatus }), _jsxs("div", { style: playfieldStyle, children: [_jsx("div", { style: graveyardStyle("left") }), _jsx("div", { style: graveyardStyle("right") }), _jsx("div", { style: boardStyle, children: gridItems.map((item) => {
|
|
176
176
|
const isFocus = gridRow === item.row && gridColumn === item.column;
|
|
177
|
-
return (_jsx("div", { onClick: () =>
|
|
177
|
+
return (_jsx("div", { onClick: () => onClick(item.row, item.column), "vne-cursor-type": "pointer", style: {
|
|
178
178
|
...cellBaseStyle,
|
|
179
179
|
background: cellBackground({ ...item, isFocus }),
|
|
180
180
|
} }, `${item.row}-${item.column}`));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vnejs/plugins.views.screens.checkers",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "",
|
|
3
|
+
"version": "0.2.4",
|
|
4
|
+
"description": "Checkers screen view (CHECKERS_VIEW)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"exports": {
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
"author": "",
|
|
31
31
|
"license": "ISC",
|
|
32
32
|
"dependencies": {
|
|
33
|
+
"@vnejs/contracts.ai": "~0.2.0",
|
|
34
|
+
"@vnejs/contracts.common.grid": "~0.2.0",
|
|
35
|
+
"@vnejs/contracts.common.turn": "~0.2.0",
|
|
36
|
+
"@vnejs/contracts.game.checkers": "~0.2.0",
|
|
33
37
|
"@vnejs/contracts.views.screens.checkers": "~0.2.0"
|
|
34
38
|
},
|
|
35
39
|
"peerDependencies": {
|
|
@@ -40,13 +44,18 @@
|
|
|
40
44
|
"@vnejs/module.controller.arrows.grid": "~0.2.0",
|
|
41
45
|
"@vnejs/contracts.controls": "~0.2.0",
|
|
42
46
|
"@vnejs/shared": "~0.2.0",
|
|
47
|
+
"@vnejs/sources-set": "~0.2.0",
|
|
43
48
|
"@vnejs/uis.react": "~0.2.0"
|
|
44
49
|
},
|
|
45
50
|
"devDependencies": {
|
|
46
51
|
"@vnejs/configs.ts-common": "~0.2.0",
|
|
47
52
|
"@vnejs/configs.vitest": "~0.2.0",
|
|
48
53
|
"@vnejs/test-utils": "~0.2.0",
|
|
54
|
+
"@vnejs/contracts.ai": "~0.2.0",
|
|
55
|
+
"@vnejs/contracts.common.grid": "~0.2.0",
|
|
56
|
+
"@vnejs/contracts.common.turn": "~0.2.0",
|
|
49
57
|
"@vnejs/contracts.controls": "~0.2.0",
|
|
58
|
+
"@vnejs/contracts.game.checkers": "~0.2.0",
|
|
50
59
|
"@vnejs/contracts.views.screens.checkers": "~0.2.0",
|
|
51
60
|
"@vnejs/helpers": "~0.2.0",
|
|
52
61
|
"@vnejs/module.core": "~0.2.0",
|
|
@@ -54,6 +63,7 @@
|
|
|
54
63
|
"@vnejs/module.controller.accept": "~0.2.0",
|
|
55
64
|
"@vnejs/module.controller.arrows.grid": "~0.2.0",
|
|
56
65
|
"@vnejs/shared": "~0.2.0",
|
|
66
|
+
"@vnejs/sources-set": "~0.2.0",
|
|
57
67
|
"@vnejs/uis.react": "~0.2.0"
|
|
58
68
|
}
|
|
59
69
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,18 +1,31 @@
|
|
|
1
|
+
import "@vnejs/contracts.common.grid";
|
|
2
|
+
import "@vnejs/contracts.common.turn";
|
|
3
|
+
import "@vnejs/contracts.game.checkers";
|
|
1
4
|
import "@vnejs/contracts.views.screens.checkers";
|
|
2
5
|
|
|
3
6
|
import { regPlugin } from "@vnejs/shared";
|
|
4
|
-
import {
|
|
7
|
+
import { PARAMS, PLUGIN_NAME, SUBSCRIBE_EVENTS } from "@vnejs/contracts.views.screens.checkers";
|
|
5
8
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { CheckersViewBoard } from "./modules/board.js";
|
|
10
|
+
import { CheckersViewBridge } from "./modules/bridge.js";
|
|
11
|
+
import { CheckersViewClick } from "./modules/click.js";
|
|
12
|
+
import { CheckersViewController } from "./modules/controller.js";
|
|
13
|
+
import { CheckersViewControllerAccept } from "./modules/controller.accept.js";
|
|
14
|
+
import { CheckersViewControllerGrid } from "./modules/controller.grid.js";
|
|
15
|
+
import { CheckersViewPlayerInteract } from "./modules/player.interact.js";
|
|
16
|
+
import { CheckersViewPlayerSelect } from "./modules/player.select.js";
|
|
17
|
+
import { CheckersViewStatus } from "./modules/status.js";
|
|
18
|
+
import { CheckersViewView } from "./modules/view.js";
|
|
11
19
|
|
|
12
|
-
regPlugin(PLUGIN_NAME, {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
regPlugin(PLUGIN_NAME, { events: SUBSCRIBE_EVENTS, params: PARAMS }, [
|
|
21
|
+
CheckersViewBridge,
|
|
22
|
+
CheckersViewController,
|
|
23
|
+
CheckersViewControllerGrid,
|
|
24
|
+
CheckersViewControllerAccept,
|
|
25
|
+
CheckersViewPlayerInteract,
|
|
26
|
+
CheckersViewPlayerSelect,
|
|
27
|
+
CheckersViewClick,
|
|
28
|
+
CheckersViewBoard,
|
|
29
|
+
CheckersViewStatus,
|
|
30
|
+
CheckersViewView,
|
|
18
31
|
]);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ModuleCore } from "@vnejs/module.core";
|
|
2
|
+
import type { GridChangedPayload, GridUnsetPayload } from "@vnejs/contracts.common.grid";
|
|
3
|
+
|
|
4
|
+
import type { CheckersViewPluginConstants, CheckersViewPluginEvents, CheckersViewPluginParams, CheckersViewPluginSettings } from "../types.js";
|
|
5
|
+
import { buildGridItems, emptyBoard, fromGridChanged, type CheckersViewState } from "../utils/checkers.js";
|
|
6
|
+
|
|
7
|
+
export class CheckersViewBoard extends ModuleCore<
|
|
8
|
+
CheckersViewPluginEvents,
|
|
9
|
+
CheckersViewPluginConstants,
|
|
10
|
+
CheckersViewPluginSettings,
|
|
11
|
+
CheckersViewPluginParams,
|
|
12
|
+
CheckersViewState
|
|
13
|
+
> {
|
|
14
|
+
name = "checkers_view.board";
|
|
15
|
+
|
|
16
|
+
subscribe = () => {
|
|
17
|
+
this.on(this.EVENTS.GRID.CHANGED, this.onGridChanged);
|
|
18
|
+
this.on(this.EVENTS.GRID.UNSET, this.onGridUnset);
|
|
19
|
+
this.on(this.EVENTS.CHECKERS_VIEW.STATE_UPDATE, this.updateState);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
onGridChanged = async (payload: GridChangedPayload = {}) => {
|
|
23
|
+
if (payload.id !== this.CONST.CHECKERS.GRID_ID) return;
|
|
24
|
+
|
|
25
|
+
const mirror = fromGridChanged(payload);
|
|
26
|
+
const board = mirror.board ?? this.state.board ?? emptyBoard();
|
|
27
|
+
const gridItems = buildGridItems({ board, gridRow: this.state.gridRow ?? null, gridColumn: this.state.gridColumn ?? null });
|
|
28
|
+
|
|
29
|
+
await this.emit(this.EVENTS.CHECKERS_VIEW.STATE_UPDATE, { ...mirror, board, gridItems } satisfies Partial<CheckersViewState>);
|
|
30
|
+
await this.emit(this.EVENTS.CHECKERS_VIEW.UPDATE_FAST);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
onGridUnset = async ({ id }: GridUnsetPayload = {}) => {
|
|
34
|
+
if (id !== this.CONST.CHECKERS.GRID_ID) return;
|
|
35
|
+
|
|
36
|
+
const board = emptyBoard();
|
|
37
|
+
const gridItems = buildGridItems({ board, gridRow: this.state.gridRow ?? null, gridColumn: this.state.gridColumn ?? null });
|
|
38
|
+
|
|
39
|
+
await this.emit(this.EVENTS.CHECKERS_VIEW.STATE_UPDATE, {
|
|
40
|
+
board,
|
|
41
|
+
selected: null,
|
|
42
|
+
legalMoves: [],
|
|
43
|
+
gridItems,
|
|
44
|
+
} satisfies Partial<CheckersViewState>);
|
|
45
|
+
await this.emit(this.EVENTS.CHECKERS_VIEW.UPDATE_FAST);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ModuleCore } from "@vnejs/module.core";
|
|
2
|
+
|
|
3
|
+
import type { CheckersViewPluginConstants, CheckersViewPluginEvents, CheckersViewPluginParams, CheckersViewPluginSettings } from "../types.js";
|
|
4
|
+
|
|
5
|
+
export class CheckersViewBridge extends ModuleCore<
|
|
6
|
+
CheckersViewPluginEvents,
|
|
7
|
+
CheckersViewPluginConstants,
|
|
8
|
+
CheckersViewPluginSettings,
|
|
9
|
+
CheckersViewPluginParams
|
|
10
|
+
> {
|
|
11
|
+
name = "checkers_view.bridge";
|
|
12
|
+
|
|
13
|
+
subscribe = () => {
|
|
14
|
+
this.on(this.EVENTS.CHECKERS.SHOW, this.onCheckersShow);
|
|
15
|
+
this.on(this.EVENTS.CHECKERS.HIDE, this.onCheckersHide);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
onCheckersShow = () => this.emit(this.EVENTS.CHECKERS_VIEW.SHOW);
|
|
19
|
+
onCheckersHide = () => this.emit(this.EVENTS.CHECKERS_VIEW.HIDE);
|
|
20
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ModuleCore } from "@vnejs/module.core";
|
|
2
|
+
import type { CheckersPlayerInteractPayload } from "@vnejs/contracts.views.screens.checkers";
|
|
3
|
+
|
|
4
|
+
import type { CheckersViewPluginConstants, CheckersViewPluginEvents, CheckersViewPluginParams, CheckersViewPluginSettings } from "../types.js";
|
|
5
|
+
import type { CheckersClickPayload, CheckersViewState } from "../utils/checkers.js";
|
|
6
|
+
|
|
7
|
+
export class CheckersViewClick extends ModuleCore<
|
|
8
|
+
CheckersViewPluginEvents,
|
|
9
|
+
CheckersViewPluginConstants,
|
|
10
|
+
CheckersViewPluginSettings,
|
|
11
|
+
CheckersViewPluginParams,
|
|
12
|
+
CheckersViewState
|
|
13
|
+
> {
|
|
14
|
+
name = "checkers_view.click";
|
|
15
|
+
|
|
16
|
+
subscribe = () => {
|
|
17
|
+
this.on(this.EVENTS.CHECKERS_VIEW.CLICK, this.onClick);
|
|
18
|
+
this.on(this.EVENTS.CHECKERS_VIEW.STATE_UPDATE, this.updateState);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
onClick = async ({ row, column }: CheckersClickPayload = {}) => {
|
|
22
|
+
if (this.shared.checkersInteractDisableSources.isNotEmpty() || row == null || column == null) return;
|
|
23
|
+
|
|
24
|
+
await this.emit(this.EVENTS.CHECKERS_VIEW.GRID_CLEAR);
|
|
25
|
+
|
|
26
|
+
this.emit(this.EVENTS.CHECKERS_VIEW.PLAYER_INTERACT, { row, column } satisfies CheckersPlayerInteractPayload);
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -1,202 +1,33 @@
|
|
|
1
1
|
import { ModuleControllerAccept } from "@vnejs/module.controller.accept";
|
|
2
|
-
import type {
|
|
2
|
+
import type { CheckersPlayerInteractPayload } from "@vnejs/contracts.views.screens.checkers";
|
|
3
3
|
|
|
4
|
-
import type {
|
|
4
|
+
import type { CheckersViewPluginConstants, CheckersViewPluginEvents, CheckersViewPluginParams, CheckersViewPluginSettings } from "../types.js";
|
|
5
|
+
import type { CheckersViewState } from "../utils/checkers.js";
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
CheckersPluginEvents,
|
|
13
|
-
CheckersPluginConstants,
|
|
14
|
-
CheckersPluginSettings,
|
|
15
|
-
CheckersPluginParams,
|
|
16
|
-
CheckersPluginState
|
|
7
|
+
export class CheckersViewControllerAccept extends ModuleControllerAccept<
|
|
8
|
+
CheckersViewPluginEvents,
|
|
9
|
+
CheckersViewPluginConstants,
|
|
10
|
+
CheckersViewPluginSettings,
|
|
11
|
+
CheckersViewPluginParams,
|
|
12
|
+
CheckersViewState
|
|
17
13
|
> {
|
|
18
|
-
name = "
|
|
19
|
-
|
|
20
|
-
EVENT_SHOW = this.EVENTS.CHECKERS.SHOW;
|
|
21
|
-
EVENT_HIDE = this.EVENTS.CHECKERS.HIDE;
|
|
22
|
-
EVENT_ACCEPT = this.EVENTS.CHECKERS.ACCEPT;
|
|
23
|
-
EVENT_STATE_UPDATE = this.EVENTS.CHECKERS.STATE_UPDATE;
|
|
24
|
-
|
|
25
|
-
CONTROLS_ZINDEX = this.PARAMS.CHECKERS.ZINDEX;
|
|
26
|
-
|
|
27
|
-
subscribe = () => {
|
|
28
|
-
this.on(this.EVENT_SHOW, this.onShow);
|
|
29
|
-
this.on(this.EVENT_HIDE, this.onHide);
|
|
30
|
-
this.on(this.EVENT_ACCEPT, this.onAccept);
|
|
31
|
-
if (this.EVENT_STATE_UPDATE) this.on(this.EVENT_STATE_UPDATE, this.updateState);
|
|
32
|
-
this.on(this.EVENTS.CHECKERS.CELL_CLICK, this.onCellClick);
|
|
33
|
-
this.on(this.EVENTS.CHECKERS.AI_TURN, this.onAiTurn);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
onShow = () => void this.onShowAndMaybeAi();
|
|
37
|
-
|
|
38
|
-
onShowAndMaybeAi = async () => {
|
|
39
|
-
await this.emit(this.EVENTS.CONTROLS.PUSH, {
|
|
40
|
-
key: this.name,
|
|
41
|
-
controls: this.CONTROLS,
|
|
42
|
-
index: this.CONTROLS_ZINDEX + 1,
|
|
43
|
-
checkNext: true,
|
|
44
|
-
} satisfies ControlsPushPayload);
|
|
45
|
-
|
|
46
|
-
// SHOW handlers run in parallel with controller.beforeShow — wait for board.
|
|
47
|
-
await this.waitCheck(() => Boolean(this.state.board && this.state.turn));
|
|
48
|
-
await this.onAiTurn();
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
onHide = () => {
|
|
52
|
-
this.shared.checkersAiBusy = false;
|
|
53
|
-
this.setDefaultState();
|
|
14
|
+
name = "checkers_view.controller.accept";
|
|
54
15
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
(this.state.ai?.[turn] ?? this.shared.checkersAi?.[turn] ?? this.CONST.CHECKERS.AI_TYPES.PLAYER) as CheckersAiType;
|
|
60
|
-
|
|
61
|
-
isHumanTurn = () => {
|
|
62
|
-
const turn = this.state.turn;
|
|
63
|
-
if (!turn) return false;
|
|
64
|
-
return this.controllerFor(turn) === this.CONST.CHECKERS.AI_TYPES.PLAYER;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
onCellClick = async ({ row, column }: CheckersCellClickPayload = {}) => {
|
|
68
|
-
if (this.shared.checkersAiBusy || !this.isHumanTurn()) return;
|
|
69
|
-
if (row == null || column == null) return;
|
|
16
|
+
EVENT_SHOW = this.EVENTS.CHECKERS_VIEW.SHOW;
|
|
17
|
+
EVENT_HIDE = this.EVENTS.CHECKERS_VIEW.HIDE;
|
|
18
|
+
EVENT_ACCEPT = this.EVENTS.CHECKERS_VIEW.ACCEPT;
|
|
19
|
+
EVENT_STATE_UPDATE = this.EVENTS.CHECKERS_VIEW.STATE_UPDATE;
|
|
70
20
|
|
|
71
|
-
|
|
72
|
-
gridRow: row,
|
|
73
|
-
gridColumn: column,
|
|
74
|
-
} satisfies Partial<CheckersPluginState>);
|
|
75
|
-
await this.emit(this.EVENTS.CHECKERS.UPDATE_FAST);
|
|
76
|
-
await this.onAccept();
|
|
77
|
-
};
|
|
21
|
+
CONTROLS_ZINDEX = this.PARAMS.CHECKERS_VIEW.ZINDEX;
|
|
78
22
|
|
|
79
23
|
onAccept = async () => {
|
|
80
|
-
if (this.shared.
|
|
81
|
-
|
|
82
|
-
if (this.state.winner) {
|
|
83
|
-
await this.emit(this.EVENTS.CHECKERS.FINISH);
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (!this.isHumanTurn()) return;
|
|
88
|
-
|
|
89
|
-
const { gridRow, gridColumn, board, turn, selected } = this.state;
|
|
90
|
-
if (gridRow == null || gridColumn == null || !board || !turn) return;
|
|
91
|
-
|
|
92
|
-
const focus: CheckersPos = { row: gridRow, column: gridColumn };
|
|
93
|
-
const piece = board[focus.row]?.[focus.column] ?? null;
|
|
94
|
-
|
|
95
|
-
if (selected && findMove(this.state.legalMoves ?? [], selected, focus)) {
|
|
96
|
-
await this.playMove(selected, focus);
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (piece && piece.color === turn) {
|
|
101
|
-
const all = getAllMoves(board, turn);
|
|
102
|
-
const legalMoves = all.filter((move) => samePos(move.from, focus));
|
|
103
|
-
if (legalMoves.length === 0) return;
|
|
104
|
-
|
|
105
|
-
const nextSelected = selected && samePos(selected, focus) ? null : focus;
|
|
106
|
-
const nextLegal = nextSelected ? legalMoves : [];
|
|
107
|
-
|
|
108
|
-
await this.emitBoardState({
|
|
109
|
-
selected: nextSelected,
|
|
110
|
-
legalMoves: nextLegal,
|
|
111
|
-
});
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (selected) {
|
|
116
|
-
await this.emitBoardState({ selected: null, legalMoves: [] });
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
onAiTurn = async () => {
|
|
121
|
-
if (this.state.winner || !this.state.board || !this.state.turn) return;
|
|
122
|
-
if (this.isHumanTurn()) return;
|
|
123
|
-
if (this.shared.checkersAiBusy) return;
|
|
124
|
-
|
|
125
|
-
this.shared.checkersAiBusy = true;
|
|
126
|
-
|
|
127
|
-
try {
|
|
128
|
-
while (!this.state.winner && this.state.board && this.state.turn && !this.isHumanTurn()) {
|
|
129
|
-
await this.waitTimeout(this.PARAMS.CHECKERS.AI_DELAY);
|
|
130
|
-
|
|
131
|
-
const { board, turn } = this.state;
|
|
132
|
-
if (!board || !turn || this.state.winner) break;
|
|
133
|
-
|
|
134
|
-
const move = pickMove(board, turn, this.controllerFor(turn));
|
|
135
|
-
if (!move) break;
|
|
136
|
-
|
|
137
|
-
const played = await this.playMove(move.from, move.to, { scheduleAi: false });
|
|
138
|
-
if (!played) break;
|
|
139
|
-
}
|
|
140
|
-
} finally {
|
|
141
|
-
this.shared.checkersAiBusy = false;
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
playMove = async (from: CheckersPos, to: CheckersPos, { scheduleAi = true }: { scheduleAi?: boolean } = {}) => {
|
|
146
|
-
const { board, turn } = this.state;
|
|
147
|
-
if (!board || !turn) return false;
|
|
148
|
-
|
|
149
|
-
// Always resolve against current legal moves — `legalMoves: []` after a move is truthy and must not win over getAllMoves.
|
|
150
|
-
const move = findMove(getAllMoves(board, turn), from, to);
|
|
151
|
-
if (!move) return false;
|
|
152
|
-
|
|
153
|
-
const nextBoard = applyMove(board, move);
|
|
154
|
-
const nextTurn = oppositeColor(turn);
|
|
155
|
-
const winner = getWinner(nextBoard, nextTurn);
|
|
156
|
-
|
|
157
|
-
await this.emitBoardState({
|
|
158
|
-
board: nextBoard,
|
|
159
|
-
turn: nextTurn,
|
|
160
|
-
selected: null,
|
|
161
|
-
legalMoves: [],
|
|
162
|
-
winner,
|
|
163
|
-
statusText: statusForTurn(nextTurn, winner),
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
if (winner) {
|
|
167
|
-
await this.waitTimeout(this.PARAMS.CHECKERS.TRANSITION);
|
|
168
|
-
await this.emit(this.EVENTS.CHECKERS.FINISH);
|
|
169
|
-
return true;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (scheduleAi) await this.onAiTurn();
|
|
173
|
-
return true;
|
|
174
|
-
};
|
|
24
|
+
if (this.shared.checkersInteractDisableSources.isNotEmpty()) return;
|
|
175
25
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
board?: CheckersPluginState["board"];
|
|
179
|
-
selected?: CheckersPluginState["selected"];
|
|
180
|
-
legalMoves?: CheckersPluginState["legalMoves"];
|
|
181
|
-
},
|
|
182
|
-
) => {
|
|
183
|
-
const board = patch.board ?? this.state.board;
|
|
184
|
-
if (!board) return;
|
|
26
|
+
const { gridRow, gridColumn } = this.state;
|
|
27
|
+
if (gridRow == null || gridColumn == null) return;
|
|
185
28
|
|
|
186
|
-
|
|
187
|
-
const legalMoves = patch.legalMoves !== undefined ? patch.legalMoves : this.state.legalMoves;
|
|
188
|
-
const gridItems = buildGridItems({
|
|
189
|
-
board,
|
|
190
|
-
selected: selected ?? null,
|
|
191
|
-
legalMoves: legalMoves ?? [],
|
|
192
|
-
gridRow: this.state.gridRow ?? null,
|
|
193
|
-
gridColumn: this.state.gridColumn ?? null,
|
|
194
|
-
});
|
|
29
|
+
await this.emit(this.EVENTS.CHECKERS_VIEW.GRID_CLEAR);
|
|
195
30
|
|
|
196
|
-
|
|
197
|
-
...patch,
|
|
198
|
-
gridItems,
|
|
199
|
-
} satisfies Partial<CheckersPluginState>);
|
|
200
|
-
await this.emit(this.EVENTS.CHECKERS.UPDATE_FAST);
|
|
31
|
+
this.emit(this.EVENTS.CHECKERS_VIEW.PLAYER_INTERACT, { row: gridRow, column: gridColumn } satisfies CheckersPlayerInteractPayload);
|
|
201
32
|
};
|
|
202
33
|
}
|