feat: implement hint system (Shift+H) with scoring penalties

- Add getHint() method to BoardManager with two-tier priority:
  1. Find incorrect user-entered cells → flash red
  2. Find empty cells → show correct digit green temporarily
- Add Shift+H key handler in useCursor (normal mode only)
- Visual feedback:
  - Red flash (hint-flash-error): incorrect cells, 1s duration, red bg + border
  - Green flash (hint-flash-correct): correct digit shown, 1s duration, green bg + border
  - Distinct from invalid-move (no background, 500ms)
- Score penalties based on difficulty:
  - Easy: 25 points per hint
  - Medium: 50 points per hint
  - Hard: 100 points per hint
- Track hints used in scoreboard display
- Correct digit appears temporarily (1s) then disappears (player still does the work)
- Update example app to show hints counter and Shift+H instruction
This commit is contained in:
2025-10-22 21:24:06 +01:00
parent cfdf8100fb
commit 97c31ab6cc
9 changed files with 345 additions and 9 deletions
+43
View File
@@ -2,6 +2,7 @@ import { useRef, useState } from 'react';
import { SudokuGenerator } from './SudokuGenerator';
import { SudokuRenderer } from './SudokuRenderer';
import type { Difficulty } from './SudokuGenerator';
import type { HintResult } from './types';
export function useBoard() {
const containerRef = useRef<HTMLDivElement | null>(null);
@@ -54,6 +55,47 @@ export function useBoard() {
renderBoard();
}
function getHint(): HintResult {
const board = boardRef.current;
const solution = solutionRef.current;
const initialBoard = initialBoardRef.current;
if (!board || !solution) return null;
const incorrectCells: Array<{ row: number; col: number }> = [];
const emptyCells: Array<{ row: number; col: number }> = [];
for (let i = 0; i < 81; i++) {
const row = Math.floor(i / 9);
const col = i % 9;
const currentValue = board[i];
const correctValue = solution[i];
const initialValue = initialBoard[i];
if (currentValue !== '.' && initialValue === '.' && currentValue !== correctValue) {
incorrectCells.push({ row, col });
}
if (currentValue === '.') {
emptyCells.push({ row, col });
}
}
if (incorrectCells.length > 0) {
const randomCell = incorrectCells[Math.floor(Math.random() * incorrectCells.length)];
return { ...randomCell, type: 'error' };
}
if (emptyCells.length > 0) {
const randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
const index = randomCell.row * 9 + randomCell.col;
const correctValue = solution[index];
return { ...randomCell, type: 'correct', value: correctValue };
}
return null;
}
return {
containerRef,
boardRef,
@@ -61,5 +103,6 @@ export function useBoard() {
generateBoard,
renderBoard,
setCell,
getHint,
};
}