add hint
This commit is contained in:
@@ -27,6 +27,7 @@ const CELL_SIZE = 80;
|
||||
const CELL_FONT_SIZE = 36;
|
||||
const DRAFT_SIZE = CELL_SIZE / 2;
|
||||
const DRAFT_FONT_SIZE = CELL_FONT_SIZE / 2;
|
||||
const DIGITS = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
|
||||
const createSudokuRound = (difficulty: Difficulty, mode: GameMode): SudokuRound => {
|
||||
const { puzzle, regionMap } = generatePuzzleForMode(difficulty, mode);
|
||||
@@ -39,6 +40,50 @@ const createSudokuRound = (difficulty: Difficulty, mode: GameMode): SudokuRound
|
||||
const cloneGameBoard = (board: GameBoard): GameBoard => board.map(row => [...row]);
|
||||
const cloneDraftCells = (draftCells: DraftCells): DraftCells => draftCells.map(row => [...row]);
|
||||
const createEmptyDraftCells = (): DraftCells => Array.from({ length: 9 }, () => Array(9).fill(false));
|
||||
const getCellKey = (row: number, col: number): string => `${row}-${col}`;
|
||||
|
||||
const isFilledDigit = (value: number): boolean => value >= 1 && value <= 9;
|
||||
|
||||
const findConflictCells = (boardToCheck: GameBoard, regionMapToCheck: RegionMap): Set<string> => {
|
||||
const conflictCells = new Set<string>();
|
||||
|
||||
const addGroupConflicts = (cells: Array<[number, number]>) => {
|
||||
const cellsByValue = new Map<number, string[]>();
|
||||
|
||||
cells.forEach(([row, col]) => {
|
||||
const value = boardToCheck[row][col];
|
||||
if (!isFilledDigit(value)) return;
|
||||
|
||||
const keys = cellsByValue.get(value) ?? [];
|
||||
keys.push(getCellKey(row, col));
|
||||
cellsByValue.set(value, keys);
|
||||
});
|
||||
|
||||
cellsByValue.forEach((keys) => {
|
||||
if (keys.length <= 1) return;
|
||||
keys.forEach((key) => conflictCells.add(key));
|
||||
});
|
||||
};
|
||||
|
||||
for (let row = 0; row < 9; row++) {
|
||||
addGroupConflicts(Array.from({ length: 9 }, (_, col) => [row, col]));
|
||||
}
|
||||
|
||||
for (let col = 0; col < 9; col++) {
|
||||
addGroupConflicts(Array.from({ length: 9 }, (_, row) => [row, col]));
|
||||
}
|
||||
|
||||
const cellsByRegion = Array.from({ length: 9 }, () => [] as Array<[number, number]>);
|
||||
for (let row = 0; row < 9; row++) {
|
||||
for (let col = 0; col < 9; col++) {
|
||||
cellsByRegion[regionMapToCheck[row][col]].push([row, col]);
|
||||
}
|
||||
}
|
||||
|
||||
cellsByRegion.forEach(addGroupConflicts);
|
||||
|
||||
return conflictCells;
|
||||
};
|
||||
|
||||
const SudokuGame: React.FC = () => {
|
||||
// 游戏难度
|
||||
@@ -429,13 +474,25 @@ const SudokuGame: React.FC = () => {
|
||||
for (let r = 0; r < 9; r++) {
|
||||
for (let c = 0; c < 9; c++) {
|
||||
if (getCellHighlightType(r, c) !== 'none') {
|
||||
highlighted.add(`${r}-${c}`);
|
||||
highlighted.add(getCellKey(r, c));
|
||||
}
|
||||
}
|
||||
}
|
||||
return highlighted;
|
||||
}, [board, selectedCell, config, regionMap]);
|
||||
|
||||
const conflictCells = useMemo(() => findConflictCells(board, regionMap), [board, regionMap]);
|
||||
|
||||
const availableNumbers = useMemo(() => {
|
||||
return board.map((row, rowIndex) =>
|
||||
row.map((cell, colIndex) => (
|
||||
cell === 0
|
||||
? DIGITS.filter((num) => isValidInBoard(rowIndex, colIndex, num, board))
|
||||
: []
|
||||
))
|
||||
);
|
||||
}, [board, regionMap]);
|
||||
|
||||
// 计算剩余未填写的数字数量
|
||||
const remainingCount = useMemo(() => {
|
||||
let count = 0;
|
||||
@@ -453,11 +510,13 @@ const SudokuGame: React.FC = () => {
|
||||
const isFixed = fixedCells[row][col];
|
||||
const highlightType = getCellHighlightType(row, col);
|
||||
const isSelected = selectedCell && selectedCell[0] === row && selectedCell[1] === col;
|
||||
const displayValue = getCellValue(cell);
|
||||
const isConflicting = conflictCells.has(getCellKey(row, col));
|
||||
|
||||
// 基础背景色 - 根据高亮类型设置不同颜色
|
||||
let backgroundColor = 'white';
|
||||
if (isSelected) {
|
||||
if (isConflicting) {
|
||||
backgroundColor = '#fff1f0'; // 冲突数字 - 警告红色
|
||||
} else if (isSelected) {
|
||||
backgroundColor = '#69b1ff'; // 选中时的深蓝色
|
||||
} else if (highlightType === 'box') {
|
||||
backgroundColor = '#d9f7be'; // 宫 - 浅绿色
|
||||
@@ -495,9 +554,12 @@ const SudokuGame: React.FC = () => {
|
||||
justifyContent: 'center',
|
||||
fontSize: `${CELL_FONT_SIZE}px`,
|
||||
cursor: 'pointer',
|
||||
color: isFixed ? '#333' : '#1890ff',
|
||||
color: isConflicting ? '#cf1322' : isFixed ? '#333' : '#1890ff',
|
||||
position: 'relative' as const,
|
||||
boxSizing: 'border-box',
|
||||
boxShadow: isConflicting ? 'inset 0 0 0 3px #ff4d4f' : undefined,
|
||||
outline: isSelected ? '3px solid #1677ff' : undefined,
|
||||
outlineOffset: '-6px',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -513,9 +575,25 @@ const SudokuGame: React.FC = () => {
|
||||
justifyContent: 'center',
|
||||
fontSize: `${isDraft ? DRAFT_FONT_SIZE : CELL_FONT_SIZE}px`,
|
||||
lineHeight: 1,
|
||||
position: 'relative' as const,
|
||||
zIndex: 1,
|
||||
};
|
||||
};
|
||||
|
||||
const getAvailableNumbersStyle = (): React.CSSProperties => ({
|
||||
position: 'absolute' as const,
|
||||
top: '4px',
|
||||
right: '5px',
|
||||
maxWidth: '46px',
|
||||
fontSize: '11px',
|
||||
lineHeight: 1,
|
||||
color: '#8c8c8c',
|
||||
textAlign: 'right' as const,
|
||||
pointerEvents: 'none' as const,
|
||||
zIndex: 2,
|
||||
fontVariantNumeric: 'tabular-nums',
|
||||
});
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '20px', padding: '20px' }}>
|
||||
{/* 顶部控制和配置区 */}
|
||||
@@ -666,13 +744,19 @@ const SudokuGame: React.FC = () => {
|
||||
row.map((cell, colIndex) => {
|
||||
const displayValue = getCellValue(cell);
|
||||
const cellStyle = getCellStyle(rowIndex, colIndex);
|
||||
const candidates = availableNumbers[rowIndex][colIndex];
|
||||
const isConflicting = conflictCells.has(getCellKey(rowIndex, colIndex));
|
||||
|
||||
return (
|
||||
<div
|
||||
key={`${rowIndex}-${colIndex}`}
|
||||
onClick={() => handleCellClick(rowIndex, colIndex)}
|
||||
style={cellStyle}
|
||||
title={isConflicting ? '该数字与同一行、列或区域中的数字冲突' : undefined}
|
||||
>
|
||||
{displayValue === null && candidates.length > 0 && (
|
||||
<span style={getAvailableNumbersStyle()}>{candidates.join('')}</span>
|
||||
)}
|
||||
{displayValue !== null ? (
|
||||
<span style={getCellValueStyle(rowIndex, colIndex)}>{displayValue}</span>
|
||||
) : ''}
|
||||
|
||||
Reference in New Issue
Block a user