@vnejs/plugins.views.screens.checkers 0.2.1 → 0.2.3

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.
Files changed (53) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +21 -12
  3. package/dist/modules/board.d.ts +9 -0
  4. package/dist/modules/board.js +16 -0
  5. package/dist/modules/bridge.d.ts +8 -0
  6. package/dist/modules/bridge.js +10 -0
  7. package/dist/modules/click.d.ts +8 -0
  8. package/dist/modules/click.js +14 -0
  9. package/dist/modules/controller.accept.d.ts +7 -25
  10. package/dist/modules/controller.accept.js +12 -159
  11. package/dist/modules/controller.d.ts +4 -6
  12. package/dist/modules/controller.grid.d.ts +11 -11
  13. package/dist/modules/controller.grid.js +21 -25
  14. package/dist/modules/controller.js +8 -26
  15. package/dist/modules/player.interact.d.ts +9 -0
  16. package/dist/modules/player.interact.js +33 -0
  17. package/dist/modules/player.select.d.ts +9 -0
  18. package/dist/modules/player.select.js +29 -0
  19. package/dist/modules/view.d.ts +5 -5
  20. package/dist/modules/view.js +4 -4
  21. package/dist/types.d.ts +7 -5
  22. package/dist/utils/checkers.d.ts +19 -9
  23. package/dist/utils/checkers.js +30 -16
  24. package/dist/view/index.d.ts +2 -2
  25. package/dist/view/index.js +12 -12
  26. package/package.json +8 -2
  27. package/src/index.ts +21 -12
  28. package/src/modules/board.ts +29 -0
  29. package/src/modules/bridge.ts +20 -0
  30. package/src/modules/click.ts +28 -0
  31. package/src/modules/controller.accept.ts +20 -189
  32. package/src/modules/controller.grid.ts +23 -26
  33. package/src/modules/controller.ts +16 -36
  34. package/src/modules/player.interact.ts +48 -0
  35. package/src/modules/player.select.ts +42 -0
  36. package/src/modules/view.ts +12 -6
  37. package/src/tests/player.interact.test.ts +56 -0
  38. package/src/tests/setup.ts +21 -10
  39. package/src/types.ts +19 -6
  40. package/src/utils/checkers.ts +51 -25
  41. package/src/view/index.tsx +22 -23
  42. package/dist/modules/checkers.d.ts +0 -9
  43. package/dist/modules/checkers.js +0 -37
  44. package/dist/utils/ai.d.ts +0 -4
  45. package/dist/utils/ai.js +0 -27
  46. package/dist/utils/board.d.ts +0 -32
  47. package/dist/utils/board.js +0 -199
  48. package/src/modules/checkers.ts +0 -52
  49. package/src/tests/ai.test.ts +0 -61
  50. package/src/tests/board.test.ts +0 -43
  51. package/src/tests/checkers.test.ts +0 -58
  52. package/src/utils/ai.ts +0 -31
  53. package/src/utils/board.ts +0 -252
@@ -1,4 +1,14 @@
1
- import type { CheckersBoard, CheckersColor, CheckersMove, CheckersPiece, CheckersPos } from "./board.js";
1
+ import type { CheckersBoardChangedPayload, CheckersMovePayload, CheckersPosPayload } from "@vnejs/contracts.game.checkers";
2
+ export declare const BOARD_SIZE = 8;
3
+ export type CheckersColor = "white" | "black";
4
+ export type CheckersPiece = {
5
+ id: string;
6
+ color: CheckersColor;
7
+ king: boolean;
8
+ };
9
+ export type CheckersPos = CheckersPosPayload;
10
+ export type CheckersMove = CheckersMovePayload;
11
+ export type CheckersBoard = (CheckersPiece | null)[][];
2
12
  export type CheckersGridItem = {
3
13
  row: number;
4
14
  column: number;
@@ -8,7 +18,7 @@ export type CheckersGridItem = {
8
18
  isLegalTarget?: boolean;
9
19
  isFocus?: boolean;
10
20
  };
11
- export type CheckersPluginState = {
21
+ export type CheckersViewState = {
12
22
  isShow: boolean;
13
23
  isForce: boolean;
14
24
  board?: CheckersBoard;
@@ -22,16 +32,13 @@ export type CheckersPluginState = {
22
32
  gridItems?: CheckersGridItem[];
23
33
  gridIndex?: number;
24
34
  gridsCount?: number;
25
- ai?: {
26
- white: string;
27
- black: string;
28
- };
29
35
  };
30
- export type CheckersCellClickPayload = {
36
+ export type CheckersClickPayload = {
31
37
  row?: number;
32
38
  column?: number;
33
39
  };
34
- export declare const statusForTurn: (turn: CheckersColor, winner?: CheckersColor | null) => "Black to move" | "Black wins" | "White to move" | "White wins";
40
+ export declare const isDarkSquare: (row: number, column: number) => boolean;
41
+ export declare const samePos: (a: CheckersPos, b: CheckersPos) => boolean;
35
42
  export declare const buildGridItems: ({ board, selected, legalMoves, gridRow, gridColumn, }: {
36
43
  board: CheckersBoard;
37
44
  selected?: CheckersPos | null;
@@ -39,4 +46,7 @@ export declare const buildGridItems: ({ board, selected, legalMoves, gridRow, gr
39
46
  gridRow?: number | null;
40
47
  gridColumn?: number | null;
41
48
  }) => CheckersGridItem[];
42
- export declare const createInitialCheckersState: () => Pick<CheckersPluginState, "board" | "turn" | "selected" | "legalMoves" | "winner" | "statusText" | "gridItems" | "gridIndex" | "gridsCount">;
49
+ export declare const listBoardPieces: (board: CheckersBoard) => (CheckersPiece & CheckersPosPayload)[];
50
+ export declare const emptyBoard: () => CheckersBoard;
51
+ export declare const statusForTurn: (turn?: CheckersColor, winner?: CheckersColor | null) => "" | "Black to move" | "Black wins" | "White to move" | "White wins";
52
+ export declare const fromBoardChanged: (payload?: CheckersBoardChangedPayload) => Partial<CheckersViewState>;
@@ -1,9 +1,6 @@
1
- import { BOARD_SIZE, createInitialBoard, getAllMoves, isDarkSquare } from "./board.js";
2
- export const statusForTurn = (turn, winner = null) => {
3
- if (winner)
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,36 @@ export const buildGridItems = ({ board, selected = null, legalMoves = [], gridRo
22
19
  }
23
20
  return items;
24
21
  };
25
- export const createInitialCheckersState = () => {
26
- const board = createInitialBoard();
27
- const turn = "white";
28
- const legalMoves = getAllMoves(board, turn);
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";
41
+ };
42
+ export const fromBoardChanged = (payload = {}) => {
43
+ const turn = payload.turn;
44
+ const winner = (payload.winner ?? null);
29
45
  return {
30
- board,
46
+ board: payload.board,
31
47
  turn,
48
+ winner,
49
+ statusText: statusForTurn(turn, winner),
50
+ // selection is view-only — reset on every game model change
32
51
  selected: null,
33
52
  legalMoves: [],
34
- winner: null,
35
- statusText: statusForTurn(turn),
36
- gridItems: buildGridItems({ board, legalMoves: [] }),
37
- gridIndex: 0,
38
- gridsCount: 1,
39
53
  };
40
54
  };
@@ -1,3 +1,3 @@
1
1
  import type { ViewRenderFunc } from "@vnejs/module.components";
2
- import type { CheckersPluginState } from "../utils/checkers.js";
3
- export declare const render: ViewRenderFunc<CheckersPluginState>;
2
+ import type { CheckersViewState } from "../utils/checkers.js";
3
+ export declare const render: ViewRenderFunc<CheckersViewState>;
@@ -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/board.js";
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.CHECKERS.TRANSITION;
41
- const propsByView = PARAMS.CHECKERS.VIEW_PROPS;
42
- const rows = PARAMS.CHECKERS.ROWS;
43
- const columns = PARAMS.CHECKERS.COLUMNS;
44
- const cellSize = PARAMS.CHECKERS.CELL_SIZE;
45
- const cellGap = PARAMS.CHECKERS.CELL_GAP;
46
- const graveyardGap = PARAMS.CHECKERS.GRAVEYARD_GAP;
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 onCellClick = useCallback((row, column) => {
173
- void emit(EVENTS.CHECKERS.CELL_CLICK, { row, column });
174
- }, [emit, EVENTS.CHECKERS.CELL_CLICK]);
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: () => onCellClick(item.row, item.column), "vne-cursor-type": "pointer", style: {
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.1",
4
- "description": "",
3
+ "version": "0.2.3",
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,8 @@
30
30
  "author": "",
31
31
  "license": "ISC",
32
32
  "dependencies": {
33
+ "@vnejs/contracts.ai": "~0.2.0",
34
+ "@vnejs/contracts.game.checkers": "~0.2.0",
33
35
  "@vnejs/contracts.views.screens.checkers": "~0.2.0"
34
36
  },
35
37
  "peerDependencies": {
@@ -40,13 +42,16 @@
40
42
  "@vnejs/module.controller.arrows.grid": "~0.2.0",
41
43
  "@vnejs/contracts.controls": "~0.2.0",
42
44
  "@vnejs/shared": "~0.2.0",
45
+ "@vnejs/sources-set": "~0.2.0",
43
46
  "@vnejs/uis.react": "~0.2.0"
44
47
  },
45
48
  "devDependencies": {
46
49
  "@vnejs/configs.ts-common": "~0.2.0",
47
50
  "@vnejs/configs.vitest": "~0.2.0",
48
51
  "@vnejs/test-utils": "~0.2.0",
52
+ "@vnejs/contracts.ai": "~0.2.0",
49
53
  "@vnejs/contracts.controls": "~0.2.0",
54
+ "@vnejs/contracts.game.checkers": "~0.2.0",
50
55
  "@vnejs/contracts.views.screens.checkers": "~0.2.0",
51
56
  "@vnejs/helpers": "~0.2.0",
52
57
  "@vnejs/module.core": "~0.2.0",
@@ -54,6 +59,7 @@
54
59
  "@vnejs/module.controller.accept": "~0.2.0",
55
60
  "@vnejs/module.controller.arrows.grid": "~0.2.0",
56
61
  "@vnejs/shared": "~0.2.0",
62
+ "@vnejs/sources-set": "~0.2.0",
57
63
  "@vnejs/uis.react": "~0.2.0"
58
64
  }
59
65
  }
package/src/index.ts CHANGED
@@ -1,18 +1,27 @@
1
+ import "@vnejs/contracts.game.checkers";
1
2
  import "@vnejs/contracts.views.screens.checkers";
2
3
 
3
4
  import { regPlugin } from "@vnejs/shared";
4
- import { CONSTANTS, PARAMS, PLUGIN_NAME, SUBSCRIBE_EVENTS } from "@vnejs/contracts.views.screens.checkers";
5
+ import { PARAMS, PLUGIN_NAME, SUBSCRIBE_EVENTS } from "@vnejs/contracts.views.screens.checkers";
5
6
 
6
- import { CheckersController } from "./modules/controller.js";
7
- import { CheckersControllerAccept } from "./modules/controller.accept.js";
8
- import { CheckersControllerGrid } from "./modules/controller.grid.js";
9
- import { Checkers } from "./modules/checkers.js";
10
- import { CheckersView } from "./modules/view.js";
7
+ import { CheckersViewBoard } from "./modules/board.js";
8
+ import { CheckersViewBridge } from "./modules/bridge.js";
9
+ import { CheckersViewClick } from "./modules/click.js";
10
+ import { CheckersViewController } from "./modules/controller.js";
11
+ import { CheckersViewControllerAccept } from "./modules/controller.accept.js";
12
+ import { CheckersViewControllerGrid } from "./modules/controller.grid.js";
13
+ import { CheckersViewPlayerInteract } from "./modules/player.interact.js";
14
+ import { CheckersViewPlayerSelect } from "./modules/player.select.js";
15
+ import { CheckersViewView } from "./modules/view.js";
11
16
 
12
- regPlugin(PLUGIN_NAME, { constants: CONSTANTS, events: SUBSCRIBE_EVENTS, params: PARAMS }, [
13
- CheckersController,
14
- CheckersControllerGrid,
15
- CheckersControllerAccept,
16
- Checkers,
17
- CheckersView,
17
+ regPlugin(PLUGIN_NAME, { events: SUBSCRIBE_EVENTS, params: PARAMS }, [
18
+ CheckersViewBridge,
19
+ CheckersViewController,
20
+ CheckersViewControllerGrid,
21
+ CheckersViewControllerAccept,
22
+ CheckersViewPlayerInteract,
23
+ CheckersViewPlayerSelect,
24
+ CheckersViewClick,
25
+ CheckersViewBoard,
26
+ CheckersViewView,
18
27
  ]);
@@ -0,0 +1,29 @@
1
+ import { ModuleCore } from "@vnejs/module.core";
2
+ import type { CheckersBoardChangedPayload } from "@vnejs/contracts.game.checkers";
3
+
4
+ import type { CheckersViewPluginConstants, CheckersViewPluginEvents, CheckersViewPluginParams, CheckersViewPluginSettings } from "../types.js";
5
+ import { buildGridItems, emptyBoard, fromBoardChanged, 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.CHECKERS.BOARD_CHANGED, this.onBoardChanged);
18
+ this.on(this.EVENTS.CHECKERS_VIEW.STATE_UPDATE, this.updateState);
19
+ };
20
+
21
+ onBoardChanged = async (payload: CheckersBoardChangedPayload = {}) => {
22
+ const mirror = fromBoardChanged(payload);
23
+ const board = mirror.board ?? this.state.board ?? emptyBoard();
24
+ const gridItems = buildGridItems({ board, gridRow: this.state.gridRow ?? null, gridColumn: this.state.gridColumn ?? null });
25
+
26
+ await this.emit(this.EVENTS.CHECKERS_VIEW.STATE_UPDATE, { ...mirror, board, gridItems } satisfies Partial<CheckersViewState>);
27
+ await this.emit(this.EVENTS.CHECKERS_VIEW.UPDATE_FAST);
28
+ };
29
+ }
@@ -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 { ControlsPopPayload, ControlsPushPayload } from "@vnejs/contracts.controls";
2
+ import type { CheckersPlayerInteractPayload } from "@vnejs/contracts.views.screens.checkers";
3
3
 
4
- import type { CheckersAiType } from "@vnejs/contracts.views.screens.checkers";
4
+ import type { CheckersViewPluginConstants, CheckersViewPluginEvents, CheckersViewPluginParams, CheckersViewPluginSettings } from "../types.js";
5
+ import type { CheckersViewState } from "../utils/checkers.js";
5
6
 
6
- import type { CheckersPluginConstants, CheckersPluginEvents, CheckersPluginParams, CheckersPluginSettings } from "../types.js";
7
- import { applyMove, findMove, getAllMoves, getWinner, oppositeColor, samePos, type CheckersPos } from "../utils/board.js";
8
- import { pickMove } from "../utils/ai.js";
9
- import { buildGridItems, statusForTurn, type CheckersCellClickPayload, type CheckersPluginState } from "../utils/checkers.js";
10
-
11
- export class CheckersControllerAccept extends ModuleControllerAccept<
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 = "checkers.controller.accept";
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
- return this.emit(this.EVENTS.CONTROLS.POP, { key: this.name } satisfies ControlsPopPayload);
56
- };
57
-
58
- controllerFor = (turn: NonNullable<CheckersPluginState["turn"]>) =>
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
- await this.emit(this.EVENTS.CHECKERS.STATE_UPDATE, {
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.checkersAiBusy) return;
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
- emitBoardState = async (
177
- patch: Partial<CheckersPluginState> & {
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
- const selected = patch.selected !== undefined ? patch.selected : this.state.selected;
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
- await this.emit(this.EVENTS.CHECKERS.STATE_UPDATE, {
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
  }
@@ -1,43 +1,40 @@
1
1
  import { ModuleControllerArrowsGrid } from "@vnejs/module.controller.arrows.grid";
2
2
 
3
- import type { CheckersPluginConstants, CheckersPluginEvents, CheckersPluginParams, CheckersPluginSettings } from "../types.js";
4
- import { buildGridItems, type CheckersPluginState } from "../utils/checkers.js";
5
- import { createInitialBoard } from "../utils/board.js";
3
+ import type { CheckersViewPluginConstants, CheckersViewPluginEvents, CheckersViewPluginParams, CheckersViewPluginSettings } from "../types.js";
4
+ import { buildGridItems, emptyBoard, type CheckersViewState } from "../utils/checkers.js";
6
5
 
7
- export class CheckersControllerGrid extends ModuleControllerArrowsGrid<
8
- CheckersPluginEvents,
9
- CheckersPluginConstants,
10
- CheckersPluginSettings,
11
- CheckersPluginParams,
12
- CheckersPluginState
6
+ export class CheckersViewControllerGrid extends ModuleControllerArrowsGrid<
7
+ CheckersViewPluginEvents,
8
+ CheckersViewPluginConstants,
9
+ CheckersViewPluginSettings,
10
+ CheckersViewPluginParams,
11
+ CheckersViewState
13
12
  > {
14
- name = "checkers.controller.grid";
13
+ name = "checkers_view.controller.grid";
15
14
 
16
- EVENT_SHOW = this.EVENTS.CHECKERS.SHOW;
17
- EVENT_HIDE = this.EVENTS.CHECKERS.HIDE;
18
- EVENT_STATE_UPDATE = this.EVENTS.CHECKERS.STATE_UPDATE;
19
- EVENT_UPDATE_FAST = this.EVENTS.CHECKERS.UPDATE_FAST;
20
- EVENT_GRID_NEXT = this.EVENTS.CHECKERS.GRID_NEXT;
21
- EVENT_GRID_PREV = this.EVENTS.CHECKERS.GRID_PREV;
22
- EVENT_GRID_CLEAR = this.EVENTS.CHECKERS.GRID_CLEAR;
23
- EVENT_GRID_UPDATE_ITEMS = this.EVENTS.CHECKERS.GRID_UPDATE_ITEMS;
15
+ EVENT_SHOW = this.EVENTS.CHECKERS_VIEW.SHOW;
16
+ EVENT_HIDE = this.EVENTS.CHECKERS_VIEW.HIDE;
17
+ EVENT_STATE_UPDATE = this.EVENTS.CHECKERS_VIEW.STATE_UPDATE;
18
+ EVENT_UPDATE_FAST = this.EVENTS.CHECKERS_VIEW.UPDATE_FAST;
19
+ EVENT_GRID_NEXT = this.EVENTS.CHECKERS_VIEW.GRID_NEXT;
20
+ EVENT_GRID_PREV = this.EVENTS.CHECKERS_VIEW.GRID_PREV;
21
+ EVENT_GRID_CLEAR = this.EVENTS.CHECKERS_VIEW.GRID_CLEAR;
22
+ EVENT_GRID_UPDATE_ITEMS = this.EVENTS.CHECKERS_VIEW.GRID_UPDATE_ITEMS;
24
23
 
25
- CONTROLS_ZINDEX = this.PARAMS.CHECKERS.ZINDEX;
24
+ CONTROLS_ZINDEX = this.PARAMS.CHECKERS_VIEW.ZINDEX;
26
25
 
27
26
  isDisableGridChangeHorizontal = true;
28
27
  isDisableGridChangeVertical = true;
29
28
 
30
- getMaxRows = () => this.PARAMS.CHECKERS.ROWS;
31
- getMaxColumns = () => this.PARAMS.CHECKERS.COLUMNS;
29
+ getMaxRows = () => this.PARAMS.CHECKERS_VIEW.ROWS;
30
+ getMaxColumns = () => this.PARAMS.CHECKERS_VIEW.COLUMNS;
32
31
 
33
- getGridItems = async () => {
34
- const board = this.state.board ?? createInitialBoard();
35
- return buildGridItems({
36
- board,
32
+ getGridItems = async () =>
33
+ buildGridItems({
34
+ board: this.state.board ?? emptyBoard(),
37
35
  selected: this.state.selected ?? null,
38
36
  legalMoves: this.state.legalMoves ?? [],
39
37
  gridRow: this.state.gridRow ?? null,
40
38
  gridColumn: this.state.gridColumn ?? null,
41
39
  });
42
- };
43
40
  }