@react-chess-tools/react-chess-puzzle 0.4.0 → 0.5.0

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @react-chess-tools/react-chess-puzzle
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3060132: Pass the whole puzzle context to onSolve and onFail callbacks
8
+
3
9
  ## 0.4.0
4
10
 
5
11
  ### Minor Changes
package/README.MD CHANGED
@@ -1,4 +1,5 @@
1
1
  <div align="center">
2
+ <h1>react-chess-puzzle</h1>
2
3
  A lightweight, customizable React component library for rendering and interacting with chess puzzles.
3
4
  </div>
4
5
 
@@ -26,50 +27,166 @@ To use the `react-chess-puzzle` package, you can import the `ChessPuzzle` compon
26
27
  import { ChessPuzzle } from "@react-chess-tools/react-chess-puzzle";
27
28
 
28
29
  const App = () => {
29
- <ChessPuzzle.Root puzzle={...}>
30
- <ChessPuzzle.Board />
31
- </ChessPuzzle.Root>;
30
+ const puzzle = {
31
+ fen: "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3",
32
+ moves: ["d2d4", "e5d4", "f3d4"],
33
+ makeFirstMove: false,
34
+ };
35
+
36
+ return (
37
+ <ChessPuzzle.Root puzzle={puzzle}>
38
+ <ChessPuzzle.Board />
39
+ <div className="controls">
40
+ <ChessPuzzle.Reset>Restart Puzzle</ChessPuzzle.Reset>
41
+ <ChessPuzzle.Hint showOn={["in-progress"]}>Get Hint</ChessPuzzle.Hint>
42
+ </div>
43
+ </ChessPuzzle.Root>
44
+ );
32
45
  };
33
46
  ```
34
47
 
48
+ ## Puzzle Solving Flow
49
+
50
+ The puzzle-solving flow follows these steps:
51
+
52
+ 1. **Initial Setup**: The board is set up using the provided FEN string
53
+ 2. **First Move**: If `makeFirstMove` is `true`, the component automatically makes the first move in the solution sequence
54
+ 3. **User Interaction**: The user attempts to solve the puzzle by making the correct moves
55
+ 4. **Feedback**: The component validates each move and provides feedback:
56
+ - If the move is correct, the puzzle continues
57
+ - If the move is incorrect, the puzzle is marked as failed
58
+ 5. **Completion**: When all correct moves have been made, the puzzle is marked as solved
59
+
35
60
  ## Documentation
36
61
 
37
62
  The `react-chess-puzzle` package provides a set of components that you can use to build your chess app. The following sections describe the components and their usage.
38
63
 
39
64
  ### ChessPuzzle.Root
40
65
 
41
- The `ChessPuzzle.Root` component is the root component of the `react-chess-puzzle` package. It is used to provide the `ChessPuzzleContext` to the rest of the components. It accept a `puzzle` prop that is used to instantiate the puzzle.
66
+ The `ChessPuzzle.Root` component is the root component of the `react-chess-puzzle` package. It is used to provide the `ChessPuzzleContext` to the rest of the components. It accepts a `puzzle` prop that is used to instantiate the puzzle.
42
67
 
43
68
  #### Props
44
69
 
45
70
  The `ChessPuzzle.Root` component accepts the following props:
46
71
 
47
- | Name | Type | Default | Description |
48
- | ----------- | ----------- | ------- | --------------------------- |
49
- | `puzzle` | `Puzzle` | | The puzzle to be solved |
50
- | `children?` | `ReactNode` | | The children to be rendered |
72
+ | Name | Type | Default | Description |
73
+ | ----------- | ------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------- |
74
+ | `puzzle` | `Puzzle` | | The puzzle to be solved |
75
+ | `onSolve` | `(puzzleContext: ChessPuzzleContextType) => void` | `undefined` | Callback function that is triggered when the puzzle is successfully solved, receives the puzzle context as parameter |
76
+ | `onFail` | `(puzzleContext: ChessPuzzleContextType) => void` | `undefined` | Callback function that is triggered when an incorrect move is played, receives the puzzle context as parameter |
77
+ | `children?` | `ReactNode` | | The children to be rendered |
51
78
 
52
- the `puzzle` prop contains the following properties:
79
+ The `puzzle` prop contains the following properties:
53
80
 
54
81
  | Name | Type | Default | Description |
55
82
  | --------------- | ---------- | ------- | -------------------------------------------------------------------------- |
56
- | `fen` | `string` | | The FEN string of the puzzle |
57
- | `moves` | `string[]` | | The moves of the puzzle |
83
+ | `fen` | `string` | | The FEN string representing the initial position of the puzzle |
84
+ | `moves` | `string[]` | | The sequence of moves (in algebraic notation) that solve the puzzle |
58
85
  | `makeFirstMove` | `boolean` | `false` | Whether the first move is part of the problem or must be played by the CPU |
59
86
 
60
87
  ### ChessPuzzle.Board
61
88
 
62
89
  The `ChessPuzzle.Board` component is used to render the chess board. It is a wrapper around the `ChessGame.Board` component and accepts the same props.
63
90
 
64
- ### Using react-chess-game Components
91
+ #### Props
92
+
93
+ Inherits all props from `ChessGame.Board` with these additional options:
94
+
95
+ | Name | Type | Default | Description |
96
+ | ---------------- | -------------------- | --------- | -------------------------------------------- |
97
+ | `showHighlights` | `boolean` | `true` | Show highlights for legal moves on the board |
98
+ | `orientation` | `"white" \| "black"` | `"white"` | Board orientation |
99
+
100
+ ### ChessPuzzle.Reset
101
+
102
+ A button component that resets the current puzzle or loads a new one.
103
+
104
+ #### Props
105
+
106
+ | Name | Type | Default | Description |
107
+ | --------- | --------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
108
+ | `puzzle` | `Puzzle \| undefined` | `undefined` | The puzzle object for a new puzzle. If not provided, the current puzzle is reset. |
109
+ | `onReset` | `() => void` | `undefined` | A callback function that is called when the puzzle is reset. |
110
+ | `showOn` | `PuzzleState[]` | `["not-started", "in-progress", "solved", "failed"]` | The state(s) in which the button is shown. Valid states are: "not-started", "in-progress", "solved", "failed" |
111
+ | `asChild` | `boolean` | `false` | Change the component to the HTML tag or custom component of the only child. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node. |
65
112
 
66
- Since `react-chess-puzzle` is built on top of `react-chess-game`, you can use any of its components within your puzzle interface. This includes:
113
+ ### ChessPuzzle.Hint
67
114
 
68
- - `ChessGame.Sounds` - Add sound effects for moves/captures
69
- - `ChessGame.KeyboardControls` - Add keyboard navigation
70
- - `useChessGameContext` - Access game state and methods
115
+ A button component that provides a hint by highlighting the next move in the solution.
71
116
 
72
- Example usage with sounds and keyboard controls:
117
+ #### Props
118
+
119
+ | Name | Type | Default | Description |
120
+ | --------- | --------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
121
+ | `showOn` | `PuzzleState[]` | `["in-progress"]` | The state(s) in which the button is shown. Valid states are: "not-started", "in-progress", "solved", "failed" |
122
+ | `asChild` | `boolean` | `false` | Change the component to the HTML tag or custom component of the only child. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node. |
123
+
124
+ ## Hooks and Context
125
+
126
+ ### useChessPuzzleContext
127
+
128
+ A hook that provides access to the puzzle state and methods.
129
+
130
+ ```tsx
131
+ import { useChessPuzzleContext } from "@react-chess-tools/react-chess-puzzle";
132
+
133
+ const MyComponent = () => {
134
+ const {
135
+ puzzleState, // "not-started" | "in-progress" | "solved" | "failed"
136
+ resetPuzzle, // Function to reset the current puzzle
137
+ showHint, // Function to show a hint
138
+ movesPlayed, // Number of moves played so far
139
+ totalMoves, // Total number of moves in the solution
140
+ } = useChessPuzzleContext();
141
+
142
+ return (
143
+ <div>
144
+ <p>Puzzle state: {puzzleState}</p>
145
+ <p>
146
+ Progress: {movesPlayed}/{totalMoves} moves
147
+ </p>
148
+ <button onClick={resetPuzzle}>Reset</button>
149
+ </div>
150
+ );
151
+ };
152
+ ```
153
+
154
+ ### useChessGameContext
155
+
156
+ Since `react-chess-puzzle` is built on top of `react-chess-game`, you can also use the `useChessGameContext` hook to access the underlying game state and methods.
157
+
158
+ ```tsx
159
+ import { useChessGameContext } from "@react-chess-tools/react-chess-game";
160
+
161
+ const MyComponent = () => {
162
+ const {
163
+ fen, // Current FEN string
164
+ gameState, // Game state (playing, checkmate, etc.)
165
+ turn, // Current turn ("w" | "b")
166
+ moves, // List of legal moves
167
+ makeMove, // Function to make a move
168
+ history, // Game move history
169
+ selectedSquare, // Currently selected square
170
+ setSelectedSquare, // Function to select a square
171
+ checkSquare, // Square with the king in check (if any)
172
+ } = useChessGameContext();
173
+
174
+ return (
175
+ <div>
176
+ <p>Current FEN: {fen}</p>
177
+ <p>Current turn: {turn === "w" ? "White" : "Black"}</p>
178
+ </div>
179
+ );
180
+ };
181
+ ```
182
+
183
+ ## Using react-chess-game Components
184
+
185
+ Since `react-chess-puzzle` is built on top of `react-chess-game`, you can use any of its components within your puzzle interface:
186
+
187
+ ### ChessGame.Sounds
188
+
189
+ Add sound effects for moves, captures, and other chess events.
73
190
 
74
191
  ```tsx
75
192
  import { ChessPuzzle } from "@react-chess-tools/react-chess-puzzle";
@@ -78,35 +195,128 @@ import { ChessGame } from "@react-chess-tools/react-chess-game";
78
195
  const App = () => (
79
196
  <ChessPuzzle.Root puzzle={...}>
80
197
  <ChessGame.Sounds />
81
- <ChessGame.KeyboardControls />
82
198
  <ChessPuzzle.Board />
83
199
  </ChessPuzzle.Root>
84
200
  );
85
201
  ```
86
202
 
87
- ### `Puzzle.Reset`
203
+ ### ChessGame.KeyboardControls
88
204
 
89
- A button that changes the puzzle. It can be used, for example, to restart the puzzle or move to the next puzzle.
205
+ Add keyboard navigation for accessible play.
90
206
 
91
- #### Props
207
+ ```tsx
208
+ import { ChessPuzzle } from "@react-chess-tools/react-chess-puzzle";
209
+ import { ChessGame } from "@react-chess-tools/react-chess-game";
92
210
 
93
- | Name | Type | Default | Description |
94
- | --------- | ---------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
95
- | `puzzle` | `Puzzle` | | The puzzle object containing the FEN string and moves sequence. If not provided, the current puzzle is reset. |
96
- | `onReset` | `() => void` | | A callback function that is called when the puzzle is reset. |
97
- | `showOn` | `"not-started" \| "in-progress" \| "solved" \| "failed"[]` | | The state(s) in which the button is shown. |
98
- | `asChild` | `boolean` | `false` | Change the component to the HTML tag or custom component of the only child. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node. |
211
+ const App = () => (
212
+ <ChessPuzzle.Root puzzle={...}>
213
+ <ChessGame.KeyboardControls />
214
+ <ChessPuzzle.Board />
215
+ </ChessPuzzle.Root>
216
+ );
217
+ ```
99
218
 
100
- ### `Puzzle.Hint`
219
+ ## Complete Example
101
220
 
102
- A button that shows the next move of the puzzle.
221
+ Here's a complete example of a chess puzzle component with sounds, keyboard controls, and custom styling:
103
222
 
104
- #### Props
223
+ ```tsx
224
+ import { ChessPuzzle } from "@react-chess-tools/react-chess-puzzle";
225
+ import { ChessGame } from "@react-chess-tools/react-chess-game";
226
+ import { useState } from "react";
227
+ import "./ChessPuzzleStyles.css"; // Your custom CSS
228
+
229
+ export const PuzzleSolver = () => {
230
+ // Example puzzles
231
+ const puzzles = [
232
+ {
233
+ fen: "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3",
234
+ moves: ["d2d4", "e5d4", "f3d4"],
235
+ makeFirstMove: false,
236
+ },
237
+ {
238
+ fen: "r1bqkb1r/pppp1ppp/2n2n2/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 4 4",
239
+ moves: ["f3g5", "d7d5", "e4d5", "c6a5"],
240
+ makeFirstMove: false,
241
+ },
242
+ ];
243
+
244
+ const [currentPuzzle, setCurrentPuzzle] = useState(0);
245
+ const [score, setScore] = useState(0);
246
+
247
+ // Function to load the next puzzle
248
+ const nextPuzzle = () => {
249
+ setCurrentPuzzle((prev) => (prev + 1) % puzzles.length);
250
+ };
251
+
252
+ const handleSolve = (puzzleContext) => {
253
+ setScore((prev) => prev + 10);
254
+ console.log(`Puzzle solved in ${puzzleContext.movesPlayed} moves`);
255
+ // Could automatically progress to next puzzle
256
+ // setTimeout(nextPuzzle, 1500);
257
+ };
258
+
259
+ const handleFail = (puzzleContext) => {
260
+ setScore((prev) => Math.max(0, prev - 5));
261
+ console.log(`Failed move: ${puzzleContext.lastMove}`);
262
+ };
263
+
264
+ return (
265
+ <div className="puzzle-container">
266
+ <div className="score">Score: {score}</div>
267
+ <ChessPuzzle.Root
268
+ puzzle={puzzles[currentPuzzle]}
269
+ onSolve={handleSolve}
270
+ onFail={handleFail}
271
+ >
272
+ <ChessGame.Sounds />
273
+ <ChessGame.KeyboardControls />
274
+
275
+ <div className="board-container">
276
+ <ChessPuzzle.Board />
277
+ </div>
278
+
279
+ <div className="controls">
280
+ <PuzzleStatus />
281
+ <div className="buttons">
282
+ <ChessPuzzle.Reset>Restart</ChessPuzzle.Reset>
283
+ <ChessPuzzle.Hint showOn={["in-progress"]}>Hint</ChessPuzzle.Hint>
284
+ <ChessPuzzle.Reset
285
+ puzzle={puzzles[(currentPuzzle + 1) % puzzles.length]}
286
+ onReset={nextPuzzle}
287
+ >
288
+ Next Puzzle
289
+ </ChessPuzzle.Reset>
290
+ </div>
291
+ </div>
292
+ </ChessPuzzle.Root>
293
+ </div>
294
+ );
295
+ };
105
296
 
106
- | Name | Type | Default | Description |
107
- | --------- | ---------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
108
- | `showOn` | `"not-started" \| "in-progress" \| "solved" \| "failed"[]` | | The state(s) in which the button is shown. |
109
- | `asChild` | `boolean` | `false` | Change the component to the HTML tag or custom component of the only child. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node. |
297
+ // Custom component using the context
298
+ const PuzzleStatus = () => {
299
+ const { puzzleState, movesPlayed, totalMoves } = useChessPuzzleContext();
300
+
301
+ let message = "";
302
+ switch (puzzleState) {
303
+ case "not-started":
304
+ message = "Make your move to start the puzzle";
305
+ break;
306
+ case "in-progress":
307
+ message = `Progress: ${movesPlayed}/${totalMoves} moves`;
308
+ break;
309
+ case "solved":
310
+ message = "Puzzle solved! Well done!";
311
+ break;
312
+ case "failed":
313
+ message = "Incorrect move. Try again!";
314
+ break;
315
+ }
316
+
317
+ return <div className={`status ${puzzleState}`}>{message}</div>;
318
+ };
319
+ ```
110
320
 
111
321
  ## 📝 License
112
322
 
package/dist/index.d.mts CHANGED
@@ -25,10 +25,20 @@ interface ResetProps {
25
25
  interface PuzzleBoardProps extends React__default.ComponentProps<typeof ChessGame.Board> {
26
26
  }
27
27
 
28
+ type ChessPuzzleContextType = {
29
+ status: Status;
30
+ changePuzzle: (puzzle: Puzzle) => void;
31
+ puzzle: Puzzle;
32
+ hint: Hint;
33
+ nextMove?: string | null;
34
+ isPlayerTurn: boolean;
35
+ onHint: () => void;
36
+ };
37
+
28
38
  interface RootProps {
29
39
  puzzle: Puzzle;
30
- onSolve?: (changePuzzle: (puzzle: Puzzle) => void) => void;
31
- onFail?: (changePuzzle: (puzzle: Puzzle) => void) => void;
40
+ onSolve?: (puzzleContext: ChessPuzzleContextType) => void;
41
+ onFail?: (puzzleContext: ChessPuzzleContextType) => void;
32
42
  }
33
43
 
34
44
  declare const ChessPuzzle: {
@@ -38,14 +48,6 @@ declare const ChessPuzzle: {
38
48
  Hint: React.FC<React.PropsWithChildren<HintProps>>;
39
49
  };
40
50
 
41
- declare const useChessPuzzleContext: () => {
42
- status: Status;
43
- changePuzzle: (puzzle: Puzzle) => void;
44
- puzzle: Puzzle;
45
- hint: Hint;
46
- onHint: () => void;
47
- nextMove: string | null | undefined;
48
- isPlayerTurn: boolean;
49
- };
51
+ declare const useChessPuzzleContext: () => ChessPuzzleContextType;
50
52
 
51
53
  export { ChessPuzzle, useChessPuzzleContext };
package/dist/index.mjs CHANGED
@@ -119,14 +119,14 @@ var reducer = (state, action) => {
119
119
  status: "in-progress"
120
120
  };
121
121
  case "PLAYER_MOVE": {
122
- const { move, onSolve, onFail, changePuzzle } = action.payload;
122
+ const { move, onSolve, onFail, puzzleContext } = action.payload;
123
123
  const isMoveRight = [move == null ? void 0 : move.san, move == null ? void 0 : move.lan].includes(
124
124
  (state == null ? void 0 : state.nextMove) || ""
125
125
  );
126
126
  const isPuzzleSolved = state.currentMoveIndex === state.puzzle.moves.length - 1;
127
127
  if (!isMoveRight) {
128
128
  if (onFail) {
129
- onFail(changePuzzle);
129
+ onFail(puzzleContext);
130
130
  }
131
131
  return {
132
132
  ...state,
@@ -138,7 +138,7 @@ var reducer = (state, action) => {
138
138
  }
139
139
  if (isPuzzleSolved) {
140
140
  if (onSolve) {
141
- onSolve(changePuzzle);
141
+ onSolve(puzzleContext);
142
142
  }
143
143
  return {
144
144
  ...state,
@@ -197,6 +197,21 @@ var useChessPuzzle = (puzzle, onSolve, onFail) => {
197
197
  makeMove(state.cpuMove);
198
198
  }
199
199
  }, [state.cpuMove]);
200
+ if (!gameContext) {
201
+ throw new Error("useChessPuzzle must be used within a ChessGameContext");
202
+ }
203
+ const onHint = () => {
204
+ dispatch({ type: "TOGGLE_HINT" });
205
+ };
206
+ const puzzleContext = {
207
+ status: state.status,
208
+ changePuzzle,
209
+ puzzle,
210
+ hint: state.hint,
211
+ onHint,
212
+ nextMove: state.nextMove,
213
+ isPlayerTurn: state.isPlayerTurn
214
+ };
200
215
  useEffect(() => {
201
216
  var _a2, _b, _c;
202
217
  if (((_a2 = game == null ? void 0 : game.history()) == null ? void 0 : _a2.length) <= 0 + (puzzle.makeFirstMove ? 1 : 0)) {
@@ -209,7 +224,7 @@ var useChessPuzzle = (puzzle, onSolve, onFail) => {
209
224
  move: ((_c = (_b = gameContext == null ? void 0 : gameContext.game) == null ? void 0 : _b.history({ verbose: true })) == null ? void 0 : _c.pop()) ?? null,
210
225
  onSolve,
211
226
  onFail,
212
- changePuzzle,
227
+ puzzleContext,
213
228
  game
214
229
  }
215
230
  });
@@ -218,21 +233,7 @@ var useChessPuzzle = (puzzle, onSolve, onFail) => {
218
233
  });
219
234
  }
220
235
  }, [(_a = game == null ? void 0 : game.history()) == null ? void 0 : _a.length]);
221
- if (!gameContext) {
222
- throw new Error("useChessPuzzle must be used within a ChessGameContext");
223
- }
224
- const onHint = () => {
225
- dispatch({ type: "TOGGLE_HINT" });
226
- };
227
- return {
228
- status: state.status,
229
- changePuzzle,
230
- puzzle,
231
- hint: state.hint,
232
- onHint,
233
- nextMove: state.nextMove,
234
- isPlayerTurn: state.isPlayerTurn
235
- };
236
+ return puzzleContext;
236
237
  };
237
238
 
238
239
  // src/components/ChessPuzzle/parts/Root.tsx
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/ChessPuzzle/parts/Root.tsx","../src/utils/index.ts","../src/hooks/useChessPuzzle.ts","../src/hooks/reducer.ts","../src/hooks/useChessPuzzleContext.ts","../src/components/ChessPuzzle/parts/PuzzleBoard.tsx","../src/components/ChessPuzzle/parts/Reset.tsx","../src/components/ChessPuzzle/parts/Hint.tsx","../src/components/ChessPuzzle/index.ts"],"sourcesContent":["import React from \"react\";\nimport { Puzzle, getOrientation } from \"../../../utils\";\nimport { useChessPuzzle } from \"../../../hooks/useChessPuzzle\";\nimport { ChessGame } from \"@react-chess-tools/react-chess-game\";\nimport { ChessPuzzleContext } from \"../../../hooks/useChessPuzzleContext\";\n\nexport interface RootProps {\n puzzle: Puzzle;\n onSolve?: (changePuzzle: (puzzle: Puzzle) => void) => void;\n onFail?: (changePuzzle: (puzzle: Puzzle) => void) => void;\n}\n\nconst PuzzleRoot: React.FC<React.PropsWithChildren<RootProps>> = ({\n puzzle,\n onSolve,\n onFail,\n children,\n}) => {\n const context = useChessPuzzle(puzzle, onSolve, onFail);\n\n return (\n <ChessPuzzleContext.Provider value={context}>\n {children}\n </ChessPuzzleContext.Provider>\n );\n};\n\nexport const Root: React.FC<React.PropsWithChildren<RootProps>> = ({\n puzzle,\n onSolve,\n onFail,\n children,\n}) => {\n return (\n <ChessGame.Root fen={puzzle.fen} orientation={getOrientation(puzzle)}>\n <PuzzleRoot puzzle={puzzle} onSolve={onSolve} onFail={onFail}>\n {children}\n </PuzzleRoot>\n </ChessGame.Root>\n );\n};\n","import { type Color, Chess, Move } from \"chess.js\";\nimport React, { CSSProperties, ReactElement, ReactNode } from \"react\";\nimport _ from \"lodash\";\n\nexport type Status = \"not-started\" | \"in-progress\" | \"solved\" | \"failed\";\n\nexport type Hint = \"none\" | \"piece\" | \"move\";\n\nexport type Puzzle = {\n fen: string;\n moves: string[];\n // if the first move of the puzzle has to be made by the cpu, as in chess.com puzzles\n makeFirstMove?: boolean;\n};\n\nconst FAIL_COLOR = \"rgba(201, 52, 48, 0.5)\";\nconst SUCCESS_COLOR = \"rgba(172, 206, 89, 0.5)\";\nconst HINT_COLOR = \"rgba(27, 172, 166, 0.5)\";\n\nexport const getOrientation = (puzzle: Puzzle): Color => {\n const fen = puzzle.fen;\n const game = new Chess(fen);\n if (puzzle.makeFirstMove) {\n game.move(puzzle.moves[0]);\n }\n return game.turn();\n};\n\ninterface ClickableElement extends ReactElement {\n props: {\n onClick?: () => void;\n };\n}\n\nexport const isClickableElement = (\n element: ReactNode,\n): element is ClickableElement => React.isValidElement(element);\n\nexport const getCustomSquareStyles = (\n status: Status,\n hint: Hint,\n isPlayerTurn: boolean,\n game: Chess,\n nextMove?: Move | null,\n) => {\n const customSquareStyles: Record<string, CSSProperties> = {};\n\n const lastMove = _.last(game.history({ verbose: true }));\n\n if (status === \"failed\" && lastMove) {\n customSquareStyles[lastMove.from] = {\n backgroundColor: FAIL_COLOR,\n };\n customSquareStyles[lastMove.to] = {\n backgroundColor: FAIL_COLOR,\n };\n }\n\n if (\n lastMove &&\n (status === \"solved\" || (status !== \"failed\" && !isPlayerTurn))\n ) {\n customSquareStyles[lastMove.from] = {\n backgroundColor: SUCCESS_COLOR,\n };\n customSquareStyles[lastMove.to] = {\n backgroundColor: SUCCESS_COLOR,\n };\n }\n\n if (hint === \"piece\") {\n if (nextMove) {\n customSquareStyles[nextMove.from] = {\n backgroundColor: HINT_COLOR,\n };\n }\n }\n\n if (hint === \"move\") {\n if (nextMove) {\n customSquareStyles[nextMove.from] = {\n backgroundColor: HINT_COLOR,\n };\n customSquareStyles[nextMove.to] = {\n backgroundColor: HINT_COLOR,\n };\n }\n }\n\n return customSquareStyles;\n};\n\nexport const stringToMove = (game: Chess, move: string | null | undefined) => {\n const copy = new Chess(game.fen());\n if (move === null || move === undefined) {\n return null;\n }\n try {\n return copy.move(move);\n } catch (e) {\n return null;\n }\n};\n","import { useEffect, useReducer } from \"react\";\nimport { initializePuzzle, reducer } from \"./reducer\";\nimport { getOrientation, type Puzzle } from \"../utils\";\nimport { useChessGameContext } from \"@react-chess-tools/react-chess-game\";\n\nexport const useChessPuzzle = (\n puzzle: Puzzle,\n onSolve?: (changePuzzle: (puzzle: Puzzle) => void) => void,\n onFail?: (changePuzzle: (puzzle: Puzzle) => void) => void,\n) => {\n const gameContext = useChessGameContext();\n\n const [state, dispatch] = useReducer(reducer, { puzzle }, initializePuzzle);\n\n const {\n game,\n methods: { makeMove, setPosition },\n } = gameContext;\n\n useEffect(() => {\n if (gameContext && game.fen() !== puzzle.fen) {\n setPosition(puzzle.fen, getOrientation(puzzle));\n }\n }, []);\n\n const changePuzzle = (puzzle: Puzzle) => {\n dispatch({ type: \"INITIALIZE\", payload: { puzzle } });\n setPosition(puzzle.fen, getOrientation(puzzle));\n };\n\n useEffect(() => {\n if (gameContext && game.fen() === puzzle.fen && state.needCpuMove) {\n setTimeout(\n () =>\n dispatch({\n type: \"CPU_MOVE\",\n }),\n 0,\n );\n }\n }, [gameContext, state.needCpuMove]);\n\n useEffect(() => {\n if (state.cpuMove) {\n makeMove(state.cpuMove);\n }\n }, [state.cpuMove]);\n\n useEffect(() => {\n if (game?.history()?.length <= 0 + (puzzle.makeFirstMove ? 1 : 0)) {\n return;\n }\n if (game.history().length % 2 === (puzzle.makeFirstMove ? 0 : 1)) {\n dispatch({\n type: \"PLAYER_MOVE\",\n payload: {\n move: gameContext?.game?.history({ verbose: true })?.pop() ?? null,\n onSolve,\n onFail,\n changePuzzle,\n game: game,\n },\n });\n\n dispatch({\n type: \"CPU_MOVE\",\n });\n }\n }, [game?.history()?.length]);\n\n if (!gameContext) {\n throw new Error(\"useChessPuzzle must be used within a ChessGameContext\");\n }\n\n const onHint = () => {\n dispatch({ type: \"TOGGLE_HINT\" });\n };\n\n return {\n status: state.status,\n changePuzzle,\n puzzle,\n hint: state.hint,\n onHint,\n nextMove: state.nextMove,\n isPlayerTurn: state.isPlayerTurn,\n };\n};\n","import { Chess, Move } from \"chess.js\";\nimport { type Puzzle, type Hint, type Status } from \"../utils\";\n\nexport type State = {\n puzzle: Puzzle;\n currentMoveIndex: number;\n status: Status;\n cpuMove?: string | null;\n nextMove?: string | null;\n hint: Hint;\n needCpuMove: boolean;\n isPlayerTurn: boolean;\n};\n\nexport type Action =\n | {\n type: \"INITIALIZE\";\n payload: {\n puzzle: Puzzle;\n };\n }\n | {\n type: \"RESET\";\n }\n | { type: \"TOGGLE_HINT\" }\n | {\n type: \"CPU_MOVE\";\n }\n | {\n type: \"PLAYER_MOVE\";\n payload: {\n move?: Move | null;\n onSolve?: (changePuzzle: (puzzle: Puzzle) => void) => void;\n onFail?: (changePuzzle: (puzzle: Puzzle) => void) => void;\n changePuzzle: (puzzle: Puzzle) => void;\n game: Chess;\n };\n };\n\nexport const initializePuzzle = ({ puzzle }: { puzzle: Puzzle }): State => {\n return {\n puzzle,\n currentMoveIndex: 0,\n status: \"not-started\",\n nextMove: puzzle.moves[0],\n hint: \"none\",\n cpuMove: null,\n needCpuMove: !!puzzle.makeFirstMove,\n isPlayerTurn: !puzzle.makeFirstMove,\n };\n};\n\nexport const reducer = (state: State, action: Action): State => {\n switch (action.type) {\n case \"INITIALIZE\":\n return {\n ...state,\n ...initializePuzzle(action.payload),\n };\n case \"RESET\":\n return {\n ...state,\n ...initializePuzzle({\n puzzle: state.puzzle,\n }),\n };\n case \"TOGGLE_HINT\":\n if (state.hint === \"none\") {\n return { ...state, hint: \"piece\" };\n }\n return { ...state, hint: \"move\" };\n case \"CPU_MOVE\":\n if (state.isPlayerTurn) {\n return state;\n }\n if ([\"solved\", \"failed\"].includes(state.status)) {\n return state;\n }\n\n return {\n ...state,\n currentMoveIndex: state.currentMoveIndex + 1,\n cpuMove: state.puzzle.moves[state.currentMoveIndex],\n nextMove:\n state.currentMoveIndex < state.puzzle.moves.length - 1\n ? state.puzzle.moves[state.currentMoveIndex + 1]\n : null,\n needCpuMove: false,\n isPlayerTurn: true,\n status: \"in-progress\",\n };\n\n case \"PLAYER_MOVE\": {\n const { move, onSolve, onFail, changePuzzle } = action.payload;\n\n const isMoveRight = [move?.san, move?.lan].includes(\n state?.nextMove || \"\",\n );\n const isPuzzleSolved =\n state.currentMoveIndex === state.puzzle.moves.length - 1;\n\n if (!isMoveRight) {\n if (onFail) {\n onFail(changePuzzle);\n }\n return {\n ...state,\n status: \"failed\",\n nextMove: null,\n hint: \"none\",\n isPlayerTurn: false,\n };\n }\n\n if (isPuzzleSolved) {\n if (onSolve) {\n onSolve(changePuzzle);\n }\n\n return {\n ...state,\n status: \"solved\",\n nextMove: null,\n hint: \"none\",\n isPlayerTurn: false,\n };\n }\n\n return {\n ...state,\n hint: \"none\",\n currentMoveIndex: state.currentMoveIndex + 1,\n nextMove: state.puzzle.moves[state.currentMoveIndex + 1],\n status: \"in-progress\",\n needCpuMove: true,\n isPlayerTurn: false,\n };\n }\n\n default:\n return state;\n }\n};\n","import React from \"react\";\nimport { useChessPuzzle } from \"./useChessPuzzle\";\n\nexport const ChessPuzzleContext = React.createContext<ReturnType<\n typeof useChessPuzzle\n> | null>(null);\n\nexport const useChessPuzzleContext = () => {\n const context = React.useContext(ChessPuzzleContext);\n if (!context) {\n throw new Error(\n \"useChessGameContext must be used within a ChessGameProvider\",\n );\n }\n return context;\n};\n","import React from \"react\";\nimport {\n ChessGame,\n useChessGameContext,\n} from \"@react-chess-tools/react-chess-game\";\nimport { getCustomSquareStyles, stringToMove } from \"../../../utils\";\nimport { useChessPuzzleContext } from \"../../..\";\n\nexport interface PuzzleBoardProps\n extends React.ComponentProps<typeof ChessGame.Board> {}\nexport const PuzzleBoard: React.FC<PuzzleBoardProps> = ({ ...rest }) => {\n const puzzleContext = useChessPuzzleContext();\n const gameContext = useChessGameContext();\n\n if (!puzzleContext) {\n throw new Error(\"PuzzleContext not found\");\n }\n if (!gameContext) {\n throw new Error(\"ChessGameContext not found\");\n }\n\n const { game } = gameContext;\n const { status, hint, isPlayerTurn, nextMove } = puzzleContext;\n\n return (\n <ChessGame.Board\n customSquareStyles={getCustomSquareStyles(\n status,\n hint,\n isPlayerTurn,\n game,\n stringToMove(game, nextMove),\n )}\n {...rest}\n />\n );\n};\n","import React from \"react\";\nimport { isClickableElement, type Puzzle, type Status } from \"../../../utils\";\nimport { useChessPuzzleContext } from \"../../..\";\n\nexport interface ResetProps {\n asChild?: boolean;\n puzzle?: Puzzle;\n onReset?: () => void;\n showOn?: Status[];\n}\n\nconst defaultShowOn: Status[] = [\"failed\", \"solved\"];\n\nexport const Reset: React.FC<React.PropsWithChildren<ResetProps>> = ({\n children,\n asChild,\n puzzle,\n onReset,\n showOn = defaultShowOn,\n}) => {\n const puzzleContext = useChessPuzzleContext();\n if (!puzzleContext) {\n throw new Error(\"PuzzleContext not found\");\n }\n const { changePuzzle, status } = puzzleContext;\n const handleClick = () => {\n changePuzzle(puzzle || puzzleContext.puzzle);\n onReset?.();\n };\n\n if (!showOn.includes(status)) {\n return null;\n }\n\n if (asChild) {\n const child = React.Children.only(children);\n if (isClickableElement(child)) {\n return React.cloneElement(child, {\n onClick: handleClick,\n });\n } else {\n throw new Error(\"Change child must be a clickable element\");\n }\n }\n\n return (\n <button type=\"button\" onClick={handleClick}>\n {children}\n </button>\n );\n};\n","import React from \"react\";\nimport { Status, isClickableElement } from \"../../../utils\";\nimport { useChessPuzzleContext } from \"../../..\";\n\nexport interface HintProps {\n asChild?: boolean;\n showOn?: Status[];\n}\n\nconst defaultShowOn: Status[] = [\"not-started\", \"in-progress\"];\n\nexport const Hint: React.FC<React.PropsWithChildren<HintProps>> = ({\n children,\n asChild,\n showOn = defaultShowOn,\n}) => {\n const puzzleContext = useChessPuzzleContext();\n if (!puzzleContext) {\n throw new Error(\"PuzzleContext not found\");\n }\n const { onHint, status } = puzzleContext;\n const handleClick = () => {\n onHint();\n };\n\n if (!showOn.includes(status)) {\n return null;\n }\n\n if (asChild) {\n const child = React.Children.only(children);\n if (isClickableElement(child)) {\n return React.cloneElement(child, {\n onClick: handleClick,\n });\n } else {\n throw new Error(\"Change child must be a clickable element\");\n }\n }\n\n return (\n <button type=\"button\" onClick={handleClick}>\n {children}\n </button>\n );\n};\n","import { Root } from \"./parts/Root\";\nimport { PuzzleBoard } from \"./parts/PuzzleBoard\";\nimport { Reset } from \"./parts/Reset\";\nimport { Hint } from \"./parts/Hint\";\n\nexport const ChessPuzzle = {\n Root,\n Board: PuzzleBoard,\n Reset,\n Hint,\n};\n"],"mappings":"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","names":["React","puzzle","_a","React","React","React","ChessGame","useChessGameContext","useChessGameContext","React","ChessGame","React","React","React","defaultShowOn","React"]}
1
+ {"version":3,"sources":["../src/components/ChessPuzzle/parts/Root.tsx","../src/utils/index.ts","../src/hooks/useChessPuzzle.ts","../src/hooks/reducer.ts","../src/hooks/useChessPuzzleContext.ts","../src/components/ChessPuzzle/parts/PuzzleBoard.tsx","../src/components/ChessPuzzle/parts/Reset.tsx","../src/components/ChessPuzzle/parts/Hint.tsx","../src/components/ChessPuzzle/index.ts"],"sourcesContent":["import React from \"react\";\nimport { Puzzle, getOrientation } from \"../../../utils\";\nimport {\n ChessPuzzleContextType,\n useChessPuzzle,\n} from \"../../../hooks/useChessPuzzle\";\nimport { ChessGame } from \"@react-chess-tools/react-chess-game\";\nimport { ChessPuzzleContext } from \"../../../hooks/useChessPuzzleContext\";\n\nexport interface RootProps {\n puzzle: Puzzle;\n onSolve?: (puzzleContext: ChessPuzzleContextType) => void;\n onFail?: (puzzleContext: ChessPuzzleContextType) => void;\n}\n\nconst PuzzleRoot: React.FC<React.PropsWithChildren<RootProps>> = ({\n puzzle,\n onSolve,\n onFail,\n children,\n}) => {\n const context = useChessPuzzle(puzzle, onSolve, onFail);\n\n return (\n <ChessPuzzleContext.Provider value={context}>\n {children}\n </ChessPuzzleContext.Provider>\n );\n};\n\nexport const Root: React.FC<React.PropsWithChildren<RootProps>> = ({\n puzzle,\n onSolve,\n onFail,\n children,\n}) => {\n return (\n <ChessGame.Root fen={puzzle.fen} orientation={getOrientation(puzzle)}>\n <PuzzleRoot puzzle={puzzle} onSolve={onSolve} onFail={onFail}>\n {children}\n </PuzzleRoot>\n </ChessGame.Root>\n );\n};\n","import { type Color, Chess, Move } from \"chess.js\";\nimport React, { CSSProperties, ReactElement, ReactNode } from \"react\";\nimport _ from \"lodash\";\n\nexport type Status = \"not-started\" | \"in-progress\" | \"solved\" | \"failed\";\n\nexport type Hint = \"none\" | \"piece\" | \"move\";\n\nexport type Puzzle = {\n fen: string;\n moves: string[];\n // if the first move of the puzzle has to be made by the cpu, as in chess.com puzzles\n makeFirstMove?: boolean;\n};\n\nconst FAIL_COLOR = \"rgba(201, 52, 48, 0.5)\";\nconst SUCCESS_COLOR = \"rgba(172, 206, 89, 0.5)\";\nconst HINT_COLOR = \"rgba(27, 172, 166, 0.5)\";\n\nexport const getOrientation = (puzzle: Puzzle): Color => {\n const fen = puzzle.fen;\n const game = new Chess(fen);\n if (puzzle.makeFirstMove) {\n game.move(puzzle.moves[0]);\n }\n return game.turn();\n};\n\ninterface ClickableElement extends ReactElement {\n props: {\n onClick?: () => void;\n };\n}\n\nexport const isClickableElement = (\n element: ReactNode,\n): element is ClickableElement => React.isValidElement(element);\n\nexport const getCustomSquareStyles = (\n status: Status,\n hint: Hint,\n isPlayerTurn: boolean,\n game: Chess,\n nextMove?: Move | null,\n) => {\n const customSquareStyles: Record<string, CSSProperties> = {};\n\n const lastMove = _.last(game.history({ verbose: true }));\n\n if (status === \"failed\" && lastMove) {\n customSquareStyles[lastMove.from] = {\n backgroundColor: FAIL_COLOR,\n };\n customSquareStyles[lastMove.to] = {\n backgroundColor: FAIL_COLOR,\n };\n }\n\n if (\n lastMove &&\n (status === \"solved\" || (status !== \"failed\" && !isPlayerTurn))\n ) {\n customSquareStyles[lastMove.from] = {\n backgroundColor: SUCCESS_COLOR,\n };\n customSquareStyles[lastMove.to] = {\n backgroundColor: SUCCESS_COLOR,\n };\n }\n\n if (hint === \"piece\") {\n if (nextMove) {\n customSquareStyles[nextMove.from] = {\n backgroundColor: HINT_COLOR,\n };\n }\n }\n\n if (hint === \"move\") {\n if (nextMove) {\n customSquareStyles[nextMove.from] = {\n backgroundColor: HINT_COLOR,\n };\n customSquareStyles[nextMove.to] = {\n backgroundColor: HINT_COLOR,\n };\n }\n }\n\n return customSquareStyles;\n};\n\nexport const stringToMove = (game: Chess, move: string | null | undefined) => {\n const copy = new Chess(game.fen());\n if (move === null || move === undefined) {\n return null;\n }\n try {\n return copy.move(move);\n } catch (e) {\n return null;\n }\n};\n","import { useEffect, useReducer } from \"react\";\nimport { initializePuzzle, reducer } from \"./reducer\";\nimport { getOrientation, type Puzzle, type Hint, type Status } from \"../utils\";\nimport { useChessGameContext } from \"@react-chess-tools/react-chess-game\";\nimport { useChessPuzzleContext } from \"./useChessPuzzleContext\";\n\nexport type ChessPuzzleContextType = {\n status: Status;\n changePuzzle: (puzzle: Puzzle) => void;\n puzzle: Puzzle;\n hint: Hint;\n nextMove?: string | null;\n isPlayerTurn: boolean;\n onHint: () => void;\n};\n\nexport const useChessPuzzle = (\n puzzle: Puzzle,\n onSolve?: (puzzleContext: ChessPuzzleContextType) => void,\n onFail?: (puzzleContext: ChessPuzzleContextType) => void,\n): ChessPuzzleContextType => {\n const gameContext = useChessGameContext();\n\n const [state, dispatch] = useReducer(reducer, { puzzle }, initializePuzzle);\n\n const {\n game,\n methods: { makeMove, setPosition },\n } = gameContext;\n\n useEffect(() => {\n if (gameContext && game.fen() !== puzzle.fen) {\n setPosition(puzzle.fen, getOrientation(puzzle));\n }\n }, []);\n\n const changePuzzle = (puzzle: Puzzle) => {\n dispatch({ type: \"INITIALIZE\", payload: { puzzle } });\n setPosition(puzzle.fen, getOrientation(puzzle));\n };\n\n useEffect(() => {\n if (gameContext && game.fen() === puzzle.fen && state.needCpuMove) {\n setTimeout(\n () =>\n dispatch({\n type: \"CPU_MOVE\",\n }),\n 0,\n );\n }\n }, [gameContext, state.needCpuMove]);\n\n useEffect(() => {\n if (state.cpuMove) {\n makeMove(state.cpuMove);\n }\n }, [state.cpuMove]);\n\n if (!gameContext) {\n throw new Error(\"useChessPuzzle must be used within a ChessGameContext\");\n }\n\n const onHint = () => {\n dispatch({ type: \"TOGGLE_HINT\" });\n };\n\n const puzzleContext: ChessPuzzleContextType = {\n status: state.status,\n changePuzzle,\n puzzle,\n hint: state.hint,\n onHint,\n nextMove: state.nextMove,\n isPlayerTurn: state.isPlayerTurn,\n };\n\n useEffect(() => {\n if (game?.history()?.length <= 0 + (puzzle.makeFirstMove ? 1 : 0)) {\n return;\n }\n if (game.history().length % 2 === (puzzle.makeFirstMove ? 0 : 1)) {\n dispatch({\n type: \"PLAYER_MOVE\",\n payload: {\n move: gameContext?.game?.history({ verbose: true })?.pop() ?? null,\n onSolve,\n onFail,\n puzzleContext,\n game: game,\n },\n });\n\n dispatch({\n type: \"CPU_MOVE\",\n });\n }\n }, [game?.history()?.length]);\n\n return puzzleContext;\n};\n","import { Chess, Move } from \"chess.js\";\nimport { type Puzzle, type Hint, type Status } from \"../utils\";\nimport { ChessPuzzleContextType } from \"./useChessPuzzle\";\n\nexport type State = {\n puzzle: Puzzle;\n currentMoveIndex: number;\n status: Status;\n cpuMove?: string | null;\n nextMove?: string | null;\n hint: Hint;\n needCpuMove: boolean;\n isPlayerTurn: boolean;\n};\n\nexport type Action =\n | {\n type: \"INITIALIZE\";\n payload: {\n puzzle: Puzzle;\n };\n }\n | {\n type: \"RESET\";\n }\n | { type: \"TOGGLE_HINT\" }\n | {\n type: \"CPU_MOVE\";\n }\n | {\n type: \"PLAYER_MOVE\";\n payload: {\n move?: Move | null;\n onSolve?: (puzzleContext: ChessPuzzleContextType) => void;\n onFail?: (puzzleContext: ChessPuzzleContextType) => void;\n puzzleContext: ChessPuzzleContextType;\n game: Chess;\n };\n };\n\nexport const initializePuzzle = ({ puzzle }: { puzzle: Puzzle }): State => {\n return {\n puzzle,\n currentMoveIndex: 0,\n status: \"not-started\",\n nextMove: puzzle.moves[0],\n hint: \"none\",\n cpuMove: null,\n needCpuMove: !!puzzle.makeFirstMove,\n isPlayerTurn: !puzzle.makeFirstMove,\n };\n};\n\nexport const reducer = (state: State, action: Action): State => {\n switch (action.type) {\n case \"INITIALIZE\":\n return {\n ...state,\n ...initializePuzzle(action.payload),\n };\n case \"RESET\":\n return {\n ...state,\n ...initializePuzzle({\n puzzle: state.puzzle,\n }),\n };\n case \"TOGGLE_HINT\":\n if (state.hint === \"none\") {\n return { ...state, hint: \"piece\" };\n }\n return { ...state, hint: \"move\" };\n case \"CPU_MOVE\":\n if (state.isPlayerTurn) {\n return state;\n }\n if ([\"solved\", \"failed\"].includes(state.status)) {\n return state;\n }\n\n return {\n ...state,\n currentMoveIndex: state.currentMoveIndex + 1,\n cpuMove: state.puzzle.moves[state.currentMoveIndex],\n nextMove:\n state.currentMoveIndex < state.puzzle.moves.length - 1\n ? state.puzzle.moves[state.currentMoveIndex + 1]\n : null,\n needCpuMove: false,\n isPlayerTurn: true,\n status: \"in-progress\",\n };\n\n case \"PLAYER_MOVE\": {\n const { move, onSolve, onFail, puzzleContext } = action.payload;\n\n const isMoveRight = [move?.san, move?.lan].includes(\n state?.nextMove || \"\",\n );\n const isPuzzleSolved =\n state.currentMoveIndex === state.puzzle.moves.length - 1;\n\n if (!isMoveRight) {\n if (onFail) {\n onFail(puzzleContext);\n }\n return {\n ...state,\n status: \"failed\",\n nextMove: null,\n hint: \"none\",\n isPlayerTurn: false,\n };\n }\n\n if (isPuzzleSolved) {\n if (onSolve) {\n onSolve(puzzleContext);\n }\n\n return {\n ...state,\n status: \"solved\",\n nextMove: null,\n hint: \"none\",\n isPlayerTurn: false,\n };\n }\n\n return {\n ...state,\n hint: \"none\",\n currentMoveIndex: state.currentMoveIndex + 1,\n nextMove: state.puzzle.moves[state.currentMoveIndex + 1],\n status: \"in-progress\",\n needCpuMove: true,\n isPlayerTurn: false,\n };\n }\n\n default:\n return state;\n }\n};\n","import React from \"react\";\nimport { useChessPuzzle } from \"./useChessPuzzle\";\n\nexport const ChessPuzzleContext = React.createContext<ReturnType<\n typeof useChessPuzzle\n> | null>(null);\n\nexport const useChessPuzzleContext = () => {\n const context = React.useContext(ChessPuzzleContext);\n if (!context) {\n throw new Error(\n \"useChessGameContext must be used within a ChessGameProvider\",\n );\n }\n return context;\n};\n","import React from \"react\";\nimport {\n ChessGame,\n useChessGameContext,\n} from \"@react-chess-tools/react-chess-game\";\nimport { getCustomSquareStyles, stringToMove } from \"../../../utils\";\nimport { useChessPuzzleContext } from \"../../..\";\n\nexport interface PuzzleBoardProps\n extends React.ComponentProps<typeof ChessGame.Board> {}\nexport const PuzzleBoard: React.FC<PuzzleBoardProps> = ({ ...rest }) => {\n const puzzleContext = useChessPuzzleContext();\n const gameContext = useChessGameContext();\n\n if (!puzzleContext) {\n throw new Error(\"PuzzleContext not found\");\n }\n if (!gameContext) {\n throw new Error(\"ChessGameContext not found\");\n }\n\n const { game } = gameContext;\n const { status, hint, isPlayerTurn, nextMove } = puzzleContext;\n\n return (\n <ChessGame.Board\n customSquareStyles={getCustomSquareStyles(\n status,\n hint,\n isPlayerTurn,\n game,\n stringToMove(game, nextMove),\n )}\n {...rest}\n />\n );\n};\n","import React from \"react\";\nimport { isClickableElement, type Puzzle, type Status } from \"../../../utils\";\nimport { useChessPuzzleContext } from \"../../..\";\n\nexport interface ResetProps {\n asChild?: boolean;\n puzzle?: Puzzle;\n onReset?: () => void;\n showOn?: Status[];\n}\n\nconst defaultShowOn: Status[] = [\"failed\", \"solved\"];\n\nexport const Reset: React.FC<React.PropsWithChildren<ResetProps>> = ({\n children,\n asChild,\n puzzle,\n onReset,\n showOn = defaultShowOn,\n}) => {\n const puzzleContext = useChessPuzzleContext();\n if (!puzzleContext) {\n throw new Error(\"PuzzleContext not found\");\n }\n const { changePuzzle, status } = puzzleContext;\n const handleClick = () => {\n changePuzzle(puzzle || puzzleContext.puzzle);\n onReset?.();\n };\n\n if (!showOn.includes(status)) {\n return null;\n }\n\n if (asChild) {\n const child = React.Children.only(children);\n if (isClickableElement(child)) {\n return React.cloneElement(child, {\n onClick: handleClick,\n });\n } else {\n throw new Error(\"Change child must be a clickable element\");\n }\n }\n\n return (\n <button type=\"button\" onClick={handleClick}>\n {children}\n </button>\n );\n};\n","import React from \"react\";\nimport { Status, isClickableElement } from \"../../../utils\";\nimport { useChessPuzzleContext } from \"../../..\";\n\nexport interface HintProps {\n asChild?: boolean;\n showOn?: Status[];\n}\n\nconst defaultShowOn: Status[] = [\"not-started\", \"in-progress\"];\n\nexport const Hint: React.FC<React.PropsWithChildren<HintProps>> = ({\n children,\n asChild,\n showOn = defaultShowOn,\n}) => {\n const puzzleContext = useChessPuzzleContext();\n if (!puzzleContext) {\n throw new Error(\"PuzzleContext not found\");\n }\n const { onHint, status } = puzzleContext;\n const handleClick = () => {\n onHint();\n };\n\n if (!showOn.includes(status)) {\n return null;\n }\n\n if (asChild) {\n const child = React.Children.only(children);\n if (isClickableElement(child)) {\n return React.cloneElement(child, {\n onClick: handleClick,\n });\n } else {\n throw new Error(\"Change child must be a clickable element\");\n }\n }\n\n return (\n <button type=\"button\" onClick={handleClick}>\n {children}\n </button>\n );\n};\n","import { Root } from \"./parts/Root\";\nimport { PuzzleBoard } from \"./parts/PuzzleBoard\";\nimport { Reset } from \"./parts/Reset\";\nimport { Hint } from \"./parts/Hint\";\n\nexport const ChessPuzzle = {\n Root,\n Board: PuzzleBoard,\n Reset,\n Hint,\n};\n"],"mappings":";AAAA,OAAOA,YAAW;;;ACAlB,SAAqB,aAAmB;AACxC,OAAO,WAAuD;AAC9D,OAAO,OAAO;AAad,IAAM,aAAa;AACnB,IAAM,gBAAgB;AACtB,IAAM,aAAa;AAEZ,IAAM,iBAAiB,CAAC,WAA0B;AACvD,QAAM,MAAM,OAAO;AACnB,QAAM,OAAO,IAAI,MAAM,GAAG;AAC1B,MAAI,OAAO,eAAe;AACxB,SAAK,KAAK,OAAO,MAAM,CAAC,CAAC;AAAA,EAC3B;AACA,SAAO,KAAK,KAAK;AACnB;AAQO,IAAM,qBAAqB,CAChC,YACgC,MAAM,eAAe,OAAO;AAEvD,IAAM,wBAAwB,CACnC,QACA,MACA,cACA,MACA,aACG;AACH,QAAM,qBAAoD,CAAC;AAE3D,QAAM,WAAW,EAAE,KAAK,KAAK,QAAQ,EAAE,SAAS,KAAK,CAAC,CAAC;AAEvD,MAAI,WAAW,YAAY,UAAU;AACnC,uBAAmB,SAAS,IAAI,IAAI;AAAA,MAClC,iBAAiB;AAAA,IACnB;AACA,uBAAmB,SAAS,EAAE,IAAI;AAAA,MAChC,iBAAiB;AAAA,IACnB;AAAA,EACF;AAEA,MACE,aACC,WAAW,YAAa,WAAW,YAAY,CAAC,eACjD;AACA,uBAAmB,SAAS,IAAI,IAAI;AAAA,MAClC,iBAAiB;AAAA,IACnB;AACA,uBAAmB,SAAS,EAAE,IAAI;AAAA,MAChC,iBAAiB;AAAA,IACnB;AAAA,EACF;AAEA,MAAI,SAAS,SAAS;AACpB,QAAI,UAAU;AACZ,yBAAmB,SAAS,IAAI,IAAI;AAAA,QAClC,iBAAiB;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAEA,MAAI,SAAS,QAAQ;AACnB,QAAI,UAAU;AACZ,yBAAmB,SAAS,IAAI,IAAI;AAAA,QAClC,iBAAiB;AAAA,MACnB;AACA,yBAAmB,SAAS,EAAE,IAAI;AAAA,QAChC,iBAAiB;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEO,IAAM,eAAe,CAAC,MAAa,SAAoC;AAC5E,QAAM,OAAO,IAAI,MAAM,KAAK,IAAI,CAAC;AACjC,MAAI,SAAS,QAAQ,SAAS,QAAW;AACvC,WAAO;AAAA,EACT;AACA,MAAI;AACF,WAAO,KAAK,KAAK,IAAI;AAAA,EACvB,SAAS,GAAG;AACV,WAAO;AAAA,EACT;AACF;;;ACtGA,SAAS,WAAW,kBAAkB;;;ACwC/B,IAAM,mBAAmB,CAAC,EAAE,OAAO,MAAiC;AACzE,SAAO;AAAA,IACL;AAAA,IACA,kBAAkB;AAAA,IAClB,QAAQ;AAAA,IACR,UAAU,OAAO,MAAM,CAAC;AAAA,IACxB,MAAM;AAAA,IACN,SAAS;AAAA,IACT,aAAa,CAAC,CAAC,OAAO;AAAA,IACtB,cAAc,CAAC,OAAO;AAAA,EACxB;AACF;AAEO,IAAM,UAAU,CAAC,OAAc,WAA0B;AAC9D,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK;AACH,aAAO;AAAA,QACL,GAAG;AAAA,QACH,GAAG,iBAAiB,OAAO,OAAO;AAAA,MACpC;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,GAAG;AAAA,QACH,GAAG,iBAAiB;AAAA,UAClB,QAAQ,MAAM;AAAA,QAChB,CAAC;AAAA,MACH;AAAA,IACF,KAAK;AACH,UAAI,MAAM,SAAS,QAAQ;AACzB,eAAO,EAAE,GAAG,OAAO,MAAM,QAAQ;AAAA,MACnC;AACA,aAAO,EAAE,GAAG,OAAO,MAAM,OAAO;AAAA,IAClC,KAAK;AACH,UAAI,MAAM,cAAc;AACtB,eAAO;AAAA,MACT;AACA,UAAI,CAAC,UAAU,QAAQ,EAAE,SAAS,MAAM,MAAM,GAAG;AAC/C,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,QACL,GAAG;AAAA,QACH,kBAAkB,MAAM,mBAAmB;AAAA,QAC3C,SAAS,MAAM,OAAO,MAAM,MAAM,gBAAgB;AAAA,QAClD,UACE,MAAM,mBAAmB,MAAM,OAAO,MAAM,SAAS,IACjD,MAAM,OAAO,MAAM,MAAM,mBAAmB,CAAC,IAC7C;AAAA,QACN,aAAa;AAAA,QACb,cAAc;AAAA,QACd,QAAQ;AAAA,MACV;AAAA,IAEF,KAAK,eAAe;AAClB,YAAM,EAAE,MAAM,SAAS,QAAQ,cAAc,IAAI,OAAO;AAExD,YAAM,cAAc,CAAC,6BAAM,KAAK,6BAAM,GAAG,EAAE;AAAA,SACzC,+BAAO,aAAY;AAAA,MACrB;AACA,YAAM,iBACJ,MAAM,qBAAqB,MAAM,OAAO,MAAM,SAAS;AAEzD,UAAI,CAAC,aAAa;AAChB,YAAI,QAAQ;AACV,iBAAO,aAAa;AAAA,QACtB;AACA,eAAO;AAAA,UACL,GAAG;AAAA,UACH,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,MAAM;AAAA,UACN,cAAc;AAAA,QAChB;AAAA,MACF;AAEA,UAAI,gBAAgB;AAClB,YAAI,SAAS;AACX,kBAAQ,aAAa;AAAA,QACvB;AAEA,eAAO;AAAA,UACL,GAAG;AAAA,UACH,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,MAAM;AAAA,UACN,cAAc;AAAA,QAChB;AAAA,MACF;AAEA,aAAO;AAAA,QACL,GAAG;AAAA,QACH,MAAM;AAAA,QACN,kBAAkB,MAAM,mBAAmB;AAAA,QAC3C,UAAU,MAAM,OAAO,MAAM,MAAM,mBAAmB,CAAC;AAAA,QACvD,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,cAAc;AAAA,MAChB;AAAA,IACF;AAAA,IAEA;AACE,aAAO;AAAA,EACX;AACF;;;AD5IA,SAAS,2BAA2B;AAa7B,IAAM,iBAAiB,CAC5B,QACA,SACA,WAC2B;AApB7B;AAqBE,QAAM,cAAc,oBAAoB;AAExC,QAAM,CAAC,OAAO,QAAQ,IAAI,WAAW,SAAS,EAAE,OAAO,GAAG,gBAAgB;AAE1E,QAAM;AAAA,IACJ;AAAA,IACA,SAAS,EAAE,UAAU,YAAY;AAAA,EACnC,IAAI;AAEJ,YAAU,MAAM;AACd,QAAI,eAAe,KAAK,IAAI,MAAM,OAAO,KAAK;AAC5C,kBAAY,OAAO,KAAK,eAAe,MAAM,CAAC;AAAA,IAChD;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,eAAe,CAACC,YAAmB;AACvC,aAAS,EAAE,MAAM,cAAc,SAAS,EAAE,QAAAA,QAAO,EAAE,CAAC;AACpD,gBAAYA,QAAO,KAAK,eAAeA,OAAM,CAAC;AAAA,EAChD;AAEA,YAAU,MAAM;AACd,QAAI,eAAe,KAAK,IAAI,MAAM,OAAO,OAAO,MAAM,aAAa;AACjE;AAAA,QACE,MACE,SAAS;AAAA,UACP,MAAM;AAAA,QACR,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG,CAAC,aAAa,MAAM,WAAW,CAAC;AAEnC,YAAU,MAAM;AACd,QAAI,MAAM,SAAS;AACjB,eAAS,MAAM,OAAO;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,MAAM,OAAO,CAAC;AAElB,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM,uDAAuD;AAAA,EACzE;AAEA,QAAM,SAAS,MAAM;AACnB,aAAS,EAAE,MAAM,cAAc,CAAC;AAAA,EAClC;AAEA,QAAM,gBAAwC;AAAA,IAC5C,QAAQ,MAAM;AAAA,IACd;AAAA,IACA;AAAA,IACA,MAAM,MAAM;AAAA,IACZ;AAAA,IACA,UAAU,MAAM;AAAA,IAChB,cAAc,MAAM;AAAA,EACtB;AAEA,YAAU,MAAM;AA7ElB,QAAAC,KAAA;AA8EI,UAAIA,MAAA,6BAAM,cAAN,gBAAAA,IAAiB,WAAU,KAAK,OAAO,gBAAgB,IAAI,IAAI;AACjE;AAAA,IACF;AACA,QAAI,KAAK,QAAQ,EAAE,SAAS,OAAO,OAAO,gBAAgB,IAAI,IAAI;AAChE,eAAS;AAAA,QACP,MAAM;AAAA,QACN,SAAS;AAAA,UACP,QAAM,sDAAa,SAAb,mBAAmB,QAAQ,EAAE,SAAS,KAAK,OAA3C,mBAA+C,UAAS;AAAA,UAC9D;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF,CAAC;AAED,eAAS;AAAA,QACP,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAAA,EACF,GAAG,EAAC,kCAAM,cAAN,mBAAiB,MAAM,CAAC;AAE5B,SAAO;AACT;;;AF9FA,SAAS,iBAAiB;;;AIN1B,OAAOC,YAAW;AAGX,IAAM,qBAAqBA,OAAM,cAE9B,IAAI;AAEP,IAAM,wBAAwB,MAAM;AACzC,QAAM,UAAUA,OAAM,WAAW,kBAAkB;AACnD,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;AJAA,IAAM,aAA2D,CAAC;AAAA,EAChE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,UAAU,eAAe,QAAQ,SAAS,MAAM;AAEtD,SACE,gBAAAC,OAAA,cAAC,mBAAmB,UAAnB,EAA4B,OAAO,WACjC,QACH;AAEJ;AAEO,IAAM,OAAqD,CAAC;AAAA,EACjE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,SACE,gBAAAA,OAAA,cAAC,UAAU,MAAV,EAAe,KAAK,OAAO,KAAK,aAAa,eAAe,MAAM,KACjE,gBAAAA,OAAA,cAAC,cAAW,QAAgB,SAAkB,UAC3C,QACH,CACF;AAEJ;;;AK3CA,OAAOC,YAAW;AAClB;AAAA,EACE,aAAAC;AAAA,EACA,uBAAAC;AAAA,OACK;AAMA,IAAM,cAA0C,CAAC,EAAE,GAAG,KAAK,MAAM;AACtE,QAAM,gBAAgB,sBAAsB;AAC5C,QAAM,cAAcC,qBAAoB;AAExC,MAAI,CAAC,eAAe;AAClB,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AACA,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAC9C;AAEA,QAAM,EAAE,KAAK,IAAI;AACjB,QAAM,EAAE,QAAQ,MAAM,cAAc,SAAS,IAAI;AAEjD,SACE,gBAAAC,OAAA;AAAA,IAACC,WAAU;AAAA,IAAV;AAAA,MACC,oBAAoB;AAAA,QAClB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,aAAa,MAAM,QAAQ;AAAA,MAC7B;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;;;ACpCA,OAAOC,YAAW;AAWlB,IAAM,gBAA0B,CAAC,UAAU,QAAQ;AAE5C,IAAM,QAAuD,CAAC;AAAA,EACnE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AACX,MAAM;AACJ,QAAM,gBAAgB,sBAAsB;AAC5C,MAAI,CAAC,eAAe;AAClB,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AACA,QAAM,EAAE,cAAc,OAAO,IAAI;AACjC,QAAM,cAAc,MAAM;AACxB,iBAAa,UAAU,cAAc,MAAM;AAC3C;AAAA,EACF;AAEA,MAAI,CAAC,OAAO,SAAS,MAAM,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,MAAI,SAAS;AACX,UAAM,QAAQC,OAAM,SAAS,KAAK,QAAQ;AAC1C,QAAI,mBAAmB,KAAK,GAAG;AAC7B,aAAOA,OAAM,aAAa,OAAO;AAAA,QAC/B,SAAS;AAAA,MACX,CAAC;AAAA,IACH,OAAO;AACL,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AAAA,EACF;AAEA,SACE,gBAAAA,OAAA,cAAC,YAAO,MAAK,UAAS,SAAS,eAC5B,QACH;AAEJ;;;AClDA,OAAOC,YAAW;AASlB,IAAMC,iBAA0B,CAAC,eAAe,aAAa;AAEtD,IAAM,OAAqD,CAAC;AAAA,EACjE;AAAA,EACA;AAAA,EACA,SAASA;AACX,MAAM;AACJ,QAAM,gBAAgB,sBAAsB;AAC5C,MAAI,CAAC,eAAe;AAClB,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AACA,QAAM,EAAE,QAAQ,OAAO,IAAI;AAC3B,QAAM,cAAc,MAAM;AACxB,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,OAAO,SAAS,MAAM,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,MAAI,SAAS;AACX,UAAM,QAAQC,OAAM,SAAS,KAAK,QAAQ;AAC1C,QAAI,mBAAmB,KAAK,GAAG;AAC7B,aAAOA,OAAM,aAAa,OAAO;AAAA,QAC/B,SAAS;AAAA,MACX,CAAC;AAAA,IACH,OAAO;AACL,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AAAA,EACF;AAEA,SACE,gBAAAA,OAAA,cAAC,YAAO,MAAK,UAAS,SAAS,eAC5B,QACH;AAEJ;;;ACxCO,IAAM,cAAc;AAAA,EACzB;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AACF;","names":["React","puzzle","_a","React","React","React","ChessGame","useChessGameContext","useChessGameContext","React","ChessGame","React","React","React","defaultShowOn","React"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-chess-tools/react-chess-puzzle",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "A lightweight, customizable React component library for rendering and interacting with chess puzzles.",
5
5
  "main": "dist/index.mjs",
6
6
  "module": "dist/index.mjs",
@@ -1,13 +1,16 @@
1
1
  import React from "react";
2
2
  import { Puzzle, getOrientation } from "../../../utils";
3
- import { useChessPuzzle } from "../../../hooks/useChessPuzzle";
3
+ import {
4
+ ChessPuzzleContextType,
5
+ useChessPuzzle,
6
+ } from "../../../hooks/useChessPuzzle";
4
7
  import { ChessGame } from "@react-chess-tools/react-chess-game";
5
8
  import { ChessPuzzleContext } from "../../../hooks/useChessPuzzleContext";
6
9
 
7
10
  export interface RootProps {
8
11
  puzzle: Puzzle;
9
- onSolve?: (changePuzzle: (puzzle: Puzzle) => void) => void;
10
- onFail?: (changePuzzle: (puzzle: Puzzle) => void) => void;
12
+ onSolve?: (puzzleContext: ChessPuzzleContextType) => void;
13
+ onFail?: (puzzleContext: ChessPuzzleContextType) => void;
11
14
  }
12
15
 
13
16
  const PuzzleRoot: React.FC<React.PropsWithChildren<RootProps>> = ({
@@ -1,5 +1,6 @@
1
1
  import { Chess, Move } from "chess.js";
2
2
  import { type Puzzle, type Hint, type Status } from "../utils";
3
+ import { ChessPuzzleContextType } from "./useChessPuzzle";
3
4
 
4
5
  export type State = {
5
6
  puzzle: Puzzle;
@@ -30,9 +31,9 @@ export type Action =
30
31
  type: "PLAYER_MOVE";
31
32
  payload: {
32
33
  move?: Move | null;
33
- onSolve?: (changePuzzle: (puzzle: Puzzle) => void) => void;
34
- onFail?: (changePuzzle: (puzzle: Puzzle) => void) => void;
35
- changePuzzle: (puzzle: Puzzle) => void;
34
+ onSolve?: (puzzleContext: ChessPuzzleContextType) => void;
35
+ onFail?: (puzzleContext: ChessPuzzleContextType) => void;
36
+ puzzleContext: ChessPuzzleContextType;
36
37
  game: Chess;
37
38
  };
38
39
  };
@@ -91,7 +92,7 @@ export const reducer = (state: State, action: Action): State => {
91
92
  };
92
93
 
93
94
  case "PLAYER_MOVE": {
94
- const { move, onSolve, onFail, changePuzzle } = action.payload;
95
+ const { move, onSolve, onFail, puzzleContext } = action.payload;
95
96
 
96
97
  const isMoveRight = [move?.san, move?.lan].includes(
97
98
  state?.nextMove || "",
@@ -101,7 +102,7 @@ export const reducer = (state: State, action: Action): State => {
101
102
 
102
103
  if (!isMoveRight) {
103
104
  if (onFail) {
104
- onFail(changePuzzle);
105
+ onFail(puzzleContext);
105
106
  }
106
107
  return {
107
108
  ...state,
@@ -114,7 +115,7 @@ export const reducer = (state: State, action: Action): State => {
114
115
 
115
116
  if (isPuzzleSolved) {
116
117
  if (onSolve) {
117
- onSolve(changePuzzle);
118
+ onSolve(puzzleContext);
118
119
  }
119
120
 
120
121
  return {
@@ -1,13 +1,24 @@
1
1
  import { useEffect, useReducer } from "react";
2
2
  import { initializePuzzle, reducer } from "./reducer";
3
- import { getOrientation, type Puzzle } from "../utils";
3
+ import { getOrientation, type Puzzle, type Hint, type Status } from "../utils";
4
4
  import { useChessGameContext } from "@react-chess-tools/react-chess-game";
5
+ import { useChessPuzzleContext } from "./useChessPuzzleContext";
6
+
7
+ export type ChessPuzzleContextType = {
8
+ status: Status;
9
+ changePuzzle: (puzzle: Puzzle) => void;
10
+ puzzle: Puzzle;
11
+ hint: Hint;
12
+ nextMove?: string | null;
13
+ isPlayerTurn: boolean;
14
+ onHint: () => void;
15
+ };
5
16
 
6
17
  export const useChessPuzzle = (
7
18
  puzzle: Puzzle,
8
- onSolve?: (changePuzzle: (puzzle: Puzzle) => void) => void,
9
- onFail?: (changePuzzle: (puzzle: Puzzle) => void) => void,
10
- ) => {
19
+ onSolve?: (puzzleContext: ChessPuzzleContextType) => void,
20
+ onFail?: (puzzleContext: ChessPuzzleContextType) => void,
21
+ ): ChessPuzzleContextType => {
11
22
  const gameContext = useChessGameContext();
12
23
 
13
24
  const [state, dispatch] = useReducer(reducer, { puzzle }, initializePuzzle);
@@ -46,6 +57,24 @@ export const useChessPuzzle = (
46
57
  }
47
58
  }, [state.cpuMove]);
48
59
 
60
+ if (!gameContext) {
61
+ throw new Error("useChessPuzzle must be used within a ChessGameContext");
62
+ }
63
+
64
+ const onHint = () => {
65
+ dispatch({ type: "TOGGLE_HINT" });
66
+ };
67
+
68
+ const puzzleContext: ChessPuzzleContextType = {
69
+ status: state.status,
70
+ changePuzzle,
71
+ puzzle,
72
+ hint: state.hint,
73
+ onHint,
74
+ nextMove: state.nextMove,
75
+ isPlayerTurn: state.isPlayerTurn,
76
+ };
77
+
49
78
  useEffect(() => {
50
79
  if (game?.history()?.length <= 0 + (puzzle.makeFirstMove ? 1 : 0)) {
51
80
  return;
@@ -57,7 +86,7 @@ export const useChessPuzzle = (
57
86
  move: gameContext?.game?.history({ verbose: true })?.pop() ?? null,
58
87
  onSolve,
59
88
  onFail,
60
- changePuzzle,
89
+ puzzleContext,
61
90
  game: game,
62
91
  },
63
92
  });
@@ -68,21 +97,5 @@ export const useChessPuzzle = (
68
97
  }
69
98
  }, [game?.history()?.length]);
70
99
 
71
- if (!gameContext) {
72
- throw new Error("useChessPuzzle must be used within a ChessGameContext");
73
- }
74
-
75
- const onHint = () => {
76
- dispatch({ type: "TOGGLE_HINT" });
77
- };
78
-
79
- return {
80
- status: state.status,
81
- changePuzzle,
82
- puzzle,
83
- hint: state.hint,
84
- onHint,
85
- nextMove: state.nextMove,
86
- isPlayerTurn: state.isPlayerTurn,
87
- };
100
+ return puzzleContext;
88
101
  };