@sudobility/sudojo_lib 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/dist/hooks/index.d.ts +27 -0
  2. package/dist/hooks/index.d.ts.map +1 -0
  3. package/dist/hooks/index.js +27 -0
  4. package/dist/hooks/index.js.map +1 -0
  5. package/dist/hooks/useDailyGame.d.ts +64 -0
  6. package/dist/hooks/useDailyGame.d.ts.map +1 -0
  7. package/dist/hooks/useDailyGame.js +120 -0
  8. package/dist/hooks/useDailyGame.js.map +1 -0
  9. package/dist/hooks/useGame.d.ts +104 -0
  10. package/dist/hooks/useGame.d.ts.map +1 -0
  11. package/dist/hooks/useGame.js +546 -0
  12. package/dist/hooks/useGame.js.map +1 -0
  13. package/dist/hooks/useGamePersistence.d.ts +63 -0
  14. package/dist/hooks/useGamePersistence.d.ts.map +1 -0
  15. package/dist/hooks/useGamePersistence.js +124 -0
  16. package/dist/hooks/useGamePersistence.js.map +1 -0
  17. package/dist/hooks/useGameTeaching.d.ts +89 -0
  18. package/dist/hooks/useGameTeaching.d.ts.map +1 -0
  19. package/dist/hooks/useGameTeaching.js +230 -0
  20. package/dist/hooks/useGameTeaching.js.map +1 -0
  21. package/dist/hooks/useGameTimer.d.ts +43 -0
  22. package/dist/hooks/useGameTimer.d.ts.map +1 -0
  23. package/dist/hooks/useGameTimer.js +68 -0
  24. package/dist/hooks/useGameTimer.js.map +1 -0
  25. package/dist/hooks/useHint.d.ts +105 -0
  26. package/dist/hooks/useHint.d.ts.map +1 -0
  27. package/dist/hooks/useHint.js +179 -0
  28. package/dist/hooks/useHint.js.map +1 -0
  29. package/dist/hooks/useLearning.d.ts +100 -0
  30. package/dist/hooks/useLearning.d.ts.map +1 -0
  31. package/dist/hooks/useLearning.js +127 -0
  32. package/dist/hooks/useLearning.js.map +1 -0
  33. package/dist/hooks/useLevelGame.d.ts +68 -0
  34. package/dist/hooks/useLevelGame.d.ts.map +1 -0
  35. package/dist/hooks/useLevelGame.js +128 -0
  36. package/dist/hooks/useLevelGame.js.map +1 -0
  37. package/dist/hooks/useLevels.d.ts +93 -0
  38. package/dist/hooks/useLevels.d.ts.map +1 -0
  39. package/dist/hooks/useLevels.js +98 -0
  40. package/dist/hooks/useLevels.js.map +1 -0
  41. package/dist/hooks/useLocalStorage.d.ts +26 -0
  42. package/dist/hooks/useLocalStorage.d.ts.map +1 -0
  43. package/dist/hooks/useLocalStorage.js +75 -0
  44. package/dist/hooks/useLocalStorage.js.map +1 -0
  45. package/dist/hooks/useSudoku.d.ts +125 -0
  46. package/dist/hooks/useSudoku.d.ts.map +1 -0
  47. package/dist/hooks/useSudoku.js +655 -0
  48. package/dist/hooks/useSudoku.js.map +1 -0
  49. package/dist/hooks/useTechniques.d.ts +92 -0
  50. package/dist/hooks/useTechniques.d.ts.map +1 -0
  51. package/dist/hooks/useTechniques.js +108 -0
  52. package/dist/hooks/useTechniques.js.map +1 -0
  53. package/dist/index.d.ts +42 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/index.js +64 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/test/setup.d.ts +2 -0
  58. package/dist/test/setup.d.ts.map +1 -0
  59. package/dist/test/setup.js +35 -0
  60. package/dist/test/setup.js.map +1 -0
  61. package/dist/types/display.d.ts +142 -0
  62. package/dist/types/display.d.ts.map +1 -0
  63. package/dist/types/display.js +94 -0
  64. package/dist/types/display.js.map +1 -0
  65. package/dist/types/game.d.ts +201 -0
  66. package/dist/types/game.d.ts.map +1 -0
  67. package/dist/types/game.js +26 -0
  68. package/dist/types/game.js.map +1 -0
  69. package/dist/types/gamePersistence.d.ts +31 -0
  70. package/dist/types/gamePersistence.d.ts.map +1 -0
  71. package/dist/types/gamePersistence.js +15 -0
  72. package/dist/types/gamePersistence.js.map +1 -0
  73. package/dist/types/index.d.ts +18 -0
  74. package/dist/types/index.d.ts.map +1 -0
  75. package/dist/types/index.js +11 -0
  76. package/dist/types/index.js.map +1 -0
  77. package/dist/types/progress.d.ts +43 -0
  78. package/dist/types/progress.d.ts.map +1 -0
  79. package/dist/types/progress.js +19 -0
  80. package/dist/types/progress.js.map +1 -0
  81. package/dist/types/settings.d.ts +19 -0
  82. package/dist/types/settings.d.ts.map +1 -0
  83. package/dist/types/settings.js +12 -0
  84. package/dist/types/settings.js.map +1 -0
  85. package/dist/types/subscription.d.ts +27 -0
  86. package/dist/types/subscription.d.ts.map +1 -0
  87. package/dist/types/subscription.js +8 -0
  88. package/dist/types/subscription.js.map +1 -0
  89. package/dist/types/sudoku.d.ts +147 -0
  90. package/dist/types/sudoku.d.ts.map +1 -0
  91. package/dist/types/sudoku.js +125 -0
  92. package/dist/types/sudoku.js.map +1 -0
  93. package/dist/utils/auth.d.ts +25 -0
  94. package/dist/utils/auth.d.ts.map +1 -0
  95. package/dist/utils/auth.js +47 -0
  96. package/dist/utils/auth.js.map +1 -0
  97. package/dist/utils/board.d.ts +116 -0
  98. package/dist/utils/board.d.ts.map +1 -0
  99. package/dist/utils/board.js +291 -0
  100. package/dist/utils/board.js.map +1 -0
  101. package/dist/utils/index.d.ts +18 -0
  102. package/dist/utils/index.d.ts.map +1 -0
  103. package/dist/utils/index.js +23 -0
  104. package/dist/utils/index.js.map +1 -0
  105. package/dist/utils/progress.d.ts +44 -0
  106. package/dist/utils/progress.d.ts.map +1 -0
  107. package/dist/utils/progress.js +104 -0
  108. package/dist/utils/progress.js.map +1 -0
  109. package/dist/utils/scramble.d.ts +35 -0
  110. package/dist/utils/scramble.d.ts.map +1 -0
  111. package/dist/utils/scramble.js +270 -0
  112. package/dist/utils/scramble.js.map +1 -0
  113. package/dist/utils/subscription.d.ts +82 -0
  114. package/dist/utils/subscription.d.ts.map +1 -0
  115. package/dist/utils/subscription.js +117 -0
  116. package/dist/utils/subscription.js.map +1 -0
  117. package/dist/utils/sudokuPresenter.d.ts +62 -0
  118. package/dist/utils/sudokuPresenter.d.ts.map +1 -0
  119. package/dist/utils/sudokuPresenter.js +403 -0
  120. package/dist/utils/sudokuPresenter.js.map +1 -0
  121. package/dist/utils/sudokuScrambler.d.ts +61 -0
  122. package/dist/utils/sudokuScrambler.d.ts.map +1 -0
  123. package/dist/utils/sudokuScrambler.js +223 -0
  124. package/dist/utils/sudokuScrambler.js.map +1 -0
  125. package/dist/utils/technique.d.ts +41 -0
  126. package/dist/utils/technique.d.ts.map +1 -0
  127. package/dist/utils/technique.js +106 -0
  128. package/dist/utils/technique.js.map +1 -0
  129. package/dist/utils/theme.d.ts +31 -0
  130. package/dist/utils/theme.d.ts.map +1 -0
  131. package/dist/utils/theme.js +40 -0
  132. package/dist/utils/theme.js.map +1 -0
  133. package/dist/utils/time.d.ts +22 -0
  134. package/dist/utils/time.d.ts.map +1 -0
  135. package/dist/utils/time.js +41 -0
  136. package/dist/utils/time.js.map +1 -0
  137. package/dist/utils/validation.d.ts +107 -0
  138. package/dist/utils/validation.d.ts.map +1 -0
  139. package/dist/utils/validation.js +248 -0
  140. package/dist/utils/validation.js.map +1 -0
  141. package/package.json +79 -0
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Hook for persisting game state to localStorage
3
+ *
4
+ * Saves game progress automatically so users can resume where they left off
5
+ */
6
+ import { useCallback, useEffect, useRef } from 'react';
7
+ import { getGameStorageKey } from '../types/gamePersistence';
8
+ /**
9
+ * Hook for persisting individual game progress
10
+ *
11
+ * @param options - Hook options
12
+ * @returns Functions to load, save, and clear game state
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * const { loadGame, saveGame, clearGame } = useGamePersistence({
17
+ * puzzleKey: { type: 'daily', id: '2024-01-15' },
18
+ * autoSave: true,
19
+ * });
20
+ *
21
+ * // On mount, try to restore saved state
22
+ * useEffect(() => {
23
+ * const saved = loadGame();
24
+ * if (saved) {
25
+ * // Restore game state
26
+ * }
27
+ * }, []);
28
+ *
29
+ * // Save after each move
30
+ * useEffect(() => {
31
+ * saveGame({ inputString, pencilmarksString, isPencilMode });
32
+ * }, [inputString, pencilmarksString, isPencilMode]);
33
+ * ```
34
+ */
35
+ export function useGamePersistence({ puzzleKey, }) {
36
+ const storageKey = puzzleKey ? getGameStorageKey(puzzleKey) : null;
37
+ const loadGame = useCallback(() => {
38
+ if (!storageKey)
39
+ return null;
40
+ try {
41
+ const item = window.localStorage.getItem(storageKey);
42
+ if (!item)
43
+ return null;
44
+ return JSON.parse(item);
45
+ }
46
+ catch (error) {
47
+ console.warn('Error loading saved game:', error);
48
+ return null;
49
+ }
50
+ }, [storageKey]);
51
+ const saveGame = useCallback((state) => {
52
+ if (!storageKey)
53
+ return;
54
+ try {
55
+ const savedState = {
56
+ ...state,
57
+ savedAt: new Date().toISOString(),
58
+ };
59
+ window.localStorage.setItem(storageKey, JSON.stringify(savedState));
60
+ }
61
+ catch (error) {
62
+ console.warn('Error saving game:', error);
63
+ }
64
+ }, [storageKey]);
65
+ const clearGame = useCallback(() => {
66
+ if (!storageKey)
67
+ return;
68
+ try {
69
+ window.localStorage.removeItem(storageKey);
70
+ }
71
+ catch (error) {
72
+ console.warn('Error clearing saved game:', error);
73
+ }
74
+ }, [storageKey]);
75
+ const hasSavedGame = useCallback(() => {
76
+ if (!storageKey)
77
+ return false;
78
+ try {
79
+ return window.localStorage.getItem(storageKey) !== null;
80
+ }
81
+ catch {
82
+ return false;
83
+ }
84
+ }, [storageKey]);
85
+ return {
86
+ loadGame,
87
+ saveGame,
88
+ clearGame,
89
+ hasSavedGame,
90
+ };
91
+ }
92
+ /**
93
+ * Hook that auto-saves game state with debouncing
94
+ *
95
+ * @param puzzleKey - Key identifying the puzzle
96
+ * @param getCurrentState - Function to get current game state
97
+ * @param dependencies - Dependencies that trigger save
98
+ * @param debounceMs - Debounce delay in ms (default: 1000)
99
+ */
100
+ export function useAutoSave(puzzleKey, getCurrentState, dependencies, debounceMs = 1000) {
101
+ const { saveGame } = useGamePersistence({ puzzleKey });
102
+ const timeoutRef = useRef(null);
103
+ useEffect(() => {
104
+ if (!puzzleKey)
105
+ return;
106
+ // Clear any pending save
107
+ if (timeoutRef.current) {
108
+ globalThis.clearTimeout(timeoutRef.current);
109
+ }
110
+ // Schedule new save
111
+ timeoutRef.current = globalThis.setTimeout(() => {
112
+ const state = getCurrentState();
113
+ if (state) {
114
+ saveGame(state);
115
+ }
116
+ }, debounceMs);
117
+ return () => {
118
+ if (timeoutRef.current) {
119
+ globalThis.clearTimeout(timeoutRef.current);
120
+ }
121
+ };
122
+ }, [puzzleKey, saveGame, debounceMs, ...dependencies]);
123
+ }
124
+ //# sourceMappingURL=useGamePersistence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useGamePersistence.js","sourceRoot":"","sources":["../../src/hooks/useGamePersistence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Hook for integrating the Sudoku solver for teaching and hints
3
+ */
4
+ import type { NetworkClient } from '@sudobility/types';
5
+ import { type ClientConfig } from '@sudobility/sudojo_client';
6
+ import type { GameHint, TeachingState } from '../types';
7
+ export interface UseGameTeachingOptions {
8
+ /** Network client for API calls */
9
+ networkClient: NetworkClient;
10
+ /** Solver API configuration */
11
+ config: ClientConfig;
12
+ }
13
+ export interface UseGameTeachingResult {
14
+ /** Current teaching state */
15
+ teachingState: TeachingState;
16
+ /** Get a hint for the current board state */
17
+ getHint: (originalPuzzle: string, userBoard: string, options?: {
18
+ autoPencilmarks?: boolean;
19
+ pencilmarks?: string;
20
+ }) => Promise<void>;
21
+ /** Apply the current hint to the board (returns the action to perform) */
22
+ applyHint: () => GameHint | null;
23
+ /** Move to the next step in a multi-step hint */
24
+ nextStep: () => void;
25
+ /** Move to the previous step in a multi-step hint */
26
+ previousStep: () => void;
27
+ /** Clear the current hint */
28
+ clearHint: () => void;
29
+ /** Whether a hint is available */
30
+ hasHint: boolean;
31
+ /** Whether there are more steps in the current hint */
32
+ hasMoreSteps: boolean;
33
+ /** Whether we can go to previous step */
34
+ hasPreviousStep: boolean;
35
+ }
36
+ /**
37
+ * Hook for teaching/hint functionality using the solver API
38
+ *
39
+ * @param options - Hook options
40
+ * @returns Teaching state and actions
41
+ *
42
+ * @example
43
+ * ```tsx
44
+ * function GameWithHints() {
45
+ * const game = useGame();
46
+ * const { teachingState, getHint, applyHint } = useGameTeaching({
47
+ * networkClient,
48
+ * config: { baseUrl: 'https://solver.sudojo.com' },
49
+ * });
50
+ *
51
+ * const handleGetHint = async () => {
52
+ * await getHint(
53
+ * game.getOriginalPuzzle(),
54
+ * game.getBoardString(),
55
+ * { autoPencilmarks: game.state.settings.autoPencilmarks }
56
+ * );
57
+ * };
58
+ *
59
+ * const handleApplyHint = () => {
60
+ * const hint = applyHint();
61
+ * if (hint) {
62
+ * // Apply the hint actions to the game
63
+ * for (const cell of hint.cells) {
64
+ * if (cell.action?.select) {
65
+ * game.selectCell(cell.row, cell.column);
66
+ * game.setValue(cell.action.select);
67
+ * }
68
+ * }
69
+ * }
70
+ * };
71
+ *
72
+ * return (
73
+ * <div>
74
+ * <Board cells={game.state.board} />
75
+ * <button onClick={handleGetHint}>Get Hint</button>
76
+ * {teachingState.currentHint && (
77
+ * <div>
78
+ * <h3>{teachingState.currentHint.title}</h3>
79
+ * <p>{teachingState.currentHint.text}</p>
80
+ * <button onClick={handleApplyHint}>Apply</button>
81
+ * </div>
82
+ * )}
83
+ * </div>
84
+ * );
85
+ * }
86
+ * ```
87
+ */
88
+ export declare function useGameTeaching(options: UseGameTeachingOptions): UseGameTeachingResult;
89
+ //# sourceMappingURL=useGameTeaching.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useGameTeaching.d.ts","sourceRoot":"","sources":["../../src/hooks/useGameTeaching.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EACL,KAAK,YAAY,EAIlB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAExD,MAAM,WAAW,sBAAsB;IACrC,mCAAmC;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,+BAA+B;IAC/B,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,6BAA6B;IAC7B,aAAa,EAAE,aAAa,CAAC;IAC7B,6CAA6C;IAC7C,OAAO,EAAE,CACP,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,KAC1D,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,QAAQ,GAAG,IAAI,CAAC;IACjC,iDAAiD;IACjD,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,qDAAqD;IACrD,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,6BAA6B;IAC7B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,uDAAuD;IACvD,YAAY,EAAE,OAAO,CAAC;IACtB,yCAAyC;IACzC,eAAe,EAAE,OAAO,CAAC;CAC1B;AA6CD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,sBAAsB,GAC9B,qBAAqB,CA8KvB"}
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Hook for integrating the Sudoku solver for teaching and hints
3
+ */
4
+ import { useCallback, useMemo, useState } from 'react';
5
+ import { createSudojoSolverClient, } from '@sudobility/sudojo_client';
6
+ /**
7
+ * Converts a solver SolverHintStep to our GameHint format
8
+ */
9
+ function convertHintStep(step) {
10
+ return {
11
+ title: step.title,
12
+ text: step.text,
13
+ cells: step.cells?.map(cell => {
14
+ const action = {};
15
+ if (cell.actions.select) {
16
+ action.select = parseInt(cell.actions.select, 10);
17
+ }
18
+ if (cell.actions.add) {
19
+ action.addPencilmarks = cell.actions.add
20
+ .split('')
21
+ .map(d => parseInt(d, 10));
22
+ }
23
+ if (cell.actions.remove) {
24
+ action.removePencilmarks = cell.actions.remove
25
+ .split('')
26
+ .map(d => parseInt(d, 10));
27
+ }
28
+ return {
29
+ row: cell.row,
30
+ column: cell.column,
31
+ color: cell.color,
32
+ ...(Object.keys(action).length > 0 ? { action } : {}),
33
+ };
34
+ }) ?? [],
35
+ areas: step.areas?.map(area => ({
36
+ type: area.type,
37
+ index: area.index,
38
+ color: area.color,
39
+ })) ?? [],
40
+ };
41
+ }
42
+ /**
43
+ * Hook for teaching/hint functionality using the solver API
44
+ *
45
+ * @param options - Hook options
46
+ * @returns Teaching state and actions
47
+ *
48
+ * @example
49
+ * ```tsx
50
+ * function GameWithHints() {
51
+ * const game = useGame();
52
+ * const { teachingState, getHint, applyHint } = useGameTeaching({
53
+ * networkClient,
54
+ * config: { baseUrl: 'https://solver.sudojo.com' },
55
+ * });
56
+ *
57
+ * const handleGetHint = async () => {
58
+ * await getHint(
59
+ * game.getOriginalPuzzle(),
60
+ * game.getBoardString(),
61
+ * { autoPencilmarks: game.state.settings.autoPencilmarks }
62
+ * );
63
+ * };
64
+ *
65
+ * const handleApplyHint = () => {
66
+ * const hint = applyHint();
67
+ * if (hint) {
68
+ * // Apply the hint actions to the game
69
+ * for (const cell of hint.cells) {
70
+ * if (cell.action?.select) {
71
+ * game.selectCell(cell.row, cell.column);
72
+ * game.setValue(cell.action.select);
73
+ * }
74
+ * }
75
+ * }
76
+ * };
77
+ *
78
+ * return (
79
+ * <div>
80
+ * <Board cells={game.state.board} />
81
+ * <button onClick={handleGetHint}>Get Hint</button>
82
+ * {teachingState.currentHint && (
83
+ * <div>
84
+ * <h3>{teachingState.currentHint.title}</h3>
85
+ * <p>{teachingState.currentHint.text}</p>
86
+ * <button onClick={handleApplyHint}>Apply</button>
87
+ * </div>
88
+ * )}
89
+ * </div>
90
+ * );
91
+ * }
92
+ * ```
93
+ */
94
+ export function useGameTeaching(options) {
95
+ const { networkClient, config } = options;
96
+ const [teachingState, setTeachingState] = useState({
97
+ isActive: false,
98
+ currentHint: null,
99
+ currentStep: 0,
100
+ totalSteps: 0,
101
+ isLoading: false,
102
+ error: null,
103
+ });
104
+ // Store all hint steps for multi-step hints
105
+ const [allSteps, setAllSteps] = useState([]);
106
+ // Create solver client
107
+ const solverClient = useMemo(() => {
108
+ return createSudojoSolverClient(networkClient, config);
109
+ }, [networkClient, config]);
110
+ const getHint = useCallback(async (originalPuzzle, userBoard, hintOptions) => {
111
+ setTeachingState(prev => ({
112
+ ...prev,
113
+ isLoading: true,
114
+ error: null,
115
+ }));
116
+ try {
117
+ const solveOptions = {
118
+ original: originalPuzzle,
119
+ user: userBoard,
120
+ };
121
+ if (hintOptions?.autoPencilmarks !== undefined) {
122
+ solveOptions.autoPencilmarks = hintOptions.autoPencilmarks;
123
+ }
124
+ if (hintOptions?.pencilmarks !== undefined) {
125
+ solveOptions.pencilmarks = hintOptions.pencilmarks;
126
+ }
127
+ const response = await solverClient.solve(solveOptions);
128
+ if (!response.success || !response.data?.hints?.length) {
129
+ const errorMessage = response.error ?? 'No hints available for the current state';
130
+ setTeachingState(prev => ({
131
+ ...prev,
132
+ isLoading: false,
133
+ error: errorMessage,
134
+ isActive: false,
135
+ currentHint: null,
136
+ currentStep: 0,
137
+ totalSteps: 0,
138
+ }));
139
+ setAllSteps([]);
140
+ return;
141
+ }
142
+ const hints = response.data.hints;
143
+ const convertedSteps = hints.map(convertHintStep);
144
+ if (convertedSteps.length === 0) {
145
+ setTeachingState(prev => ({
146
+ ...prev,
147
+ isLoading: false,
148
+ error: 'No hints available',
149
+ isActive: false,
150
+ currentHint: null,
151
+ currentStep: 0,
152
+ totalSteps: 0,
153
+ }));
154
+ setAllSteps([]);
155
+ return;
156
+ }
157
+ setAllSteps(convertedSteps);
158
+ setTeachingState({
159
+ isActive: true,
160
+ currentHint: convertedSteps[0] ?? null,
161
+ currentStep: 0,
162
+ totalSteps: convertedSteps.length,
163
+ isLoading: false,
164
+ error: null,
165
+ });
166
+ }
167
+ catch (err) {
168
+ const errorMessage = err instanceof Error ? err.message : 'Failed to get hint';
169
+ setTeachingState(prev => ({
170
+ ...prev,
171
+ isLoading: false,
172
+ error: errorMessage,
173
+ isActive: false,
174
+ currentHint: null,
175
+ currentStep: 0,
176
+ totalSteps: 0,
177
+ }));
178
+ setAllSteps([]);
179
+ }
180
+ }, [solverClient]);
181
+ const applyHint = useCallback(() => {
182
+ return teachingState.currentHint;
183
+ }, [teachingState.currentHint]);
184
+ const nextStep = useCallback(() => {
185
+ if (teachingState.currentStep < teachingState.totalSteps - 1) {
186
+ const nextStepIndex = teachingState.currentStep + 1;
187
+ setTeachingState(prev => ({
188
+ ...prev,
189
+ currentStep: nextStepIndex,
190
+ currentHint: allSteps[nextStepIndex] ?? null,
191
+ }));
192
+ }
193
+ }, [teachingState.currentStep, teachingState.totalSteps, allSteps]);
194
+ const previousStep = useCallback(() => {
195
+ if (teachingState.currentStep > 0) {
196
+ const prevStepIndex = teachingState.currentStep - 1;
197
+ setTeachingState(prev => ({
198
+ ...prev,
199
+ currentStep: prevStepIndex,
200
+ currentHint: allSteps[prevStepIndex] ?? null,
201
+ }));
202
+ }
203
+ }, [teachingState.currentStep, allSteps]);
204
+ const clearHint = useCallback(() => {
205
+ setTeachingState({
206
+ isActive: false,
207
+ currentHint: null,
208
+ currentStep: 0,
209
+ totalSteps: 0,
210
+ isLoading: false,
211
+ error: null,
212
+ });
213
+ setAllSteps([]);
214
+ }, []);
215
+ const hasHint = useMemo(() => teachingState.isActive && teachingState.currentHint !== null, [teachingState.isActive, teachingState.currentHint]);
216
+ const hasMoreSteps = useMemo(() => teachingState.currentStep < teachingState.totalSteps - 1, [teachingState.currentStep, teachingState.totalSteps]);
217
+ const hasPreviousStep = useMemo(() => teachingState.currentStep > 0, [teachingState.currentStep]);
218
+ return {
219
+ teachingState,
220
+ getHint,
221
+ applyHint,
222
+ nextStep,
223
+ previousStep,
224
+ clearHint,
225
+ hasHint,
226
+ hasMoreSteps,
227
+ hasPreviousStep,
228
+ };
229
+ }
230
+ //# sourceMappingURL=useGameTeaching.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useGameTeaching.js","sourceRoot":"","sources":["../../src/hooks/useGameTeaching.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Game Timer Hook - Track elapsed time during gameplay
3
+ */
4
+ export interface UseGameTimerResult {
5
+ /** Elapsed time in seconds */
6
+ elapsedSeconds: number;
7
+ /** Formatted time string (MM:SS or HH:MM:SS) */
8
+ formattedTime: string;
9
+ /** Whether the timer is running */
10
+ isRunning: boolean;
11
+ /** Start the timer */
12
+ start: () => void;
13
+ /** Pause the timer */
14
+ pause: () => void;
15
+ /** Resume the timer */
16
+ resume: () => void;
17
+ /** Reset the timer */
18
+ reset: () => void;
19
+ /** Stop the timer (pause and return final time) */
20
+ stop: () => number;
21
+ }
22
+ export interface UseGameTimerOptions {
23
+ /** Auto-start timer on mount */
24
+ autoStart?: boolean;
25
+ /** Initial time in seconds */
26
+ initialTime?: number;
27
+ }
28
+ /**
29
+ * Hook for game timer functionality
30
+ *
31
+ * @param options - Timer options
32
+ * @returns Timer state and controls
33
+ *
34
+ * @example
35
+ * ```tsx
36
+ * const { elapsedSeconds, formattedTime, isRunning, start, pause, stop } = useGameTimer({ autoStart: true });
37
+ *
38
+ * // Display: <span>{formattedTime}</span>
39
+ * // On completion: const finalTime = stop();
40
+ * ```
41
+ */
42
+ export declare function useGameTimer(options?: UseGameTimerOptions): UseGameTimerResult;
43
+ //# sourceMappingURL=useGameTimer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useGameTimer.d.ts","sourceRoot":"","sources":["../../src/hooks/useGameTimer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,MAAM,WAAW,kBAAkB;IACjC,8BAA8B;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,gDAAgD;IAChD,aAAa,EAAE,MAAM,CAAC;IACtB,mCAAmC;IACnC,SAAS,EAAE,OAAO,CAAC;IACnB,sBAAsB;IACtB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,sBAAsB;IACtB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,uBAAuB;IACvB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,sBAAsB;IACtB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,mDAAmD;IACnD,IAAI,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,gCAAgC;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,8BAA8B;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAC1B,OAAO,GAAE,mBAAwB,GAChC,kBAAkB,CA0DpB"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Game Timer Hook - Track elapsed time during gameplay
3
+ */
4
+ import { useCallback, useEffect, useRef, useState } from 'react';
5
+ import { formatTime } from '../utils/time';
6
+ /**
7
+ * Hook for game timer functionality
8
+ *
9
+ * @param options - Timer options
10
+ * @returns Timer state and controls
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * const { elapsedSeconds, formattedTime, isRunning, start, pause, stop } = useGameTimer({ autoStart: true });
15
+ *
16
+ * // Display: <span>{formattedTime}</span>
17
+ * // On completion: const finalTime = stop();
18
+ * ```
19
+ */
20
+ export function useGameTimer(options = {}) {
21
+ const { autoStart = false, initialTime = 0 } = options;
22
+ const [elapsedSeconds, setElapsedSeconds] = useState(initialTime);
23
+ const [isRunning, setIsRunning] = useState(autoStart);
24
+ const intervalRef = useRef(null);
25
+ // Timer tick effect
26
+ useEffect(() => {
27
+ if (isRunning) {
28
+ intervalRef.current = globalThis.setInterval(() => {
29
+ setElapsedSeconds(prev => prev + 1);
30
+ }, 1000);
31
+ }
32
+ return () => {
33
+ if (intervalRef.current) {
34
+ globalThis.clearInterval(intervalRef.current);
35
+ intervalRef.current = null;
36
+ }
37
+ };
38
+ }, [isRunning]);
39
+ const start = useCallback(() => {
40
+ setElapsedSeconds(0);
41
+ setIsRunning(true);
42
+ }, []);
43
+ const pause = useCallback(() => {
44
+ setIsRunning(false);
45
+ }, []);
46
+ const resume = useCallback(() => {
47
+ setIsRunning(true);
48
+ }, []);
49
+ const reset = useCallback(() => {
50
+ setIsRunning(false);
51
+ setElapsedSeconds(0);
52
+ }, []);
53
+ const stop = useCallback(() => {
54
+ setIsRunning(false);
55
+ return elapsedSeconds;
56
+ }, [elapsedSeconds]);
57
+ return {
58
+ elapsedSeconds,
59
+ formattedTime: formatTime(elapsedSeconds),
60
+ isRunning,
61
+ start,
62
+ pause,
63
+ resume,
64
+ reset,
65
+ stop,
66
+ };
67
+ }
68
+ //# sourceMappingURL=useGameTimer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useGameTimer.js","sourceRoot":"","sources":["../../src/hooks/useGameTimer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AA4B3C;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,YAAY,CAC1B,UAA+B,EAAE;IAEjC,MAAM,EAAE,SAAS,GAAG,KAAK,EAAE,WAAW,GAAG,CAAC,EAAE,GAAG,OAAO,CAAC;IAEvD,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;IAClE,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;IACtD,MAAM,WAAW,GAAG,MAAM,CACxB,IAAI,CACL,CAAC;IAEF,oBAAoB;IACpB,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,SAAS,EAAE,CAAC;YACd,WAAW,CAAC,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,GAAG,EAAE;gBAChD,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;YACtC,CAAC,EAAE,IAAI,CAAC,CAAC;QACX,CAAC;QAED,OAAO,GAAG,EAAE;YACV,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;gBACxB,UAAU,CAAC,aAAa,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;gBAC9C,WAAW,CAAC,OAAO,GAAG,IAAI,CAAC;YAC7B,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAEhB,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,iBAAiB,CAAC,CAAC,CAAC,CAAC;QACrB,YAAY,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE;QAC9B,YAAY,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,YAAY,CAAC,KAAK,CAAC,CAAC;QACpB,iBAAiB,CAAC,CAAC,CAAC,CAAC;IACvB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE;QAC5B,YAAY,CAAC,KAAK,CAAC,CAAC;QACpB,OAAO,cAAc,CAAC;IACxB,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;IAErB,OAAO;QACL,cAAc;QACd,aAAa,EAAE,UAAU,CAAC,cAAc,CAAC;QACzC,SAAS;QACT,KAAK;QACL,KAAK;QACL,MAAM;QACN,KAAK;QACL,IAAI;KACL,CAAC;AACJ,CAAC"}