@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.
- package/dist/hooks/index.d.ts +27 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +27 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/useDailyGame.d.ts +64 -0
- package/dist/hooks/useDailyGame.d.ts.map +1 -0
- package/dist/hooks/useDailyGame.js +120 -0
- package/dist/hooks/useDailyGame.js.map +1 -0
- package/dist/hooks/useGame.d.ts +104 -0
- package/dist/hooks/useGame.d.ts.map +1 -0
- package/dist/hooks/useGame.js +546 -0
- package/dist/hooks/useGame.js.map +1 -0
- package/dist/hooks/useGamePersistence.d.ts +63 -0
- package/dist/hooks/useGamePersistence.d.ts.map +1 -0
- package/dist/hooks/useGamePersistence.js +124 -0
- package/dist/hooks/useGamePersistence.js.map +1 -0
- package/dist/hooks/useGameTeaching.d.ts +89 -0
- package/dist/hooks/useGameTeaching.d.ts.map +1 -0
- package/dist/hooks/useGameTeaching.js +230 -0
- package/dist/hooks/useGameTeaching.js.map +1 -0
- package/dist/hooks/useGameTimer.d.ts +43 -0
- package/dist/hooks/useGameTimer.d.ts.map +1 -0
- package/dist/hooks/useGameTimer.js +68 -0
- package/dist/hooks/useGameTimer.js.map +1 -0
- package/dist/hooks/useHint.d.ts +105 -0
- package/dist/hooks/useHint.d.ts.map +1 -0
- package/dist/hooks/useHint.js +179 -0
- package/dist/hooks/useHint.js.map +1 -0
- package/dist/hooks/useLearning.d.ts +100 -0
- package/dist/hooks/useLearning.d.ts.map +1 -0
- package/dist/hooks/useLearning.js +127 -0
- package/dist/hooks/useLearning.js.map +1 -0
- package/dist/hooks/useLevelGame.d.ts +68 -0
- package/dist/hooks/useLevelGame.d.ts.map +1 -0
- package/dist/hooks/useLevelGame.js +128 -0
- package/dist/hooks/useLevelGame.js.map +1 -0
- package/dist/hooks/useLevels.d.ts +93 -0
- package/dist/hooks/useLevels.d.ts.map +1 -0
- package/dist/hooks/useLevels.js +98 -0
- package/dist/hooks/useLevels.js.map +1 -0
- package/dist/hooks/useLocalStorage.d.ts +26 -0
- package/dist/hooks/useLocalStorage.d.ts.map +1 -0
- package/dist/hooks/useLocalStorage.js +75 -0
- package/dist/hooks/useLocalStorage.js.map +1 -0
- package/dist/hooks/useSudoku.d.ts +125 -0
- package/dist/hooks/useSudoku.d.ts.map +1 -0
- package/dist/hooks/useSudoku.js +655 -0
- package/dist/hooks/useSudoku.js.map +1 -0
- package/dist/hooks/useTechniques.d.ts +92 -0
- package/dist/hooks/useTechniques.d.ts.map +1 -0
- package/dist/hooks/useTechniques.js +108 -0
- package/dist/hooks/useTechniques.js.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/dist/test/setup.d.ts +2 -0
- package/dist/test/setup.d.ts.map +1 -0
- package/dist/test/setup.js +35 -0
- package/dist/test/setup.js.map +1 -0
- package/dist/types/display.d.ts +142 -0
- package/dist/types/display.d.ts.map +1 -0
- package/dist/types/display.js +94 -0
- package/dist/types/display.js.map +1 -0
- package/dist/types/game.d.ts +201 -0
- package/dist/types/game.d.ts.map +1 -0
- package/dist/types/game.js +26 -0
- package/dist/types/game.js.map +1 -0
- package/dist/types/gamePersistence.d.ts +31 -0
- package/dist/types/gamePersistence.d.ts.map +1 -0
- package/dist/types/gamePersistence.js +15 -0
- package/dist/types/gamePersistence.js.map +1 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +11 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/progress.d.ts +43 -0
- package/dist/types/progress.d.ts.map +1 -0
- package/dist/types/progress.js +19 -0
- package/dist/types/progress.js.map +1 -0
- package/dist/types/settings.d.ts +19 -0
- package/dist/types/settings.d.ts.map +1 -0
- package/dist/types/settings.js +12 -0
- package/dist/types/settings.js.map +1 -0
- package/dist/types/subscription.d.ts +27 -0
- package/dist/types/subscription.d.ts.map +1 -0
- package/dist/types/subscription.js +8 -0
- package/dist/types/subscription.js.map +1 -0
- package/dist/types/sudoku.d.ts +147 -0
- package/dist/types/sudoku.d.ts.map +1 -0
- package/dist/types/sudoku.js +125 -0
- package/dist/types/sudoku.js.map +1 -0
- package/dist/utils/auth.d.ts +25 -0
- package/dist/utils/auth.d.ts.map +1 -0
- package/dist/utils/auth.js +47 -0
- package/dist/utils/auth.js.map +1 -0
- package/dist/utils/board.d.ts +116 -0
- package/dist/utils/board.d.ts.map +1 -0
- package/dist/utils/board.js +291 -0
- package/dist/utils/board.js.map +1 -0
- package/dist/utils/index.d.ts +18 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +23 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/progress.d.ts +44 -0
- package/dist/utils/progress.d.ts.map +1 -0
- package/dist/utils/progress.js +104 -0
- package/dist/utils/progress.js.map +1 -0
- package/dist/utils/scramble.d.ts +35 -0
- package/dist/utils/scramble.d.ts.map +1 -0
- package/dist/utils/scramble.js +270 -0
- package/dist/utils/scramble.js.map +1 -0
- package/dist/utils/subscription.d.ts +82 -0
- package/dist/utils/subscription.d.ts.map +1 -0
- package/dist/utils/subscription.js +117 -0
- package/dist/utils/subscription.js.map +1 -0
- package/dist/utils/sudokuPresenter.d.ts +62 -0
- package/dist/utils/sudokuPresenter.d.ts.map +1 -0
- package/dist/utils/sudokuPresenter.js +403 -0
- package/dist/utils/sudokuPresenter.js.map +1 -0
- package/dist/utils/sudokuScrambler.d.ts +61 -0
- package/dist/utils/sudokuScrambler.d.ts.map +1 -0
- package/dist/utils/sudokuScrambler.js +223 -0
- package/dist/utils/sudokuScrambler.js.map +1 -0
- package/dist/utils/technique.d.ts +41 -0
- package/dist/utils/technique.d.ts.map +1 -0
- package/dist/utils/technique.js +106 -0
- package/dist/utils/technique.js.map +1 -0
- package/dist/utils/theme.d.ts +31 -0
- package/dist/utils/theme.d.ts.map +1 -0
- package/dist/utils/theme.js +40 -0
- package/dist/utils/theme.js.map +1 -0
- package/dist/utils/time.d.ts +22 -0
- package/dist/utils/time.d.ts.map +1 -0
- package/dist/utils/time.js +41 -0
- package/dist/utils/time.js.map +1 -0
- package/dist/utils/validation.d.ts +107 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +248 -0
- package/dist/utils/validation.js.map +1 -0
- 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":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEvD,OAAO,EAEL,wBAAwB,GAGzB,MAAM,2BAA2B,CAAC;AAmCnC;;GAEG;AACH,SAAS,eAAe,CAAC,IAAoB;IAC3C,OAAO;QACL,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,KAAK,EACH,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE;YACrB,MAAM,MAAM,GAIR,EAAE,CAAC;YACP,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;gBACxB,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YACpD,CAAC;YACD,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;gBACrB,MAAM,CAAC,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG;qBACrC,KAAK,CAAC,EAAE,CAAC;qBACT,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YAC/B,CAAC;YACD,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;gBACxB,MAAM,CAAC,iBAAiB,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM;qBAC3C,KAAK,CAAC,EAAE,CAAC;qBACT,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YAC/B,CAAC;YACD,OAAO;gBACL,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACtD,CAAC;QACJ,CAAC,CAAC,IAAI,EAAE;QACV,KAAK,EACH,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACvB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC,CAAC,IAAI,EAAE;KACZ,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,MAAM,UAAU,eAAe,CAC7B,OAA+B;IAE/B,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAE1C,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAgB;QAChE,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,IAAI;QACjB,WAAW,EAAE,CAAC;QACd,UAAU,EAAE,CAAC;QACb,SAAS,EAAE,KAAK;QAChB,KAAK,EAAE,IAAI;KACZ,CAAC,CAAC;IAEH,4CAA4C;IAC5C,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAa,EAAE,CAAC,CAAC;IAEzD,uBAAuB;IACvB,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,EAAE;QAChC,OAAO,wBAAwB,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IACzD,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;IAE5B,MAAM,OAAO,GAAG,WAAW,CACzB,KAAK,EACH,cAAsB,EACtB,SAAiB,EACjB,WAAiE,EACjE,EAAE;QACF,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACxB,GAAG,IAAI;YACP,SAAS,EAAE,IAAI;YACf,KAAK,EAAE,IAAI;SACZ,CAAC,CAAC,CAAC;QAEJ,IAAI,CAAC;YACH,MAAM,YAAY,GAKd;gBACF,QAAQ,EAAE,cAAc;gBACxB,IAAI,EAAE,SAAS;aAChB,CAAC;YACF,IAAI,WAAW,EAAE,eAAe,KAAK,SAAS,EAAE,CAAC;gBAC/C,YAAY,CAAC,eAAe,GAAG,WAAW,CAAC,eAAe,CAAC;YAC7D,CAAC;YACD,IAAI,WAAW,EAAE,WAAW,KAAK,SAAS,EAAE,CAAC;gBAC3C,YAAY,CAAC,WAAW,GAAG,WAAW,CAAC,WAAW,CAAC;YACrD,CAAC;YACD,MAAM,QAAQ,GAAkB,MAAM,YAAY,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAEvE,IAAI,CAAC,QAAQ,CAAC,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;gBACvD,MAAM,YAAY,GAChB,QAAQ,CAAC,KAAK,IAAI,0CAA0C,CAAC;gBAC/D,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBACxB,GAAG,IAAI;oBACP,SAAS,EAAE,KAAK;oBAChB,KAAK,EAAE,YAAY;oBACnB,QAAQ,EAAE,KAAK;oBACf,WAAW,EAAE,IAAI;oBACjB,WAAW,EAAE,CAAC;oBACd,UAAU,EAAE,CAAC;iBACd,CAAC,CAAC,CAAC;gBACJ,WAAW,CAAC,EAAE,CAAC,CAAC;gBAChB,OAAO;YACT,CAAC;YAED,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;YAClC,MAAM,cAAc,GAAG,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;YAElD,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAChC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBACxB,GAAG,IAAI;oBACP,SAAS,EAAE,KAAK;oBAChB,KAAK,EAAE,oBAAoB;oBAC3B,QAAQ,EAAE,KAAK;oBACf,WAAW,EAAE,IAAI;oBACjB,WAAW,EAAE,CAAC;oBACd,UAAU,EAAE,CAAC;iBACd,CAAC,CAAC,CAAC;gBACJ,WAAW,CAAC,EAAE,CAAC,CAAC;gBAChB,OAAO;YACT,CAAC;YAED,WAAW,CAAC,cAAc,CAAC,CAAC;YAC5B,gBAAgB,CAAC;gBACf,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,IAAI;gBACtC,WAAW,EAAE,CAAC;gBACd,UAAU,EAAE,cAAc,CAAC,MAAM;gBACjC,SAAS,EAAE,KAAK;gBAChB,KAAK,EAAE,IAAI;aACZ,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,YAAY,GAChB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC;YAC5D,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACxB,GAAG,IAAI;gBACP,SAAS,EAAE,KAAK;gBAChB,KAAK,EAAE,YAAY;gBACnB,QAAQ,EAAE,KAAK;gBACf,WAAW,EAAE,IAAI;gBACjB,WAAW,EAAE,CAAC;gBACd,UAAU,EAAE,CAAC;aACd,CAAC,CAAC,CAAC;YACJ,WAAW,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,EACD,CAAC,YAAY,CAAC,CACf,CAAC;IAEF,MAAM,SAAS,GAAG,WAAW,CAAC,GAAoB,EAAE;QAClD,OAAO,aAAa,CAAC,WAAW,CAAC;IACnC,CAAC,EAAE,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;IAEhC,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;QAChC,IAAI,aAAa,CAAC,WAAW,GAAG,aAAa,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;YAC7D,MAAM,aAAa,GAAG,aAAa,CAAC,WAAW,GAAG,CAAC,CAAC;YACpD,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACxB,GAAG,IAAI;gBACP,WAAW,EAAE,aAAa;gBAC1B,WAAW,EAAE,QAAQ,CAAC,aAAa,CAAC,IAAI,IAAI;aAC7C,CAAC,CAAC,CAAC;QACN,CAAC;IACH,CAAC,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,aAAa,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEpE,MAAM,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE;QACpC,IAAI,aAAa,CAAC,WAAW,GAAG,CAAC,EAAE,CAAC;YAClC,MAAM,aAAa,GAAG,aAAa,CAAC,WAAW,GAAG,CAAC,CAAC;YACpD,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACxB,GAAG,IAAI;gBACP,WAAW,EAAE,aAAa;gBAC1B,WAAW,EAAE,QAAQ,CAAC,aAAa,CAAC,IAAI,IAAI;aAC7C,CAAC,CAAC,CAAC;QACN,CAAC;IACH,CAAC,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC;IAE1C,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE;QACjC,gBAAgB,CAAC;YACf,QAAQ,EAAE,KAAK;YACf,WAAW,EAAE,IAAI;YACjB,WAAW,EAAE,CAAC;YACd,UAAU,EAAE,CAAC;YACb,SAAS,EAAE,KAAK;YAChB,KAAK,EAAE,IAAI;SACZ,CAAC,CAAC;QACH,WAAW,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,OAAO,GAAG,OAAO,CACrB,GAAG,EAAE,CAAC,aAAa,CAAC,QAAQ,IAAI,aAAa,CAAC,WAAW,KAAK,IAAI,EAClE,CAAC,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC,WAAW,CAAC,CACpD,CAAC;IAEF,MAAM,YAAY,GAAG,OAAO,CAC1B,GAAG,EAAE,CAAC,aAAa,CAAC,WAAW,GAAG,aAAa,CAAC,UAAU,GAAG,CAAC,EAC9D,CAAC,aAAa,CAAC,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,CACtD,CAAC;IAEF,MAAM,eAAe,GAAG,OAAO,CAC7B,GAAG,EAAE,CAAC,aAAa,CAAC,WAAW,GAAG,CAAC,EACnC,CAAC,aAAa,CAAC,WAAW,CAAC,CAC5B,CAAC;IAEF,OAAO;QACL,aAAa;QACb,OAAO;QACP,SAAS;QACT,QAAQ;QACR,YAAY;QACZ,SAAS;QACT,OAAO;QACP,YAAY;QACZ,eAAe;KAChB,CAAC;AACJ,CAAC"}
|
|
@@ -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"}
|