diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..bffb357 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/app/components/developmentToolsComponent/apiKeyGenerator.tsx b/app/components/developmentToolsComponent/apiKeyGenerator.tsx index 7458a0e..92dfc0d 100644 --- a/app/components/developmentToolsComponent/apiKeyGenerator.tsx +++ b/app/components/developmentToolsComponent/apiKeyGenerator.tsx @@ -1,6 +1,6 @@ "use client"; -import React, { useEffect, useRef, useState } from "react"; +import React, { useCallback, useEffect, useRef, useState } from "react"; type Charset = { upper: boolean; @@ -71,19 +71,19 @@ const ApiKeyGenerator: React.FC = () => { const [autoUpdate, setAutoUpdate] = useState(false); const fileRef = useRef(null); - const run = () => { + const run = useCallback(() => { const clampedLen = Math.max(4, Math.min(256, Number(length) || 32)); const clampedCount = Math.max(1, Math.min(100, Number(count) || 1)); const gSize = groupSize && groupSize > 0 ? Math.max(1, Math.min(32, Number(groupSize) || 0)) : null; const out: string[] = []; for (let i = 0; i < clampedCount; i++) out.push(generateKey(clampedLen, charset, { groupSize: gSize, prefix, suffix })); setKeys(out); - }; + }, [length, count, groupSize, charset, prefix, suffix]); useEffect(() => { if (!autoUpdate) return; run(); - }, [length, count, groupSize, charset, prefix, suffix, autoUpdate]); + }, [run, autoUpdate]); const onCopy = async () => { try { diff --git a/app/components/developmentToolsComponent/fibonacciCalculator.tsx b/app/components/developmentToolsComponent/fibonacciCalculator.tsx index 1f5565b..985c8fa 100644 --- a/app/components/developmentToolsComponent/fibonacciCalculator.tsx +++ b/app/components/developmentToolsComponent/fibonacciCalculator.tsx @@ -1,6 +1,6 @@ "use client"; -import React, { useEffect, useRef, useState } from "react"; +import React, { useCallback, useEffect, useRef, useState } from "react"; type Mode = "nth" | "sequence"; @@ -50,14 +50,14 @@ const FibonacciCalculator: React.FC = () => { } }; - const computeNth = (n: number): string => { + const computeNth = useCallback((n: number): string => { // Interpret n according to zeroIndexed toggle: if one-indexed, compute F(n) where input 1 -> F(1)=1 const effective = zeroIndexed ? BigInt(n) : BigInt(Math.max(n, 1) - 1); const [fn] = fibFastDoubling(effective); return formatBigInt(fn, thousandsSep); - }; + }, [zeroIndexed, thousandsSep]); - const computeSequence = (count: number): string => { + const computeSequence = useCallback((count: number): string => { // Build sequence up to count terms. If includeZero is true, start from F(0)=0, else start from F(1)=1. if (count === 0) return ""; const result: string[] = []; @@ -71,7 +71,7 @@ const FibonacciCalculator: React.FC = () => { } const sep = separator === "comma" ? ", " : separator === "space" ? " " : "\n"; return result.join(sep); - }; + }, [includeZero, thousandsSep, separator]); useEffect(() => { if (!autoConvert) return; @@ -85,7 +85,7 @@ const FibonacciCalculator: React.FC = () => { } else { setOutput(computeSequence(n)); } - }, [input, autoConvert, mode, includeZero, thousandsSep, separator, zeroIndexed]); + }, [input, autoConvert, mode, computeNth, computeSequence]); const onConvert = () => { const n = parseNonNegativeInt(input); diff --git a/app/components/developmentToolsComponent/sudokuSolver.tsx b/app/components/developmentToolsComponent/sudokuSolver.tsx new file mode 100644 index 0000000..0f336de --- /dev/null +++ b/app/components/developmentToolsComponent/sudokuSolver.tsx @@ -0,0 +1,174 @@ +"use client"; + +import React, { useState } from "react"; + +const SudokuSolver: React.FC = () => { + const [grid, setGrid] = useState<(number | null)[][]>(Array(9).fill(null).map(() => Array(9).fill(null))); + const [status, setStatus] = useState(""); + const [difficulty, setDifficulty] = useState<"Easy" | "Medium" | "Hard">("Medium"); + + // Helper to check if safe + const isSafe = (board: (number | null)[][], row: number, col: number, num: number) => { + // Check row + for (let x = 0; x < 9; x++) if (board[row][x] === num) return false; + // Check col + for (let x = 0; x < 9; x++) if (board[x][col] === num) return false; + // Check box + const startRow = row - (row % 3); + const startCol = col - (col % 3); + for (let i = 0; i < 3; i++) + for (let j = 0; j < 3; j++) + if (board[i + startRow][j + startCol] === num) return false; + return true; + }; + + const solve = (board: (number | null)[][]): boolean => { + for (let row = 0; row < 9; row++) { + for (let col = 0; col < 9; col++) { + if (board[row][col] === null) { + for (let num = 1; num <= 9; num++) { + if (isSafe(board, row, col, num)) { + board[row][col] = num; + if (solve(board)) return true; + board[row][col] = null; + } + } + return false; + } + } + } + return true; + }; + + const handleSolve = () => { + const board = grid.map(row => [...row]); + if (solve(board)) { + setGrid(board); + setStatus("Solved!"); + } else { + setStatus("No solution found."); + } + }; + + const handleClear = () => { + setGrid(Array(9).fill(null).map(() => Array(9).fill(null))); + setStatus(""); + }; + + const isSafeBox = (board: (number | null)[][], rowStart: number, colStart: number, num: number) => { + for (let i = 0; i < 3; i++) + for (let j = 0; j < 3; j++) + if (board[rowStart + i][colStart + j] === num) return false; + return true; + }; + + const fillBox = (board: (number | null)[][], row: number, col: number) => { + let num: number; + for (let i = 0; i < 3; i++) { + for (let j = 0; j < 3; j++) { + do { + num = Math.floor(Math.random() * 9) + 1; + } while (!isSafeBox(board, row, col, num)); + board[row + i][col + j] = num; + } + } + }; + + const generate = () => { + // Start fresh + const board = Array(9).fill(null).map(() => Array(9).fill(null)); + + // Fill diagonal 3x3 matrices (independent) + for (let i = 0; i < 9; i = i + 3) { + fillBox(board, i, i); + } + + // Solve remaining + if (!solve(board)) { + setStatus("Failed to generate. Try again."); + return; + } + + // Remove digits based on difficulty + // Easy: ~30 removed (keep 51), Medium: ~45 removed (keep 36), Hard: ~55 removed (keep 26) + // Actually, usually it's specified by givens. + // Easy: 40-50 givens. Medium: 30-40 givens. Hard: 20-30 givens. + // Let's remove cells. + let attempts = difficulty === "Easy" ? 40 : difficulty === "Medium" ? 50 : 60; + + while (attempts > 0) { + let row = Math.floor(Math.random() * 9); + let col = Math.floor(Math.random() * 9); + while (board[row][col] === null) { + row = Math.floor(Math.random() * 9); + col = Math.floor(Math.random() * 9); + } + board[row][col] = null; + attempts--; + } + setGrid(board); + setStatus(""); + }; + + const handleChange = (row: number, col: number, value: string) => { + const val = parseInt(value); + const newGrid = grid.map(r => [...r]); + if (value === "" || isNaN(val)) { + newGrid[row][col] = null; + } else if (val >= 1 && val <= 9) { + newGrid[row][col] = val; + } + setGrid(newGrid); + }; + + return ( +
+
+
+
+

Sudoku Solver & Generator

+ +
+ + + + +
+ +
+
+ {grid.map((row, rowIndex) => ( + row.map((cell, colIndex) => ( + handleChange(rowIndex, colIndex, e.target.value)} + className={`w-10 h-10 text-center font-bold text-lg focus:outline-none focus:bg-blue-100 + ${colIndex % 3 === 2 && colIndex !== 8 ? "border-r-2 border-r-gray-800" : "border-r border-gray-300"} + ${rowIndex % 3 === 2 && rowIndex !== 8 ? "border-b-2 border-b-gray-800" : "border-b border-gray-300"} + `} + /> + )) + ))} +
+
+ + {status &&
{status}
} +
+
+
+
+ ); +}; + +export default SudokuSolver; diff --git a/app/libs/constants.tsx b/app/libs/constants.tsx index c51e71e..1ee14bc 100644 --- a/app/libs/constants.tsx +++ b/app/libs/constants.tsx @@ -34,6 +34,7 @@ import RoundingCalculator from '../components/developmentToolsComponent/rounding import SentenceCounterComponent from '../components/developmentToolsComponent/sentenceCounterComponent'; import SortNumbers from '../components/developmentToolsComponent/sortNumbers'; import SortWords from '../components/developmentToolsComponent/sortWords'; +import SudokuSolver from '../components/developmentToolsComponent/sudokuSolver'; import TextToOneLine from '../components/developmentToolsComponent/textToOneLine'; import TxtToCsvConverter from '../components/developmentToolsComponent/txtToCsvConverter'; import UpperCaseConverterComponent from '../components/developmentToolsComponent/upperCaseConverterComponent'; @@ -1589,6 +1590,13 @@ export const developmentToolsCategoryContent: any = { description: 'Convert HTML to Jade.', }, ], + Category176: [ + { + url: '/sudoku-solver', + title: 'Sudoku Solver & Generator', + description: 'Solve Sudoku puzzles instantly or generate new ones with difficulty levels.', + }, + ], }; export const PATHS = { @@ -1767,6 +1775,7 @@ export const PATHS = { CSS_TO_LESS: '/css-to-less', CRONTAB_GENERATOR: '/crontab-generator', MORSE_CODE_TRANSLATOR: '/morse-code-translator', + SUDOKU_SOLVER: '/sudoku-solver', }; export const developmentToolsRoutes = [ @@ -2466,6 +2475,10 @@ export const developmentToolsRoutes = [ path: PATHS.HTML_TO_JADE, component: , }, + { + path: PATHS.SUDOKU_SOLVER, + component: , + }, ]; // lorem ipsum text diff --git a/app/libs/developmentToolsConstant.tsx b/app/libs/developmentToolsConstant.tsx index aea9220..664a155 100644 --- a/app/libs/developmentToolsConstant.tsx +++ b/app/libs/developmentToolsConstant.tsx @@ -17169,4 +17169,79 @@ family[1]: "Beth"`, og_image: '/images/og-images/Cover.png', }, }, + [`sudoku-solver`]: { + hero_section: { + title: 'Sudoku Solver & Generator', + description: + 'A free online tool to solve Sudoku puzzles instantly and generate new puzzles with different difficulty levels.', + }, + development_tools_list: [ + { tool: 'Random Number Generator', url: PATHS.RANDOM_NUMBER_GENERATOR }, + { tool: 'Sorting List', url: PATHS.SORTING_LIST }, + ], + development_tools_about_details: { + about_title: 'What is the Sudoku Solver?', + about_description: [ + { + description: + 'The Sudoku Solver is a powerful tool that allows you to solve any 9x9 Sudoku puzzle instantly. It uses an advanced backtracking algorithm to find the solution.', + }, + { + description: + 'Additionally, it features a Sudoku Generator that can create new puzzles with varying difficulty levels: Easy, Medium, and Hard (Hard Level).', + }, + ], + }, + development_tools_steps_guide: { + guide_title: 'How to use', + guide_description: 'Using the tool is simple:', + steps: [ + { + step_key: 'Step 1:', + step_title: 'Generate or Input:', + step_description: + 'Click "Generate" to create a new puzzle, or manually enter numbers into the grid.', + }, + { + step_key: 'Step 2:', + step_title: 'Select Difficulty:', + step_description: + 'If generating, select the difficulty level (Easy, Medium, Hard Level) from the dropdown.', + }, + { + step_key: 'Step 3:', + step_title: 'Solve:', + step_description: + 'Click "Solve" to see the solution instantly.', + }, + ], + }, + development_tools_how_use: { + how_use_title: 'Use Cases', + how_use_description: 'Great for:', + point: [ + { + title: 'Entertainment', + description: 'Play and solve Sudoku puzzles for fun.', + }, + { + title: 'Learning', + description: 'Understand Sudoku strategies and logic.', + }, + { + title: 'Testing', + description: 'Test Sudoku solving algorithms and logic.', + }, + ], + }, + meta_data: { + meta_title: 'Sudoku Solver & Generator - Online Tool', + meta_description: + 'Solve Sudoku puzzles instantly and generate new ones with difficulty levels. Free online tool.', + og_title: 'Sudoku Solver & Generator - Developer Utility', + og_description: + 'Solve and generate Sudoku puzzles. Features Easy, Medium, and Hard levels.', + og_image: '/images/og-images/Cover.png', + }, + }, }; diff --git a/package-lock.json b/package-lock.json index ec0383b..5f5d810 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,6 +54,10 @@ "semantic-release": "^23.0.8", "tailwindcss": "^3.4.1", "typescript": "^5" + }, + "engines": { + "node": ">=20.8.0", + "npm": ">=10.0.0" } }, "node_modules/@aashutoshrathi/word-wrap": {