@react-chess-tools/react-chess-puzzle 0.6.2 → 1.0.1
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/CHANGELOG.md +28 -0
- package/README.md +568 -0
- package/dist/index.cjs +513 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +128 -0
- package/dist/index.d.ts +128 -0
- package/dist/{index.mjs → index.js} +143 -92
- package/dist/index.js.map +1 -0
- package/package.json +19 -9
- package/src/components/ChessPuzzle/ThemePuzzle.stories.tsx +300 -0
- package/src/components/ChessPuzzle/parts/Hint.tsx +32 -23
- package/src/components/ChessPuzzle/parts/PuzzleBoard.tsx +33 -27
- package/src/components/ChessPuzzle/parts/Reset.tsx +56 -36
- package/src/components/ChessPuzzle/parts/Root.tsx +29 -5
- package/src/components/ChessPuzzle/parts/__tests__/Hint.test.tsx +158 -0
- package/src/components/ChessPuzzle/parts/__tests__/PuzzleBoard.test.tsx +140 -0
- package/src/components/ChessPuzzle/parts/__tests__/Reset.test.tsx +341 -0
- package/src/components/ChessPuzzle/parts/__tests__/Root.test.tsx +42 -0
- package/src/docs/Theming.mdx +255 -0
- package/src/index.ts +14 -0
- package/src/theme/__tests__/context.test.tsx +66 -0
- package/src/theme/__tests__/defaults.test.ts +48 -0
- package/src/theme/__tests__/utils.test.ts +76 -0
- package/src/theme/context.tsx +36 -0
- package/src/theme/defaults.ts +16 -0
- package/src/theme/index.ts +20 -0
- package/src/theme/types.ts +29 -0
- package/src/theme/utils.ts +28 -0
- package/src/utils/__tests__/index.test.ts +0 -17
- package/src/utils/index.ts +21 -21
- package/README.MD +0 -344
- package/dist/index.d.mts +0 -57
- package/dist/index.mjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @react-chess-tools/react-chess-puzzle
|
|
2
2
|
|
|
3
|
+
## 1.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f9b7665: feat: add ref forwarding, HTML attributes, and improved asChild pattern
|
|
8
|
+
- Updated dependencies [f9b7665]
|
|
9
|
+
- @react-chess-tools/react-chess-game@1.0.1
|
|
10
|
+
|
|
11
|
+
## 1.0.0
|
|
12
|
+
|
|
13
|
+
### Major Changes
|
|
14
|
+
|
|
15
|
+
- b8f72df: Add comprehensive theming system with ThemeProvider, preset themes (Default, Lichess, Chess.com), Storybook documentation with interactive playgrounds, and comprehensive tests.
|
|
16
|
+
|
|
17
|
+
### Minor Changes
|
|
18
|
+
|
|
19
|
+
- 8f16568: Add dual package ESM + CJS support with conditional exports for better compatibility across different environments and build tools.
|
|
20
|
+
- 15482ec: Upgrade dependencies including ESLint 9, TypeScript-ESLint 8, Jest 30, React 19.2.3, chess.js 1.4.0, and more.
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- b38be2f: Add automated release workflow with modern CI/CD pipeline. Fix changesets config and simplify package.json scripts.
|
|
25
|
+
- Updated dependencies [8f16568]
|
|
26
|
+
- Updated dependencies [b38be2f]
|
|
27
|
+
- Updated dependencies [b8f72df]
|
|
28
|
+
- Updated dependencies [15482ec]
|
|
29
|
+
- @react-chess-tools/react-chess-game@1.0.0
|
|
30
|
+
|
|
3
31
|
## 0.6.2
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
package/README.md
ADDED
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1>@react-chess-tools/react-chess-puzzle</h1>
|
|
3
|
+
<p>A lightweight, customizable React component library for rendering and interacting with chess puzzles</p>
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@react-chess-tools/react-chess-puzzle)
|
|
6
|
+
[](https://www.npmjs.com/package/@react-chess-tools/react-chess-puzzle)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
## Table of Contents
|
|
13
|
+
|
|
14
|
+
- [Overview](#overview)
|
|
15
|
+
- [Features](#features)
|
|
16
|
+
- [Installation](#installation)
|
|
17
|
+
- [Quick Start](#quick-start)
|
|
18
|
+
- [Demo](#demo)
|
|
19
|
+
- [Puzzle Solving Flow](#puzzle-solving-flow)
|
|
20
|
+
- [API Reference](#api-reference)
|
|
21
|
+
- [ChessPuzzle.Root](#chesspuzzleroot)
|
|
22
|
+
- [ChessPuzzle.Board](#chesspuzzleboard)
|
|
23
|
+
- [ChessPuzzle.Reset](#chesspuzzlereset)
|
|
24
|
+
- [ChessPuzzle.Hint](#chesspuzzlehint)
|
|
25
|
+
- [Hooks](#hooks)
|
|
26
|
+
- [useChessPuzzleContext](#usechesspuzzlecontext)
|
|
27
|
+
- [useChessGameContext](#usechessgamecontext)
|
|
28
|
+
- [Integration with react-chess-game](#integration-with-react-chess-game)
|
|
29
|
+
- [Examples](#examples)
|
|
30
|
+
- [License](#license)
|
|
31
|
+
|
|
32
|
+
## Overview
|
|
33
|
+
|
|
34
|
+
`@react-chess-tools/react-chess-puzzle` is a React component library for creating interactive chess puzzle experiences. Built on top of [@react-chess-tools/react-chess-game](https://www.npmjs.com/package/@react-chess-tools/react-chess-game), it provides puzzle-specific features like move validation, hints, and progress tracking.
|
|
35
|
+
|
|
36
|
+
## Features
|
|
37
|
+
|
|
38
|
+
- **Move Validation** - Automatically validates moves against the puzzle solution
|
|
39
|
+
- **Hints** - Show the next correct move to help users
|
|
40
|
+
- **Progress Tracking** - Track puzzle state (not-started, in-progress, solved, failed)
|
|
41
|
+
- **Callbacks** - React to puzzle solve/fail events
|
|
42
|
+
- **Built-in Reset** - Easily restart puzzles or load new ones
|
|
43
|
+
- **Sound Effects** - Integrates with ChessGame.Sounds for audio feedback
|
|
44
|
+
- **Keyboard Controls** - Navigate through puzzle moves with keyboard
|
|
45
|
+
- **TypeScript** - Full TypeScript support with comprehensive type definitions
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install @react-chess-tools/react-chess-puzzle
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
yarn add @react-chess-tools/react-chess-puzzle
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pnpm add @react-chess-tools/react-chess-puzzle
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Quick Start
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { ChessPuzzle } from "@react-chess-tools/react-chess-puzzle";
|
|
65
|
+
|
|
66
|
+
function App() {
|
|
67
|
+
const puzzle = {
|
|
68
|
+
fen: "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3",
|
|
69
|
+
moves: ["d2d4", "e5d4", "f3d4"],
|
|
70
|
+
makeFirstMove: false,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<ChessPuzzle.Root puzzle={puzzle}>
|
|
75
|
+
<ChessPuzzle.Board />
|
|
76
|
+
<ChessPuzzle.Reset>Restart</ChessPuzzle.Reset>
|
|
77
|
+
<ChessPuzzle.Hint>Get Hint</ChessPuzzle.Hint>
|
|
78
|
+
</ChessPuzzle.Root>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Demo
|
|
84
|
+
|
|
85
|
+
Visit the [live demo](https://react-chess-tools.vercel.app/) to see the component in action.
|
|
86
|
+
|
|
87
|
+
## Puzzle Solving Flow
|
|
88
|
+
|
|
89
|
+
1. **Initial Setup** - The board displays the position from the FEN string
|
|
90
|
+
2. **First Move** - If `makeFirstMove` is `true`, the component automatically plays the first move
|
|
91
|
+
3. **User Interaction** - The user attempts to solve the puzzle by making moves
|
|
92
|
+
4. **Validation** - Each move is validated against the solution:
|
|
93
|
+
- Correct move: The puzzle continues, opponent's response is auto-played
|
|
94
|
+
- Incorrect move: The puzzle is marked as failed
|
|
95
|
+
5. **Completion** - When all correct moves are made, the puzzle is marked as solved
|
|
96
|
+
|
|
97
|
+
## API Reference
|
|
98
|
+
|
|
99
|
+
### ChessPuzzle.Root
|
|
100
|
+
|
|
101
|
+
The root component that provides puzzle context to all child components.
|
|
102
|
+
|
|
103
|
+
**Note:** This is a logic-only component (Context Provider). It does not render any DOM elements.
|
|
104
|
+
|
|
105
|
+
#### Props
|
|
106
|
+
|
|
107
|
+
| Name | Type | Default | Description |
|
|
108
|
+
| ---------- | --------------------------------------- | ------- | --------------------------------------- |
|
|
109
|
+
| `puzzle` | `Puzzle` | - | The puzzle configuration (required) |
|
|
110
|
+
| `onSolve` | `(ctx: ChessPuzzleContextType) => void` | - | Callback when puzzle is solved |
|
|
111
|
+
| `onFail` | `(ctx: ChessPuzzleContextType) => void` | - | Callback when an incorrect move is made |
|
|
112
|
+
| `theme` | `PartialChessPuzzleTheme` | - | Optional theme configuration |
|
|
113
|
+
| `children` | `ReactNode` | - | Child components |
|
|
114
|
+
|
|
115
|
+
#### Puzzle Object
|
|
116
|
+
|
|
117
|
+
| Property | Type | Default | Description |
|
|
118
|
+
| --------------- | ---------- | ------- | ------------------------------------------- |
|
|
119
|
+
| `fen` | `string` | - | Initial position in FEN notation |
|
|
120
|
+
| `moves` | `string[]` | - | Solution moves in algebraic or UCI notation |
|
|
121
|
+
| `makeFirstMove` | `boolean` | `false` | Whether to auto-play the first move |
|
|
122
|
+
|
|
123
|
+
#### Example
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
<ChessPuzzle.Root
|
|
127
|
+
puzzle={{
|
|
128
|
+
fen: "4kb1r/p2r1ppp/4qn2/1B2p1B1/4P3/1Q6/PPP2PPP/2KR4 w k - 0 1",
|
|
129
|
+
moves: ["Bxd7+", "Nxd7", "Qb8+", "Nxb8", "Rd8#"],
|
|
130
|
+
makeFirstMove: false,
|
|
131
|
+
}}
|
|
132
|
+
onSolve={(ctx) => console.log("Solved!", ctx.movesPlayed)}
|
|
133
|
+
onFail={(ctx) => console.log("Failed at move", ctx.movesPlayed)}
|
|
134
|
+
>
|
|
135
|
+
<ChessPuzzle.Board />
|
|
136
|
+
</ChessPuzzle.Root>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### ChessPuzzle.Board
|
|
140
|
+
|
|
141
|
+
Renders the chess board. Delegates to `ChessGame.Board` under the hood.
|
|
142
|
+
|
|
143
|
+
Supports **ref forwarding** and all standard **HTML div attributes** (className, style, id, data-_, aria-_, etc.).
|
|
144
|
+
|
|
145
|
+
#### Props
|
|
146
|
+
|
|
147
|
+
| Name | Type | Description |
|
|
148
|
+
| ----------- | -------------------------------- | --------------------------------------------- |
|
|
149
|
+
| `options` | `ChessboardOptions` | Options forwarded to `react-chessboard` v5 |
|
|
150
|
+
| `ref` | `Ref<HTMLDivElement>` | Forwarded ref to the underlying board element |
|
|
151
|
+
| `className` | `string` | Custom CSS class names |
|
|
152
|
+
| `style` | `CSSProperties` | Custom inline styles |
|
|
153
|
+
| `...` | `HTMLAttributes<HTMLDivElement>` | All standard HTML div attributes |
|
|
154
|
+
|
|
155
|
+
#### Example
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
<ChessPuzzle.Root puzzle={puzzle}>
|
|
159
|
+
<ChessPuzzle.Board
|
|
160
|
+
options={{
|
|
161
|
+
showNotation: true,
|
|
162
|
+
animationDurationInMs: 200,
|
|
163
|
+
}}
|
|
164
|
+
className="puzzle-board"
|
|
165
|
+
style={{ boxShadow: "0 4px 6px rgba(0,0,0,0.1)" }}
|
|
166
|
+
/>
|
|
167
|
+
</ChessPuzzle.Root>
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### ChessPuzzle.Reset
|
|
171
|
+
|
|
172
|
+
A button component that resets the current puzzle or loads a new one.
|
|
173
|
+
|
|
174
|
+
Supports **ref forwarding**, **asChild pattern**, and all standard **HTML button attributes** (className, style, disabled, etc.).
|
|
175
|
+
|
|
176
|
+
#### Props
|
|
177
|
+
|
|
178
|
+
| Name | Type | Default | Description |
|
|
179
|
+
| ----------- | ----------------------------------------- | ----------------------------------- | --------------------------------------------------- |
|
|
180
|
+
| `puzzle` | `Puzzle` | - | New puzzle to load (resets current if not provided) |
|
|
181
|
+
| `onReset` | `(ctx: ChessPuzzleContextType) => void` | - | Callback after reset |
|
|
182
|
+
| `showOn` | `Status[]` | `["failed", "solved"]` | States in which the button is visible |
|
|
183
|
+
| `asChild` | `boolean` | `false` | Render as child element (slot pattern) |
|
|
184
|
+
| `ref` | `Ref<HTMLButtonElement>` | Forwarded ref to the button element |
|
|
185
|
+
| `className` | `string` | Custom CSS class names |
|
|
186
|
+
| `...` | `ButtonHTMLAttributes<HTMLButtonElement>` | All standard HTML button attributes |
|
|
187
|
+
|
|
188
|
+
**Status values:** `"not-started"`, `"in-progress"`, `"solved"`, `"failed"`
|
|
189
|
+
|
|
190
|
+
#### Example
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
<ChessPuzzle.Root puzzle={puzzle}>
|
|
194
|
+
<ChessPuzzle.Board />
|
|
195
|
+
<ChessPuzzle.Reset>Try Again</ChessPuzzle.Reset>
|
|
196
|
+
<ChessPuzzle.Reset puzzle={nextPuzzle}>Next Puzzle</ChessPuzzle.Reset>
|
|
197
|
+
</ChessPuzzle.Root>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### Using asChild Pattern
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
// Render as a custom button component
|
|
204
|
+
import { MyCustomButton } from './MyButton';
|
|
205
|
+
|
|
206
|
+
<ChessPuzzle.Root puzzle={puzzle}>
|
|
207
|
+
<ChessPuzzle.Board />
|
|
208
|
+
<ChessPuzzle.Reset asChild>
|
|
209
|
+
<MyCustomButton variant="primary">Try Again</MyCustomButton>
|
|
210
|
+
</ChessPuzzle.Reset>
|
|
211
|
+
</ChessPuzzle.Root>
|
|
212
|
+
|
|
213
|
+
// Render as a link
|
|
214
|
+
<ChessPuzzle.Root puzzle={puzzle}>
|
|
215
|
+
<ChessPuzzle.Board />
|
|
216
|
+
<ChessPuzzle.Reset asChild>
|
|
217
|
+
<a href="#" onClick={(e) => e.preventDefault()}>
|
|
218
|
+
Restart Puzzle
|
|
219
|
+
</a>
|
|
220
|
+
</ChessPuzzle.Reset>
|
|
221
|
+
</ChessPuzzle.Root>
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
#### Event Handler Composition
|
|
225
|
+
|
|
226
|
+
When using `asChild`, both your component's onClick and the Reset's onClick handler will work together:
|
|
227
|
+
|
|
228
|
+
```tsx
|
|
229
|
+
<ChessPuzzle.Reset asChild>
|
|
230
|
+
<button onClick={() => console.log("Custom handler")} className="custom-btn">
|
|
231
|
+
Try Again
|
|
232
|
+
</button>
|
|
233
|
+
</ChessPuzzle.Reset>
|
|
234
|
+
// Both custom handler and reset logic will execute
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### ChessPuzzle.Hint
|
|
238
|
+
|
|
239
|
+
A button that highlights the next correct move on the board.
|
|
240
|
+
|
|
241
|
+
Supports **ref forwarding**, **asChild pattern**, and all standard **HTML button attributes** (className, style, disabled, etc.).
|
|
242
|
+
|
|
243
|
+
#### Props
|
|
244
|
+
|
|
245
|
+
| Name | Type | Default | Description |
|
|
246
|
+
| ----------- | ----------------------------------------- | ----------------------------------- | -------------------------------------- |
|
|
247
|
+
| `showOn` | `Status[]` | `["not-started", "in-progress"]` | States in which the button is visible |
|
|
248
|
+
| `asChild` | `boolean` | `false` | Render as child element (slot pattern) |
|
|
249
|
+
| `ref` | `Ref<HTMLButtonElement>` | Forwarded ref to the button element |
|
|
250
|
+
| `className` | `string` | Custom CSS class names |
|
|
251
|
+
| `...` | `ButtonHTMLAttributes<HTMLButtonElement>` | All standard HTML button attributes |
|
|
252
|
+
|
|
253
|
+
#### Example
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
const puzzle = {
|
|
257
|
+
fen: "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3",
|
|
258
|
+
moves: ["d2d4", "e5d4", "f3d4"],
|
|
259
|
+
makeFirstMove: false,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
<ChessPuzzle.Root puzzle={puzzle}>
|
|
263
|
+
<ChessPuzzle.Board />
|
|
264
|
+
<ChessPuzzle.Hint>Show Hint</ChessPuzzle.Hint>
|
|
265
|
+
</ChessPuzzle.Root>;
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
#### Using asChild Pattern
|
|
269
|
+
|
|
270
|
+
```tsx
|
|
271
|
+
<ChessPuzzle.Root puzzle={puzzle}>
|
|
272
|
+
<ChessPuzzle.Board />
|
|
273
|
+
<ChessPuzzle.Hint asChild>
|
|
274
|
+
<button className="hint-btn">💡 Show Hint</button>
|
|
275
|
+
</ChessPuzzle.Hint>
|
|
276
|
+
</ChessPuzzle.Root>
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Hooks
|
|
280
|
+
|
|
281
|
+
### useChessPuzzleContext
|
|
282
|
+
|
|
283
|
+
Access the puzzle state and methods from any child component.
|
|
284
|
+
|
|
285
|
+
```tsx
|
|
286
|
+
import { useChessPuzzleContext } from "@react-chess-tools/react-chess-puzzle";
|
|
287
|
+
|
|
288
|
+
function PuzzleStatus() {
|
|
289
|
+
const { puzzleState, movesPlayed, totalMoves, resetPuzzle, onHint } =
|
|
290
|
+
useChessPuzzleContext();
|
|
291
|
+
|
|
292
|
+
return (
|
|
293
|
+
<div>
|
|
294
|
+
<p>Status: {puzzleState}</p>
|
|
295
|
+
<p>
|
|
296
|
+
Progress: {movesPlayed}/{totalMoves} moves
|
|
297
|
+
</p>
|
|
298
|
+
<button onClick={resetPuzzle}>Reset</button>
|
|
299
|
+
<button onClick={onHint}>Hint</button>
|
|
300
|
+
</div>
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
#### Return Values
|
|
306
|
+
|
|
307
|
+
| Property | Type | Description |
|
|
308
|
+
| -------------- | -------------------------- | -------------------------------------- |
|
|
309
|
+
| `status` | `Status` | Current puzzle state |
|
|
310
|
+
| `puzzleState` | `Status` | Alias for `status` |
|
|
311
|
+
| `movesPlayed` | `number` | Number of correct moves made |
|
|
312
|
+
| `totalMoves` | `number` | Total moves in the solution |
|
|
313
|
+
| `puzzle` | `Puzzle` | The current puzzle object |
|
|
314
|
+
| `hint` | `Hint` | Current hint state |
|
|
315
|
+
| `nextMove` | `string \| null` | The next correct move |
|
|
316
|
+
| `isPlayerTurn` | `boolean` | Whether it's the player's turn to move |
|
|
317
|
+
| `changePuzzle` | `(puzzle: Puzzle) => void` | Load a new puzzle |
|
|
318
|
+
| `resetPuzzle` | `() => void` | Reset the current puzzle |
|
|
319
|
+
| `onHint` | `() => void` | Show hint for next move |
|
|
320
|
+
|
|
321
|
+
### useChessGameContext
|
|
322
|
+
|
|
323
|
+
Since `react-chess-puzzle` is built on `react-chess-game`, you can also access the underlying game context.
|
|
324
|
+
|
|
325
|
+
```tsx
|
|
326
|
+
import { useChessGameContext } from "@react-chess-tools/react-chess-game";
|
|
327
|
+
|
|
328
|
+
function BoardInfo() {
|
|
329
|
+
const { currentFen, info, methods } = useChessGameContext();
|
|
330
|
+
|
|
331
|
+
return (
|
|
332
|
+
<div>
|
|
333
|
+
<p>Turn: {info.turn === "w" ? "White" : "Black"}</p>
|
|
334
|
+
<button onClick={() => methods.flipBoard()}>Flip Board</button>
|
|
335
|
+
</div>
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Integration with react-chess-game
|
|
341
|
+
|
|
342
|
+
Since `react-chess-puzzle` is built on `react-chess-game`, you can use any of its components:
|
|
343
|
+
|
|
344
|
+
### Adding Sound Effects
|
|
345
|
+
|
|
346
|
+
```tsx
|
|
347
|
+
import { ChessPuzzle } from "@react-chess-tools/react-chess-puzzle";
|
|
348
|
+
import { ChessGame } from "@react-chess-tools/react-chess-game";
|
|
349
|
+
|
|
350
|
+
function PuzzleWithSounds() {
|
|
351
|
+
const puzzle = {
|
|
352
|
+
fen: "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3",
|
|
353
|
+
moves: ["d2d4", "e5d4", "f3d4"],
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
return (
|
|
357
|
+
<ChessPuzzle.Root puzzle={puzzle}>
|
|
358
|
+
<ChessGame.Sounds />
|
|
359
|
+
<ChessPuzzle.Board />
|
|
360
|
+
</ChessPuzzle.Root>
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Adding Keyboard Controls
|
|
366
|
+
|
|
367
|
+
```tsx
|
|
368
|
+
import { ChessPuzzle } from "@react-chess-tools/react-chess-puzzle";
|
|
369
|
+
import { ChessGame } from "@react-chess-tools/react-chess-game";
|
|
370
|
+
|
|
371
|
+
function PuzzleWithKeyboard() {
|
|
372
|
+
const puzzle = {
|
|
373
|
+
fen: "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3",
|
|
374
|
+
moves: ["d2d4", "e5d4", "f3d4"],
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
return (
|
|
378
|
+
<ChessPuzzle.Root puzzle={puzzle}>
|
|
379
|
+
<ChessGame.KeyboardControls />
|
|
380
|
+
<ChessPuzzle.Board />
|
|
381
|
+
</ChessPuzzle.Root>
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
## Examples
|
|
387
|
+
|
|
388
|
+
### Basic Puzzle
|
|
389
|
+
|
|
390
|
+
```tsx
|
|
391
|
+
import { ChessPuzzle } from "@react-chess-tools/react-chess-puzzle";
|
|
392
|
+
|
|
393
|
+
function BasicPuzzle() {
|
|
394
|
+
const puzzle = {
|
|
395
|
+
fen: "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3",
|
|
396
|
+
moves: ["d2d4", "e5d4", "f3d4"],
|
|
397
|
+
makeFirstMove: false,
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
return (
|
|
401
|
+
<ChessPuzzle.Root puzzle={puzzle}>
|
|
402
|
+
<ChessPuzzle.Board />
|
|
403
|
+
<ChessPuzzle.Reset>Restart</ChessPuzzle.Reset>
|
|
404
|
+
<ChessPuzzle.Hint>Hint</ChessPuzzle.Hint>
|
|
405
|
+
</ChessPuzzle.Root>
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Puzzle with Callbacks
|
|
411
|
+
|
|
412
|
+
```tsx
|
|
413
|
+
import {
|
|
414
|
+
ChessPuzzle,
|
|
415
|
+
type ChessPuzzleContextType,
|
|
416
|
+
} from "@react-chess-tools/react-chess-puzzle";
|
|
417
|
+
import { useState } from "react";
|
|
418
|
+
|
|
419
|
+
function PuzzleWithScore() {
|
|
420
|
+
const [score, setScore] = useState(0);
|
|
421
|
+
|
|
422
|
+
const handleSolve = (ctx: ChessPuzzleContextType) => {
|
|
423
|
+
setScore((prev) => prev + 10);
|
|
424
|
+
console.log(`Solved in ${ctx.movesPlayed} moves!`);
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
const handleFail = (ctx: ChessPuzzleContextType) => {
|
|
428
|
+
setScore((prev) => Math.max(0, prev - 5));
|
|
429
|
+
console.log("Incorrect move!");
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
return (
|
|
433
|
+
<div>
|
|
434
|
+
<p>Score: {score}</p>
|
|
435
|
+
<ChessPuzzle.Root
|
|
436
|
+
puzzle={puzzle}
|
|
437
|
+
onSolve={handleSolve}
|
|
438
|
+
onFail={handleFail}
|
|
439
|
+
>
|
|
440
|
+
<ChessPuzzle.Board />
|
|
441
|
+
<ChessPuzzle.Reset>Try Again</ChessPuzzle.Reset>
|
|
442
|
+
</ChessPuzzle.Root>
|
|
443
|
+
</div>
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Puzzle Trainer with Multiple Puzzles
|
|
449
|
+
|
|
450
|
+
```tsx
|
|
451
|
+
import {
|
|
452
|
+
ChessPuzzle,
|
|
453
|
+
type ChessPuzzleContextType,
|
|
454
|
+
} from "@react-chess-tools/react-chess-puzzle";
|
|
455
|
+
import { ChessGame } from "@react-chess-tools/react-chess-game";
|
|
456
|
+
import { useState } from "react";
|
|
457
|
+
|
|
458
|
+
const puzzles = [
|
|
459
|
+
{
|
|
460
|
+
fen: "4kb1r/p2r1ppp/4qn2/1B2p1B1/4P3/1Q6/PPP2PPP/2KR4 w k - 0 1",
|
|
461
|
+
moves: ["Bxd7+", "Nxd7", "Qb8+", "Nxb8", "Rd8#"],
|
|
462
|
+
makeFirstMove: false,
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
fen: "6k1/5p1p/p1q1p1p1/1pB1P3/1Pr3Pn/P4P1P/4Q3/3R2K1 b - - 0 31",
|
|
466
|
+
moves: ["h4f3", "e2f3", "c4c5", "d1d8", "g8g7", "f3f6"],
|
|
467
|
+
makeFirstMove: true,
|
|
468
|
+
},
|
|
469
|
+
];
|
|
470
|
+
|
|
471
|
+
function PuzzleTrainer() {
|
|
472
|
+
const [currentIndex, setCurrentIndex] = useState(0);
|
|
473
|
+
const [score, setScore] = useState(0);
|
|
474
|
+
|
|
475
|
+
const nextPuzzle = () => {
|
|
476
|
+
setCurrentIndex((prev) => (prev + 1) % puzzles.length);
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
const handleSolve = (ctx: ChessPuzzleContextType) => {
|
|
480
|
+
setScore((prev) => prev + 10);
|
|
481
|
+
nextPuzzle();
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
const handleFail = () => {
|
|
485
|
+
setScore((prev) => Math.max(0, prev - 5));
|
|
486
|
+
nextPuzzle();
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
return (
|
|
490
|
+
<div className="puzzle-trainer">
|
|
491
|
+
<div className="score">Score: {score}</div>
|
|
492
|
+
|
|
493
|
+
<ChessPuzzle.Root
|
|
494
|
+
puzzle={puzzles[currentIndex]}
|
|
495
|
+
onSolve={handleSolve}
|
|
496
|
+
onFail={handleFail}
|
|
497
|
+
>
|
|
498
|
+
<ChessGame.Sounds />
|
|
499
|
+
<ChessGame.KeyboardControls />
|
|
500
|
+
|
|
501
|
+
<ChessPuzzle.Board />
|
|
502
|
+
|
|
503
|
+
<div className="controls">
|
|
504
|
+
<ChessPuzzle.Reset>Restart</ChessPuzzle.Reset>
|
|
505
|
+
<ChessPuzzle.Hint>Hint</ChessPuzzle.Hint>
|
|
506
|
+
<ChessPuzzle.Reset
|
|
507
|
+
puzzle={puzzles[(currentIndex + 1) % puzzles.length]}
|
|
508
|
+
>
|
|
509
|
+
Skip
|
|
510
|
+
</ChessPuzzle.Reset>
|
|
511
|
+
</div>
|
|
512
|
+
</ChessPuzzle.Root>
|
|
513
|
+
</div>
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### Custom Status Display
|
|
519
|
+
|
|
520
|
+
```tsx
|
|
521
|
+
import {
|
|
522
|
+
ChessPuzzle,
|
|
523
|
+
useChessPuzzleContext,
|
|
524
|
+
} from "@react-chess-tools/react-chess-puzzle";
|
|
525
|
+
|
|
526
|
+
function PuzzleStatusDisplay() {
|
|
527
|
+
const { puzzleState, movesPlayed, totalMoves } = useChessPuzzleContext();
|
|
528
|
+
|
|
529
|
+
const messages = {
|
|
530
|
+
"not-started": "Make your move to start",
|
|
531
|
+
"in-progress": `Progress: ${movesPlayed}/${totalMoves} moves`,
|
|
532
|
+
solved: "Puzzle solved! Well done!",
|
|
533
|
+
failed: "Incorrect move. Try again!",
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
return <div className={`status ${puzzleState}`}>{messages[puzzleState]}</div>;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function ResetLabel() {
|
|
540
|
+
const { puzzleState } = useChessPuzzleContext();
|
|
541
|
+
return puzzleState === "solved" ? "Next Puzzle" : "Try Again";
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function PuzzleWithStatus() {
|
|
545
|
+
const puzzle = {
|
|
546
|
+
fen: "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3",
|
|
547
|
+
moves: ["d2d4", "e5d4", "f3d4"],
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
return (
|
|
551
|
+
<ChessPuzzle.Root puzzle={puzzle}>
|
|
552
|
+
<PuzzleStatusDisplay />
|
|
553
|
+
<ChessPuzzle.Board />
|
|
554
|
+
<ChessPuzzle.Reset showOn={["solved", "failed"]}>
|
|
555
|
+
<ResetLabel />
|
|
556
|
+
</ChessPuzzle.Reset>
|
|
557
|
+
</ChessPuzzle.Root>
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
## License
|
|
563
|
+
|
|
564
|
+
This project is [MIT](https://opensource.org/licenses/MIT) licensed.
|
|
565
|
+
|
|
566
|
+
## Show Your Support
|
|
567
|
+
|
|
568
|
+
Give a star if this project helped you!
|