@dxos/react-ui-gameboard 0.8.4-main.5acf9ea → 0.8.4-main.5ea62a8
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/lib/browser/index.mjs +407 -322
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +407 -322
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/Chessboard/Chessboard.d.ts.map +1 -0
- package/dist/types/src/components/Chessboard/Chessboard.stories.d.ts +22 -0
- package/dist/types/src/components/Chessboard/Chessboard.stories.d.ts.map +1 -0
- package/dist/types/src/{Chessboard → components/Chessboard}/chess.d.ts +27 -10
- package/dist/types/src/components/Chessboard/chess.d.ts.map +1 -0
- package/dist/types/src/components/Chessboard/chess.test.d.ts +2 -0
- package/dist/types/src/components/Chessboard/chess.test.d.ts.map +1 -0
- package/dist/types/src/components/Chessboard/index.d.ts.map +1 -0
- package/dist/types/src/components/Gameboard/Gameboard.d.ts +38 -0
- package/dist/types/src/components/Gameboard/Gameboard.d.ts.map +1 -0
- package/dist/types/src/{Gameboard → components/Gameboard}/Piece.d.ts +3 -2
- package/dist/types/src/components/Gameboard/Piece.d.ts.map +1 -0
- package/dist/types/src/components/Gameboard/Square.d.ts.map +1 -0
- package/dist/types/src/components/Gameboard/index.d.ts +4 -0
- package/dist/types/src/components/Gameboard/index.d.ts.map +1 -0
- package/dist/types/src/{Gameboard → components/Gameboard}/types.d.ts +2 -0
- package/dist/types/src/components/Gameboard/types.d.ts.map +1 -0
- package/dist/types/src/components/Gameboard/util.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +3 -0
- package/dist/types/src/components/index.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +1 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -15
- package/src/{Chessboard → components/Chessboard}/Chessboard.stories.tsx +24 -23
- package/src/{Chessboard → components/Chessboard}/Chessboard.tsx +43 -41
- package/src/components/Chessboard/chess.test.ts +19 -0
- package/src/components/Chessboard/chess.ts +314 -0
- package/src/components/Gameboard/Gameboard.tsx +140 -0
- package/src/{Gameboard → components/Gameboard}/Piece.tsx +25 -22
- package/src/{Gameboard → components/Gameboard}/Square.tsx +4 -4
- package/src/{Gameboard → components/Gameboard}/index.ts +0 -3
- package/src/{Gameboard → components/Gameboard}/types.ts +3 -0
- package/src/components/index.ts +6 -0
- package/src/index.ts +1 -2
- package/dist/types/src/Chessboard/Chessboard.d.ts.map +0 -1
- package/dist/types/src/Chessboard/Chessboard.stories.d.ts +0 -16
- package/dist/types/src/Chessboard/Chessboard.stories.d.ts.map +0 -1
- package/dist/types/src/Chessboard/chess.d.ts.map +0 -1
- package/dist/types/src/Chessboard/index.d.ts.map +0 -1
- package/dist/types/src/Gameboard/Gameboard.d.ts +0 -23
- package/dist/types/src/Gameboard/Gameboard.d.ts.map +0 -1
- package/dist/types/src/Gameboard/Piece.d.ts.map +0 -1
- package/dist/types/src/Gameboard/Square.d.ts.map +0 -1
- package/dist/types/src/Gameboard/context.d.ts +0 -10
- package/dist/types/src/Gameboard/context.d.ts.map +0 -1
- package/dist/types/src/Gameboard/index.d.ts +0 -7
- package/dist/types/src/Gameboard/index.d.ts.map +0 -1
- package/dist/types/src/Gameboard/types.d.ts.map +0 -1
- package/dist/types/src/Gameboard/util.d.ts.map +0 -1
- package/src/Chessboard/chess.ts +0 -213
- package/src/Gameboard/Gameboard.tsx +0 -103
- package/src/Gameboard/context.ts +0 -22
- /package/dist/types/src/{Chessboard → components/Chessboard}/Chessboard.d.ts +0 -0
- /package/dist/types/src/{Chessboard → components/Chessboard}/index.d.ts +0 -0
- /package/dist/types/src/{Gameboard → components/Gameboard}/Square.d.ts +0 -0
- /package/dist/types/src/{Gameboard → components/Gameboard}/util.d.ts +0 -0
- /package/src/{Chessboard → components/Chessboard}/index.ts +0 -0
- /package/src/{Gameboard → components/Gameboard}/util.ts +0 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
6
|
+
import { createContext } from '@radix-ui/react-context';
|
|
7
|
+
import React, { type PropsWithChildren, forwardRef, useCallback, useEffect, useState } from 'react';
|
|
8
|
+
|
|
9
|
+
import { log } from '@dxos/log';
|
|
10
|
+
import { type ThemedClassName } from '@dxos/react-ui';
|
|
11
|
+
import { mx } from '@dxos/react-ui-theme';
|
|
12
|
+
|
|
13
|
+
import { Piece, type PieceProps } from './Piece';
|
|
14
|
+
import { Square, type SquareProps } from './Square';
|
|
15
|
+
import { type GameboardModel, type Move, type PieceRecord, isLocation, isPiece } from './types';
|
|
16
|
+
|
|
17
|
+
export type GameboardContextValue<M extends GameboardModel> = {
|
|
18
|
+
model: M;
|
|
19
|
+
dragging?: boolean; // TODO(burdon): Change to PieceRecord.
|
|
20
|
+
promoting?: PieceRecord;
|
|
21
|
+
onPromotion: (move: Move) => void;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const [GameboardContextProvider, useRadixGameboardContext] = createContext<GameboardContextValue<any>>('Gameboard');
|
|
25
|
+
|
|
26
|
+
const useGameboardContext = <M extends GameboardModel>(consumerName: string): GameboardContextValue<M> => {
|
|
27
|
+
return useRadixGameboardContext(consumerName);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
//
|
|
31
|
+
// Root
|
|
32
|
+
//
|
|
33
|
+
|
|
34
|
+
type GameboardRootProps<M extends GameboardModel> = PropsWithChildren<{
|
|
35
|
+
model?: M;
|
|
36
|
+
moveNumber?: number;
|
|
37
|
+
onDrop?: (move: Move) => boolean;
|
|
38
|
+
}>;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Generic board container.
|
|
42
|
+
*/
|
|
43
|
+
const GameboardRoot = <M extends GameboardModel>({ children, model, moveNumber, onDrop }: GameboardRootProps<M>) => {
|
|
44
|
+
const [dragging, setDragging] = useState(false);
|
|
45
|
+
const [promoting, setPromoting] = useState<PieceRecord | undefined>();
|
|
46
|
+
|
|
47
|
+
const handlePromotion = useCallback<GameboardContextValue<M>['onPromotion']>((move) => {
|
|
48
|
+
log('onPromotion', { move });
|
|
49
|
+
setPromoting(undefined);
|
|
50
|
+
onDrop?.(move);
|
|
51
|
+
}, []);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (!model) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// TODO(burdon): Should target specific container.
|
|
59
|
+
return monitorForElements({
|
|
60
|
+
onDragStart: ({ source }) => {
|
|
61
|
+
log('onDragStart', { source });
|
|
62
|
+
setDragging(true);
|
|
63
|
+
},
|
|
64
|
+
onDrop: ({ source, location }) => {
|
|
65
|
+
log('onDrop', { source, location });
|
|
66
|
+
const target = location.current.dropTargets[0];
|
|
67
|
+
if (!target) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const targetLocation = target.data.location;
|
|
72
|
+
const piece = source.data.piece;
|
|
73
|
+
if (!isLocation(targetLocation) || !isPiece(piece)) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const move: Move = { from: piece.location, to: targetLocation, piece: piece.type };
|
|
78
|
+
if (model.isValidMove(move)) {
|
|
79
|
+
if (model.canPromote?.(move)) {
|
|
80
|
+
setPromoting({ ...piece, location: targetLocation });
|
|
81
|
+
} else {
|
|
82
|
+
onDrop?.(move);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
setDragging(false);
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
}, [model]);
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<GameboardContextProvider model={model} dragging={dragging} promoting={promoting} onPromotion={handlePromotion}>
|
|
93
|
+
{children}
|
|
94
|
+
</GameboardContextProvider>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
GameboardRoot.displayName = 'Gameboard.Root';
|
|
99
|
+
|
|
100
|
+
//
|
|
101
|
+
// Content
|
|
102
|
+
//
|
|
103
|
+
|
|
104
|
+
type GameboardContentProps = ThemedClassName<PropsWithChildren<{ grow?: boolean; contain?: boolean }>>;
|
|
105
|
+
|
|
106
|
+
const GameboardContent = forwardRef<HTMLDivElement, GameboardContentProps>(
|
|
107
|
+
({ children, classNames, grow, contain }, forwardedRef) => {
|
|
108
|
+
return (
|
|
109
|
+
<div
|
|
110
|
+
role='none'
|
|
111
|
+
className={mx(grow && 'grid is-full bs-full size-container place-content-center', classNames)}
|
|
112
|
+
ref={forwardedRef}
|
|
113
|
+
>
|
|
114
|
+
{contain ? <div className='is-[min(100cqw,100cqh)] bs-[min(100cqw,100cqh)]'>{children}</div> : children}
|
|
115
|
+
</div>
|
|
116
|
+
);
|
|
117
|
+
},
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
GameboardContent.displayName = 'Gameboard.Content';
|
|
121
|
+
|
|
122
|
+
//
|
|
123
|
+
// Gameboard
|
|
124
|
+
//
|
|
125
|
+
|
|
126
|
+
export const Gameboard = {
|
|
127
|
+
Root: GameboardRoot,
|
|
128
|
+
Content: GameboardContent,
|
|
129
|
+
Piece,
|
|
130
|
+
Square,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export { useGameboardContext };
|
|
134
|
+
|
|
135
|
+
export type {
|
|
136
|
+
GameboardRootProps,
|
|
137
|
+
GameboardContentProps,
|
|
138
|
+
PieceProps as GameboardPieceProps,
|
|
139
|
+
SquareProps as GameboardSquareProps,
|
|
140
|
+
};
|
|
@@ -3,34 +3,32 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
6
|
-
// import { preserveOffsetOnSource } from '@atlaskit/pragmatic-drag-and-drop/element/preserve-offset-on-source';
|
|
7
6
|
import { centerUnderPointer } from '@atlaskit/pragmatic-drag-and-drop/element/center-under-pointer';
|
|
8
7
|
import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview';
|
|
9
|
-
import React, {
|
|
8
|
+
import React, { type FC, type SVGProps, memo, useEffect, useRef, useState } from 'react';
|
|
10
9
|
import { createPortal } from 'react-dom';
|
|
11
10
|
|
|
12
11
|
import { invariant } from '@dxos/invariant';
|
|
13
12
|
import { log } from '@dxos/log';
|
|
14
|
-
import { useDynamicRef, useTrackProps
|
|
13
|
+
import { type ThemedClassName, useDynamicRef, useTrackProps } from '@dxos/react-ui';
|
|
15
14
|
import { mx } from '@dxos/react-ui-theme';
|
|
16
15
|
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
16
|
+
import { useGameboardContext } from './Gameboard';
|
|
17
|
+
import { type Location, type PieceRecord, type Player, isEqualLocation, isLocation } from './types';
|
|
19
18
|
import { type DOMRectBounds } from './util';
|
|
20
19
|
|
|
21
20
|
export type PieceProps = ThemedClassName<{
|
|
21
|
+
Component: FC<SVGProps<SVGSVGElement>>;
|
|
22
22
|
piece: PieceRecord;
|
|
23
23
|
bounds: DOMRectBounds;
|
|
24
24
|
label?: string;
|
|
25
25
|
orientation?: Player;
|
|
26
|
-
|
|
26
|
+
onClick?: () => void;
|
|
27
27
|
}>;
|
|
28
28
|
|
|
29
|
-
export const Piece = memo(({ classNames, piece, orientation, bounds, label,
|
|
30
|
-
useTrackProps({ classNames, piece, orientation, bounds, label
|
|
31
|
-
const { model } =
|
|
32
|
-
|
|
33
|
-
const { dragging: isDragging, promoting } = useBoardContext();
|
|
29
|
+
export const Piece = memo(({ classNames, Component, piece, orientation, bounds, label, onClick }: PieceProps) => {
|
|
30
|
+
useTrackProps({ classNames, Component, piece, orientation, bounds, label }, Piece.displayName, false);
|
|
31
|
+
const { model, dragging: isDragging, promoting } = useGameboardContext(Piece.displayName!);
|
|
34
32
|
const promotingRef = useDynamicRef(promoting);
|
|
35
33
|
const [dragging, setDragging] = useState(false);
|
|
36
34
|
const [preview, setPreview] = useState<HTMLElement>();
|
|
@@ -40,20 +38,20 @@ export const Piece = memo(({ classNames, piece, orientation, bounds, label, Comp
|
|
|
40
38
|
|
|
41
39
|
const ref = useRef<HTMLDivElement>(null);
|
|
42
40
|
useEffect(() => {
|
|
41
|
+
if (!model) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
43
45
|
const el = ref.current;
|
|
44
46
|
invariant(el);
|
|
45
47
|
|
|
46
48
|
return draggable({
|
|
47
49
|
element: el,
|
|
48
50
|
getInitialData: () => ({ piece }),
|
|
49
|
-
onGenerateDragPreview: ({ nativeSetDragImage,
|
|
51
|
+
onGenerateDragPreview: ({ nativeSetDragImage, source }) => {
|
|
50
52
|
log('onGenerateDragPreview', { source: source.data });
|
|
51
53
|
setCustomNativeDragPreview({
|
|
52
54
|
getOffset: centerUnderPointer,
|
|
53
|
-
// getOffset: preserveOffsetOnSource({
|
|
54
|
-
// element: source.element,
|
|
55
|
-
// input: location.current.input,
|
|
56
|
-
// }),
|
|
57
55
|
render: ({ container }) => {
|
|
58
56
|
setPreview(container);
|
|
59
57
|
const { width, height } = el.getBoundingClientRect();
|
|
@@ -66,12 +64,17 @@ export const Piece = memo(({ classNames, piece, orientation, bounds, label, Comp
|
|
|
66
64
|
nativeSetDragImage,
|
|
67
65
|
});
|
|
68
66
|
},
|
|
69
|
-
canDrag: () => !promotingRef.current && model
|
|
67
|
+
canDrag: () => !promotingRef.current && !model.readonly && model.turn === piece.side,
|
|
70
68
|
onDragStart: () => setDragging(true),
|
|
71
69
|
onDrop: ({ location: { current } }) => {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
// TODO(burdon): Create wrapper function to catch errors.
|
|
71
|
+
try {
|
|
72
|
+
const location = current.dropTargets[0]?.data.location;
|
|
73
|
+
if (isLocation(location)) {
|
|
74
|
+
setCurrent((current) => ({ ...current, location }));
|
|
75
|
+
}
|
|
76
|
+
} catch {
|
|
77
|
+
// Ignore.
|
|
75
78
|
}
|
|
76
79
|
|
|
77
80
|
setDragging(false);
|
|
@@ -88,7 +91,7 @@ export const Piece = memo(({ classNames, piece, orientation, bounds, label, Comp
|
|
|
88
91
|
|
|
89
92
|
// Check if piece moved.
|
|
90
93
|
if (!current.location || !isEqualLocation(current.location, piece.location)) {
|
|
91
|
-
ref.current.style.transition = 'top
|
|
94
|
+
ref.current.style.transition = 'top 250ms ease-out, left 250ms ease-out';
|
|
92
95
|
ref.current.style.top = bounds.top + 'px';
|
|
93
96
|
ref.current.style.left = bounds.left + 'px';
|
|
94
97
|
setCurrent({ location: piece.location, bounds });
|
|
@@ -112,10 +115,10 @@ export const Piece = memo(({ classNames, piece, orientation, bounds, label, Comp
|
|
|
112
115
|
className={mx(
|
|
113
116
|
'absolute',
|
|
114
117
|
classNames,
|
|
115
|
-
// orientation === 'black' && '_rotate-180',
|
|
116
118
|
dragging && 'opacity-20', // Must not unmount component while dragging.
|
|
117
119
|
isDragging && 'pointer-events-none', // Don't block the square's drop target.
|
|
118
120
|
)}
|
|
121
|
+
onClick={onClick}
|
|
119
122
|
>
|
|
120
123
|
<Component className='grow' />
|
|
121
124
|
{label && <div className='absolute inset-1 text-xs text-black'>{label}</div>}
|
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
6
|
-
import React, {
|
|
6
|
+
import React, { memo, useEffect, useRef, useState } from 'react';
|
|
7
7
|
|
|
8
8
|
import { invariant } from '@dxos/invariant';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
10
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
11
11
|
import { mx } from '@dxos/react-ui-theme';
|
|
12
12
|
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
13
|
+
import { useGameboardContext } from './Gameboard';
|
|
14
|
+
import { type Location, isPiece } from './types';
|
|
15
15
|
import { type DOMRectBounds } from './util';
|
|
16
16
|
|
|
17
17
|
type HoveredState = 'idle' | 'validMove' | 'invalidMove';
|
|
@@ -25,7 +25,7 @@ export type SquareProps = ThemedClassName<{
|
|
|
25
25
|
export const Square = memo(({ location, bounds, label, classNames }: SquareProps) => {
|
|
26
26
|
const ref = useRef<HTMLDivElement>(null);
|
|
27
27
|
const [state, setState] = useState<HoveredState>('idle');
|
|
28
|
-
const { model } =
|
|
28
|
+
const { model } = useGameboardContext(Square.displayName!);
|
|
29
29
|
|
|
30
30
|
useEffect(() => {
|
|
31
31
|
const el = ref.current;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { type ReadonlySignal } from '@preact/signals-core';
|
|
6
6
|
|
|
7
|
+
// TODO(burdon): Don't make this assumption.
|
|
7
8
|
export type Player = 'black' | 'white';
|
|
8
9
|
|
|
9
10
|
export type Location = [number, number];
|
|
@@ -51,7 +52,9 @@ export const isEqualLocation = (l1: Location, l2: Location): boolean => l1[0] ==
|
|
|
51
52
|
* Generic board model.
|
|
52
53
|
*/
|
|
53
54
|
export interface GameboardModel<T extends PieceType = PieceType> {
|
|
55
|
+
readonly: boolean;
|
|
54
56
|
turn: Player;
|
|
57
|
+
/** @reactive */
|
|
55
58
|
pieces: ReadonlySignal<PieceMap<T>>;
|
|
56
59
|
isValidMove: (move: Move) => boolean;
|
|
57
60
|
canPromote?: (move: Move) => boolean;
|
package/src/index.ts
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Chessboard.d.ts","sourceRoot":"","sources":["../../../../src/Chessboard/Chessboard.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAAE,KAAK,iBAAiB,EAA8C,MAAM,OAAO,CAAC;AAGlG,OAAO,EAAE,KAAK,eAAe,EAAiB,MAAM,gBAAgB,CAAC;AAKrE,OAAO,EAIL,KAAK,MAAM,EAMZ,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,eAAe,GAAG,eAAe,CAC3C,iBAAiB,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC,CACH,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,UAAU,yFACgD,eAAe,uBA0GrF,CAAC"}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import '@dxos-theme';
|
|
2
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { type ChessboardProps } from './Chessboard';
|
|
5
|
-
type RenderProps = Pick<ChessboardProps, 'orientation' | 'showLabels' | 'debug'> & {
|
|
6
|
-
fen: string;
|
|
7
|
-
};
|
|
8
|
-
declare const DefaultStory: ({ fen, orientation: _orientation, ...props }: RenderProps) => React.JSX.Element;
|
|
9
|
-
declare const meta: Meta<typeof DefaultStory>;
|
|
10
|
-
export default meta;
|
|
11
|
-
type Story = StoryObj<typeof DefaultStory>;
|
|
12
|
-
export declare const Default: Story;
|
|
13
|
-
export declare const Promotion: Story;
|
|
14
|
-
export declare const Debug: Story;
|
|
15
|
-
export declare const Nine: Story;
|
|
16
|
-
//# sourceMappingURL=Chessboard.stories.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Chessboard.stories.d.ts","sourceRoot":"","sources":["../../../../src/Chessboard/Chessboard.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAoD,MAAM,OAAO,CAAC;AAMzE,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAIhE,KAAK,WAAW,GAAG,IAAI,CAAC,eAAe,EAAE,aAAa,GAAG,YAAY,GAAG,OAAO,CAAC,GAAG;IACjF,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,QAAA,MAAM,YAAY,GAAI,8CAA8C,WAAW,sBA+B9E,CAAC;AA2BF,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,YAAY,CAKnC,CAAC;AAEF,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,YAAY,CAAC,CAAC;AAE3C,eAAO,MAAM,OAAO,EAAE,KAAU,CAAC;AAEjC,eAAO,MAAM,SAAS,EAAE,KAIvB,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,KAOnB,CAAC;AAEF,eAAO,MAAM,IAAI,EAAE,KAElB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chess.d.ts","sourceRoot":"","sources":["../../../../src/Chessboard/chess.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,cAAc,EAAU,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,KAAK,EAAe,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,QAAQ,EAAE,MAAM,OAAO,CAAC;AAI/C,OAAO,EACL,KAAK,IAAI,EACT,KAAK,QAAQ,EACb,KAAK,QAAQ,EAEb,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,MAAM,EACZ,MAAM,cAAc,CAAC;AAGtB,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3G,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAS,CAAC;AAElF,eAAO,MAAM,aAAa,GAAI,KAAK,MAAM,KAAG,QAI3C,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,YAAY,QAAQ,KAAG,MAEpD,CAAC;AAyBF,eAAO,MAAM,WAAW;;;;CAAkB,CAAC;AAE3C,eAAO,MAAM,cAAc,GAAI,YAAY,QAAQ,WAElD,CAAC;AAmBF;;GAEG;AACH,qBAAa,UAAW,YAAW,cAAc,CAAC,UAAU,CAAC;IAC3D,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;gBAEhD,GAAG,CAAC,EAAE,MAAM;IAIxB,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,MAAM,IAAI,cAAc,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAEjD;IAED,IAAI,IAAI,IAAI,KAAK,CAEhB;IAED,IAAI,GAAG,IAAI,MAAM,CAEhB;IAED,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI;IAM9B,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO;IAIhC,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO;IAM/B,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO;IAW7B,cAAc,IAAI,OAAO;IAYzB;;OAEG;IACH,OAAO,CAAC,OAAO;CAsBhB;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,SAAS,EAAE,QAAQ,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,QAAQ,CAAC,CAAC,CAAC,KAAG,QAAQ,CAAC,CAAC,CAmClG,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/Chessboard/index.ts"],"names":[],"mappings":"AAIA,cAAc,SAAS,CAAC;AAExB,cAAc,cAAc,CAAC"}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import React, { type CSSProperties, type PropsWithChildren } from 'react';
|
|
2
|
-
import { type ThemedClassName } from '@dxos/react-ui';
|
|
3
|
-
import { type GameboardModel, type Move } from './types';
|
|
4
|
-
type GameboardRootProps = PropsWithChildren<{
|
|
5
|
-
model?: GameboardModel;
|
|
6
|
-
onDrop?: (move: Move) => boolean;
|
|
7
|
-
}>;
|
|
8
|
-
type GameboardContentsProps = ThemedClassName<PropsWithChildren<{
|
|
9
|
-
style?: CSSProperties;
|
|
10
|
-
}>>;
|
|
11
|
-
export declare const Gameboard: {
|
|
12
|
-
Root: {
|
|
13
|
-
({ children, model, onDrop }: GameboardRootProps): React.JSX.Element;
|
|
14
|
-
displayName: string;
|
|
15
|
-
};
|
|
16
|
-
Content: React.ForwardRefExoticComponent<Omit<React.PropsWithChildren<{
|
|
17
|
-
style?: CSSProperties;
|
|
18
|
-
}>, "className"> & {
|
|
19
|
-
classNames?: import("@dxos/react-ui-types").ClassNameValue;
|
|
20
|
-
} & React.RefAttributes<HTMLDivElement>>;
|
|
21
|
-
};
|
|
22
|
-
export type { GameboardRootProps, GameboardContentsProps };
|
|
23
|
-
//# sourceMappingURL=Gameboard.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Gameboard.d.ts","sourceRoot":"","sources":["../../../../src/Gameboard/Gameboard.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,EAAE,KAAK,aAAa,EAAc,KAAK,iBAAiB,EAAoC,MAAM,OAAO,CAAC;AAGxH,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAItD,OAAO,EAAE,KAAK,cAAc,EAAuB,KAAK,IAAI,EAAoB,MAAM,SAAS,CAAC;AAEhG,KAAK,kBAAkB,GAAG,iBAAiB,CAAC;IAC1C,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC;CAClC,CAAC,CAAC;AA6DH,KAAK,sBAAsB,GAAG,eAAe,CAC3C,iBAAiB,CAAC;IAChB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB,CAAC,CACH,CAAC;AAeF,eAAO,MAAM,SAAS;;sCA3E8B,kBAAkB;;;;gBA0D1D,aAAa;;;;CAoBxB,CAAC;AAEF,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Piece.d.ts","sourceRoot":"","sources":["../../../../src/Gameboard/Piece.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,EAA+B,KAAK,EAAE,EAAE,KAAK,QAAQ,EAAQ,MAAM,OAAO,CAAC;AAKzF,OAAO,EAAgC,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAIpF,OAAO,EAA8C,KAAK,WAAW,EAAE,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AACpG,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;AAE5C,MAAM,MAAM,UAAU,GAAG,eAAe,CAAC;IACvC,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,aAAa,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC;CACxC,CAAC,CAAC;AAEH,eAAO,MAAM,KAAK,2FAAuE,UAAU,uBAwGjG,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Square.d.ts","sourceRoot":"","sources":["../../../../src/Gameboard/Square.tsx"],"names":[],"mappings":"AAKA,OAAO,KAA4C,MAAM,OAAO,CAAC;AAIjE,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAItD,OAAO,EAAW,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;AAI5C,MAAM,MAAM,WAAW,GAAG,eAAe,CAAC;IACxC,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,aAAa,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CAAC;AAEH,eAAO,MAAM,MAAM,sEAAkD,WAAW,uBA+C9E,CAAC"}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { type PieceRecord, type GameboardModel, type Move } from './types';
|
|
2
|
-
export type GameboardContextType = {
|
|
3
|
-
model?: GameboardModel;
|
|
4
|
-
dragging?: boolean;
|
|
5
|
-
promoting?: PieceRecord;
|
|
6
|
-
onPromotion: (move: Move) => void;
|
|
7
|
-
};
|
|
8
|
-
export declare const GameboardContext: import("react").Context<GameboardContextType | undefined>;
|
|
9
|
-
export declare const useBoardContext: () => GameboardContextType;
|
|
10
|
-
//# sourceMappingURL=context.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../../src/Gameboard/context.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,KAAK,IAAI,EAAE,MAAM,SAAS,CAAC;AAE3E,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,WAAW,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;CACnC,CAAC;AAEF,eAAO,MAAM,gBAAgB,2DAA6D,CAAC;AAE3F,eAAO,MAAM,eAAe,4BAE3B,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/Gameboard/index.ts"],"names":[],"mappings":"AAIA,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AAEvB,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/Gameboard/types.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,MAAM,MAAM,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAEvC,MAAM,MAAM,QAAQ,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAExC,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAE/B,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,SAAS,GAAG,SAAS,IAAI;IACzD,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,CAAC,CAAC;IACR,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,CAAC;CACpB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,SAAS,GAAG,SAAS,IAAI,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;AAEvF,MAAM,MAAM,IAAI,GAAG;IACjB,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,QAAQ,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;IACjB,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,UAAU,QAAQ,KAAG,MAA4B,CAAC;AACnF,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,KAAG,QAAkD,CAAC;AAGlG,eAAO,MAAM,OAAO,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,WAMtB,CAAC;AAG7B,eAAO,MAAM,UAAU,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,QACwC,CAAC;AAE9F,eAAO,MAAM,eAAe,GAAI,IAAI,QAAQ,EAAE,IAAI,QAAQ,KAAG,OAA6C,CAAC;AAE3G;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,SAAS,GAAG,SAAS;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,WAAW,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC;IACrC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC;CACtC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../../../src/Gameboard/util.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC,CAAC;AAE/E,eAAO,MAAM,iBAAiB,GAAI,WAAW,WAAW,EAAE,SAAS,WAAW,KAAG,aAShF,CAAC"}
|
package/src/Chessboard/chess.ts
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { type ReadonlySignal, signal } from '@preact/signals-core';
|
|
6
|
-
import { Chess, validateFen } from 'chess.js';
|
|
7
|
-
import { type FC, type SVGProps } from 'react';
|
|
8
|
-
|
|
9
|
-
import { log } from '@dxos/log';
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
type Move,
|
|
13
|
-
type Location,
|
|
14
|
-
type PieceMap,
|
|
15
|
-
locationToString,
|
|
16
|
-
type PieceType,
|
|
17
|
-
type GameboardModel,
|
|
18
|
-
type Player,
|
|
19
|
-
} from '../Gameboard';
|
|
20
|
-
import * as Alpha from '../gen/pieces/chess/alpha';
|
|
21
|
-
|
|
22
|
-
export type ChessPiece = 'BK' | 'BQ' | 'BR' | 'BB' | 'BN' | 'BP' | 'WK' | 'WQ' | 'WR' | 'WB' | 'WN' | 'WP';
|
|
23
|
-
|
|
24
|
-
export const ChessPieces: Record<ChessPiece, FC<SVGProps<SVGSVGElement>>> = Alpha;
|
|
25
|
-
|
|
26
|
-
export const posToLocation = (pos: string): Location => {
|
|
27
|
-
const col = pos.charCodeAt(0) - 'a'.charCodeAt(0);
|
|
28
|
-
const row = parseInt(pos[1]) - 1;
|
|
29
|
-
return [row, col];
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export const locationToPos = ([row, col]: Location): string => {
|
|
33
|
-
return String.fromCharCode(col + 'a'.charCodeAt(0)) + (row + 1);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const styles = {
|
|
37
|
-
neutral: {
|
|
38
|
-
black: 'bg-neutral-50',
|
|
39
|
-
white: 'bg-neutral-200',
|
|
40
|
-
promotion: 'bg-neutral-200 hover:bg-neutral-300 opacity-70 hover:opacity-100',
|
|
41
|
-
},
|
|
42
|
-
original: {
|
|
43
|
-
black: 'bg-[#6C95B9]',
|
|
44
|
-
white: 'bg-[#CCD3DB]',
|
|
45
|
-
promotion: 'duration-500 bg-[#CCD3DB] opacity-70 hover:opacity-100',
|
|
46
|
-
},
|
|
47
|
-
blue: {
|
|
48
|
-
black: 'bg-[#608BC1]',
|
|
49
|
-
white: 'bg-[#CBDCEB]',
|
|
50
|
-
promotion: 'duration-500 bg-[#CBDCEB] opacity-70 hover:opacity-100',
|
|
51
|
-
},
|
|
52
|
-
green: {
|
|
53
|
-
black: 'bg-[#8EB486]',
|
|
54
|
-
white: 'bg-[#FDF7F4]',
|
|
55
|
-
promotion: 'duration-500 bg-[#FDF7F4] opacity-70 hover:opacity-100',
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export const boardStyles = styles.original;
|
|
60
|
-
|
|
61
|
-
export const getSquareColor = ([row, col]: Location) => {
|
|
62
|
-
return (col + row) % 2 === 0 ? boardStyles.black : boardStyles.white;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Attempt move.
|
|
67
|
-
*/
|
|
68
|
-
const makeMove = (game: Chess, move: Move): Chess | null => {
|
|
69
|
-
const from = locationToPos(move.from);
|
|
70
|
-
const to = locationToPos(move.to);
|
|
71
|
-
try {
|
|
72
|
-
log('makeMove', { move });
|
|
73
|
-
const promotion = move.promotion ? move.promotion[1].toLowerCase() : 'q';
|
|
74
|
-
game.move({ from, to, promotion }, { strict: false });
|
|
75
|
-
return game;
|
|
76
|
-
} catch (err) {
|
|
77
|
-
// Ignore.
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Chess model.
|
|
84
|
-
*/
|
|
85
|
-
export class ChessModel implements GameboardModel<ChessPiece> {
|
|
86
|
-
private _game!: Chess;
|
|
87
|
-
private readonly _pieces = signal<PieceMap<ChessPiece>>({});
|
|
88
|
-
|
|
89
|
-
constructor(fen?: string) {
|
|
90
|
-
this.initialize(fen);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
get turn(): Player {
|
|
94
|
-
return this._game.turn() === 'w' ? 'white' : 'black';
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
get pieces(): ReadonlySignal<PieceMap<ChessPiece>> {
|
|
98
|
-
return this._pieces;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
get game(): Chess {
|
|
102
|
-
return this._game;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
get fen(): string {
|
|
106
|
-
return this._game.fen();
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
initialize(fen?: string): void {
|
|
110
|
-
this._pieces.value = {};
|
|
111
|
-
this._game = new Chess(fen ? (validateFen(fen).ok ? fen : undefined) : undefined);
|
|
112
|
-
this._update();
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
isValidMove(move: Move): boolean {
|
|
116
|
-
return makeMove(new Chess(this._game.fen()), move) !== null;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
canPromote(move: Move): boolean {
|
|
120
|
-
const isPawnMove = move.piece === 'BP' || move.piece === 'WP';
|
|
121
|
-
const isToLastRank = move.to[0] === 0 || move.to[0] === 7;
|
|
122
|
-
return isPawnMove && isToLastRank;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
makeMove(move: Move): boolean {
|
|
126
|
-
const game = makeMove(this._game, move);
|
|
127
|
-
if (!game) {
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
this._game = game;
|
|
132
|
-
this._update();
|
|
133
|
-
return true;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
makeRandomMove(): boolean {
|
|
137
|
-
const moves = this._game.moves();
|
|
138
|
-
if (!moves.length) {
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const move = moves[Math.floor(Math.random() * moves.length)];
|
|
143
|
-
this._game.move(move);
|
|
144
|
-
this._update();
|
|
145
|
-
return true;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Update pieces preserving identity.
|
|
150
|
-
*/
|
|
151
|
-
private _update(): void {
|
|
152
|
-
const pieces: PieceMap<ChessPiece> = {};
|
|
153
|
-
this._game.board().flatMap((row) =>
|
|
154
|
-
row.forEach((record) => {
|
|
155
|
-
if (!record) {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const { square, type, color } = record;
|
|
160
|
-
const pieceType = `${color.toUpperCase()}${type.toUpperCase()}` as ChessPiece;
|
|
161
|
-
const location = posToLocation(square);
|
|
162
|
-
pieces[locationToString(location)] = {
|
|
163
|
-
id: `${square}-${pieceType}`,
|
|
164
|
-
type: pieceType,
|
|
165
|
-
side: color === 'w' ? 'white' : 'black',
|
|
166
|
-
location,
|
|
167
|
-
};
|
|
168
|
-
}),
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
this._pieces.value = mapPieces(this._pieces.value, pieces);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Preserve the original piece objects (and IDs).
|
|
177
|
-
*/
|
|
178
|
-
export const mapPieces = <T extends PieceType>(before: PieceMap<T>, after: PieceMap<T>): PieceMap<T> => {
|
|
179
|
-
const difference: { added: PieceMap; removed: PieceMap } = {
|
|
180
|
-
removed: {},
|
|
181
|
-
added: {},
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
(Object.keys(before) as Array<keyof typeof before>).forEach((square) => {
|
|
185
|
-
if (after[square]?.type !== before[square]?.type) {
|
|
186
|
-
difference.removed[square] = before[square];
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
(Object.keys(after) as Array<keyof typeof after>).forEach((square) => {
|
|
191
|
-
if (before[square]?.type !== after[square]?.type) {
|
|
192
|
-
difference.added[square] = after[square];
|
|
193
|
-
} else {
|
|
194
|
-
after[square] = before[square];
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
for (const piece of Object.values(difference.added)) {
|
|
199
|
-
const previous = Object.values(difference.removed).find((p) => p.type === piece.type);
|
|
200
|
-
if (previous) {
|
|
201
|
-
piece.id = previous.id;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
log('delta', {
|
|
206
|
-
before: Object.keys(before).length,
|
|
207
|
-
after: Object.keys(after).length,
|
|
208
|
-
removed: Object.keys(difference.removed).length,
|
|
209
|
-
added: Object.keys(difference.added).length,
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
return after;
|
|
213
|
-
};
|