diff --git a/Calculators/Sudoku-Calculator/README.md b/Calculators/Sudoku-Calculator/README.md new file mode 100644 index 000000000..ee700a1d1 --- /dev/null +++ b/Calculators/Sudoku-Calculator/README.md @@ -0,0 +1,19 @@ +#

Sudoku Calculator

+ +## Description :- + +- Generates and Calculates Sudoku +- Can solve user given sudoko's as well + +## Tech Stacks :- + +- HTML +- CSS +- JavaScript +- ConfettiJS Library for confetti effect after solving + +## Screenshots :- + +![image](../Sudoku-Calculator/screenshot1.png) +![image](../Sudoku-Calculator/screenshot2.png) +![image](../Sudoku-Calculator/screenshot3.png) \ No newline at end of file diff --git a/Calculators/Sudoku-Calculator/index.html b/Calculators/Sudoku-Calculator/index.html new file mode 100644 index 000000000..e781605aa --- /dev/null +++ b/Calculators/Sudoku-Calculator/index.html @@ -0,0 +1,166 @@ + + + + + + + + + + + + + Sudoku Calculator + + + + + + + + + + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ + + + \ No newline at end of file diff --git a/Calculators/Sudoku-Calculator/screenshot1.png b/Calculators/Sudoku-Calculator/screenshot1.png new file mode 100644 index 000000000..ecca64b24 Binary files /dev/null and b/Calculators/Sudoku-Calculator/screenshot1.png differ diff --git a/Calculators/Sudoku-Calculator/screenshot2.png b/Calculators/Sudoku-Calculator/screenshot2.png new file mode 100644 index 000000000..514ff2ffc Binary files /dev/null and b/Calculators/Sudoku-Calculator/screenshot2.png differ diff --git a/Calculators/Sudoku-Calculator/screenshot3.png b/Calculators/Sudoku-Calculator/screenshot3.png new file mode 100644 index 000000000..110856e7e Binary files /dev/null and b/Calculators/Sudoku-Calculator/screenshot3.png differ diff --git a/Calculators/Sudoku-Calculator/script.js b/Calculators/Sudoku-Calculator/script.js new file mode 100644 index 000000000..54555227f --- /dev/null +++ b/Calculators/Sudoku-Calculator/script.js @@ -0,0 +1,679 @@ +// Get some element from html +const subMenu = document.querySelector("#nav-bar").children[4].children[1]; +const speedButton = document.querySelector("#nav-bar").children[4].children[0]; +const liAroundSpeedDropdownMenu = document.querySelector("#nav-bar").children[4]; +const solve = document.querySelector("#solve"); +const clear = document.querySelector("#clear"); +const randomlyFill = document.querySelector("#randomly-fill"); +const grid = document.querySelector("#grid"); +const inputs = document.getElementsByTagName("input"); + +// START Dropdown menu (Speed) +const speedDropDown = document.querySelector("span.selected"); +const speedOptions = document.querySelectorAll(".speed-options"); +speedOptions.forEach((e) => { + e.addEventListener("click", () => { + let value = e.innerHTML; + speedDropDown.innerHTML = value; + }); +}); +// DONE Dropdown menu (Speed) + + +// CONSTANT SPEED (The lower the faster. It actually is the time lapse between 2 animation) +const FAST_SPEED = 0.4; +const MEDIUM_SPEED = 10; +const SLOW_SPEED = 50; +const EXTRA_SLOW_SPEED = 150; + +// Add eventListener +clear.addEventListener("click", clickedClear); +randomlyFill.addEventListener("click", clickedRandomlyFill); +solve.addEventListener("click", clickedSolve); + +//-------------------------------------------------START ClickedClear------------------------------------------------- +//-------------------------------------------------START ClickedClear------------------------------------------------- +//-------------------------------------------------START ClickedClear------------------------------------------------- + +// This function clears all timeouts, animation colors and allow to press Solve and Speed again +function clickedClear(e) { + clearAllTimeOuts(); + clearAllColors(); + setAllowSolveSpeedAndAlgorithms(); + clear.textContent = "Clear"; + // clear.style.backgroundColor = ""; + speedDropDown.innerHTML = "Speed"; + for (let i = 0; i < 9; i++) { + for (let j = 0; j < 9; j++) { + grid.rows[i].cells[j].firstChild.value = ""; + } + } +} + +function clickedClearExceptAlgoInfo() { + clearAllTimeOuts(); + clearAllColors(); + setAllowSolveSpeedAndAlgorithms(); + for (let i = 0; i < 9; i++) { + for (let j = 0; j < 9; j++) { + grid.rows[i].cells[j].firstChild.value = ""; + } + } +} + +// This function delete all timeOut (animations) +function clearAllTimeOuts() { + while (timeOutIDSameForAnyAnimation >= 0) { + clearTimeout(timeOutIDSameForAnyAnimation); + timeOutIDSameForAnyAnimation--; + } +} + +// Clear all colors from animations +function clearAllColors() { + for (let i = 0; i < inputs.length; i++) { + inputs[i].classList.remove("active"); + inputs[i].classList.remove("succeeded"); + } +} + +// Allow to click solve, choose speed and algorithms again +function setAllowSolveSpeedAndAlgorithms() { + solve.setAttribute("style", "cursor: pointer"); // Allow to click solve button + solve.addEventListener("click", clickedSolve); // Add back eventListener for solve button + clear.textContent = "Clear"; + clear.style.backgroundColor = ""; + clear.removeAttribute("title"); + + solve.setAttribute("style", "cursor: pointer"); + randomlyFill.setAttribute("style", "cursor: pointer"); + liAroundSpeedDropdownMenu.setAttribute("style", "cursor: pointer"); // enable dropdown (pointerEvent) +} + +// Not allow to click solve, choose speed and algorithms +function setNotAllowSolveSpeedAndAlgorithms() { + // solve.style.backgroundColor = "red"; // Turn solve button to red + clear.textContent = "Stop & Clear"; + clear.setAttribute("title", "Sudoku solving is in process."); + + solve.style.cursor = "not-allowed"; + solve.removeEventListener("click", clickedSolve); // Remove any function when click + + solve.setAttribute("style", "pointer-events: none"); + randomlyFill.setAttribute("style", "pointer-events: none"); + liAroundSpeedDropdownMenu.setAttribute("style", "pointer-events: none"); // Cannot click Speed menu +} + +//--------------------------=================== DONE ClickedClear =================---------------------------- +//--------------------------=================== DONE ClickedClear =================---------------------------- + +//--------------------------================ START clickedRandomlyFill ================---------------------------- +//--------------------------================ START clickedRandomlyFill ================--------------------------- + +// This function is called when we click the "Randomly-fill" button +function clickedRandomlyFill(e) { + clickedClearExceptAlgoInfo(); // Clear the board first + fill80Succeed20NotSure(); +} + +// Fill the board with 80% probability that we will have a solution and 20% truly random +function fill80Succeed20NotSure() { + if (Math.random() < 0.8) { + // 80% guaranttee solution + hasSolutionMatrix = [ + [8, 2, 5, 1, 9, 7, 3, 4, 6], + [6, 1, 7, 3, 4, 2, 9, 5, 8], + [4, 3, 9, 6, 8, 5, 7, 1, 2], + [1, 9, 6, 5, 3, 8, 2, 7, 4], + [2, 8, 3, 7, 6, 4, 5, 9, 1], + [5, 7, 4, 9, 2, 1, 8, 6, 3], + [7, 6, 1, 2, 5, 3, 4, 8, 9], + [9, 4, 2, 8, 7, 6, 1, 3, 5], + [3, 5, 8, 4, 1, 9, 6, 2, 7], + ]; + newSudokuQuiz = mixSudokuQuiz(hasSolutionMatrix); + printBoardOnWeb(newSudokuQuiz); + } // The rest 20% Just randomly fill + else { + matrix = generateRandomBoard(); // This is random + printBoardOnWeb(matrix); + } +} + +// This function randomly swaps rows and columns of a sudoku board with a specific rule +// Rule: If a sudoku board has a solution, if we swap 2 rows (or 2 columns) within the same +// 3x9 (or 9x3) "rectangle", our sudoku will preserve its solvability +function mixSudokuQuiz(matrix) { + let numEntries = 20 + Math.floor(Math.random() * 8); // Number of entries to be kept + mixRowsAndColumns(matrix); // Mix board + keepSomeEntries(matrix, numEntries); // Keep some random Entries + return matrix; +} + +// This function randomly swaps different rows (or columns) with the "appropriate" rows(or columns) +function mixRowsAndColumns(matrix) { + let numSwap = Math.floor(Math.random() * 15) + 1; // Swap 1-10 times + while (numSwap > 0) { + let num1 = Math.floor(Math.random() * 9); // Pick a row (or column) from 0 to 8 + let num2 = Math.floor(num1 / 3) * 3 + Math.floor(Math.random() * 3); // Pick another row (column) in the right range + if (Math.random() < 0.5) { + swapRow(matrix, num1, num2); + } else { + swapCol(matrix, num1, num2); + } + numSwap--; + } +} + +// Randomly keep some entries out of a full sudoku board +function keepSomeEntries(matrix, numEntriesKeep) { + let numEntriesDelete = 81 - numEntriesKeep; + for (let i = 0; i < numEntriesDelete; i++) { + while (true) { + let row = Math.floor(Math.random() * 9); + let col = Math.floor(Math.random() * 9); + if (matrix[row][col] != 0) { + matrix[row][col] = 0; + break; + } + } + } +} + +// Swap 2 row +function swapRow(matrix, row1, row2) { + for (let i = 0; i < 9; i++) { + let temp = matrix[row1][i]; + matrix[row1][i] = matrix[row2][i]; + matrix[row2][i] = temp; + } +} + +// Swap 2 col +function swapCol(matrix, col1, col2) { + for (let i = 0; i < 9; i++) { + let temp = matrix[i][col1]; + matrix[i][col1] = matrix[i][col2]; + matrix[i][col2] = temp; + } +} + +// This function actually generate a random board +function generateRandomBoard() { + let numFill = 20 + Math.floor(Math.random() * 8); + let matrix = new Array(9); + + for (let i = 0; i < 9; i++) { + matrix[i] = new Array(9); + for (let j = 0; j < 9; j++) { + matrix[i][j] = ""; + } + } + + while (true) { + if (numFill === 0) break; + let i = Math.floor(Math.random() * 9); + let j = Math.floor(Math.random() * 9); + if (matrix[i][j] == "") { + matrix[i][j] = Math.floor(Math.random() * 9) + 1; + if (canBeCorrect(matrix, i, j)) numFill--; + else matrix[i][j] = ""; + } + } + return matrix; +} + +//---------------------------------------------DONE clickedRandomlyFill---------------------------------------------- +//---------------------------------------------DONE clickedRandomlyFill---------------------------------------------- +//---------------------------------------------DONE clickedRandomlyFill---------------------------------------------- + +//------------------------------------------------START clickedSolve------------------------------------------------- +//------------------------------------------------START clickedSolve------------------------------------------------- +//------------------------------------------------START clickedSolve------------------------------------------------- + +// This function is called when we click the "Solve" button +// It will call the proper algorithms, and using the proper speed +// By default, it will use Backtracking at Medium Speed +function clickedSolve(e) { + // Verify input first + if (verifyInput() == false) { + for (let i = 0; i < 81; i++) { + inputs[i].addEventListener("click", function () { + inputs[i].style.border = ""; + inputs[i].classList.remove("shake"); + }); + } + return; + } + + if (speedDropDown.innerHTML === "Speed") + // If haven't set speed + speedDropDown.innerHTML = "Medium"; // Set to medium + + + let currentAlgo = "Backtracking"; + + solveByBacktracking(e, currentAlgo); +} + +//------------------------------------------------START Backtracking------------------------------------------------- +//------------------------------------------------START Backtracking------------------------------------------------- +//------------------------------------------------START Backtracking------------------------------------------------- +function solveByBacktracking(e, currentAlgo) { + backtrackingCountToPreventHanging = 0; + setNotAllowSolveSpeedAndAlgorithms(); // Disable some buttons + let matrix = readValue(); // Read values from web board + + backtracking(matrix, currentAlgo); // Solving sudoku + + let timeAfterAllDone = ++backtrackingTimeCount * backtrackingDuration; + + if (allBoardNonZero(matrix)) { + // If We actually have a solution + if (currentAlgo === "Backtracking") + succeededNormalAnimation( + backtrackingTimeCount, + backtrackingDuration + ); + } else { + timeOutIDSameForAnyAnimation = setTimeout( + alertNoSolution, + timeAfterAllDone + ); + timeOutIDSameForAnyAnimation = setTimeout( + setAllowSolveSpeedAndAlgorithms, + timeAfterAllDone + ); + } +} + +var backtrackingCountToPreventHanging = 0; +var backtrackingDuration = 1; +var backtrackingTimeCount = 0; +var timeOutIDSameForAnyAnimation = 0; +function backtracking(matrix, currentAlgo) { + // Setting Speed + backtrackingDuration = MEDIUM_SPEED; + if (speedDropDown.innerHTML === "Fast") backtrackingDuration = FAST_SPEED; + else if (speedDropDown.innerHTML === "Medium") + backtrackingDuration = MEDIUM_SPEED; + else if (speedDropDown.innerHTML === "Slow") + backtrackingDuration = SLOW_SPEED; + else if (speedDropDown.innerHTML === "Extra Slow") + backtrackingDuration = EXTRA_SLOW_SPEED; + + backtrackingTimeCount = 0; // Time count for scheduling animation + + // Find out which entries are user input (isFixed===true), which are empty (isFixed===false) + let isFixed = new Array(9); + for (let i = 0; i < isFixed.length; i++) { + isFixed[i] = new Array(9); + for (let j = 0; j < isFixed[i].length; j++) { + if (matrix[i][j] !== 0) { + isFixed[i][j] = true; + } else { + isFixed[i][j] = false; + } + } + } + let data = { cont: true }; + let startingRow = -1; + let startingCol = -1; + if (currentAlgo === "Backtracking") { + startingRow = 0; + startingCol = 0; + } + backtrackingHelper( + matrix, + isFixed, + startingRow, + startingCol, + data, + currentAlgo + ); +} + +function backtrackingHelper(matrix, isFixed, row, col, data, currentAlgo) { + // If !data.cont or having our current entry at (row, col) lead to a clearly invalid sudoku board + if (data.cont === false || !canBeCorrect(matrix, row, col)) + // 1st stopping point + return; + + // Backtracking is a naive solution. + backtrackingCountToPreventHanging++; + + if (backtrackingCountToPreventHanging > 100000) { + // Runs for too long without a solution + data.cont = false; // Set the flag so that the rest of the recursive calls can stop at "stopping points" + stopSolveSudokuBacktracking(currentAlgo); // Stop the program + return; + } + + if (currentAlgo === "Backtracking" && row === 8 && col === 8) { + // If reach the last entry + if (isFixed[row][col]) { + // The last entry is user input + if (canBeCorrect(matrix, row, col)) { + // And it doesn't create an invalid board + data.cont = false; // Yesss!! Found the solution! + } + return; + } // If it is not user input + else { + for (let i = 1; i <= 9; i++) { + matrix[row][col] = i; // Try 1-9 + timeOutIDSameForAnyAnimation = setTimeout( + fillCell, + backtrackingTimeCount++ * backtrackingDuration, + row, + col, + i + ); + if (canBeCorrect(matrix, row, col)) { + // If found the solution + data.cont = false; + return; + } + } + timeOutIDSameForAnyAnimation = setTimeout( + emptyCell, + backtrackingTimeCount++ * backtrackingDuration, + row, + col + ); + matrix[row][col] = 0; // Otherwise, backtrack, reset the current entry to 0 + } + } + + // Compute newRow and new Column coressponding to currentAlgo + let newRow = -1; + let newCol = -1; + if (currentAlgo === "Backtracking") { + // Fill from left to right, from top to bottom + newRow = col === 8 ? row + 1 : row; + newCol = col === 8 ? 0 : col + 1; + } + + // If this entry is user input and is valid + if (isFixed[row][col] && canBeCorrect(matrix, row, col)) { + backtrackingHelper(matrix, isFixed, newRow, newCol, data, currentAlgo); // Continue next entry + } + // If it is empty + else { + for (let i = 1; i <= 9; i++) { + if (data.cont === false) + // Stopping entry 2 + return; + timeOutIDSameForAnyAnimation = setTimeout( + fillCell, + backtrackingTimeCount++ * backtrackingDuration, + row, + col, + i + ); + matrix[row][col] = i; // Try 1-9 + + if (canBeCorrect(matrix, row, col)) { + // If any of those values (1-9) can be valid + backtrackingHelper( + matrix, + isFixed, + newRow, + newCol, + data, + currentAlgo + ); // recursively move on to the next cell + } + } + if (data.cont === false) + // Stopping entry 3 + return; + timeOutIDSameForAnyAnimation = setTimeout( + emptyCell, + backtrackingTimeCount++ * backtrackingDuration, + row, + col + ); + matrix[row][col] = 0; // Backtrack, set entry to 0 + } +} + +// This function is called when backtracking function is running for too long +// It will stop the function to prevent hanging +function stopSolveSudokuBacktracking(currentAlgo) { + if (currentAlgo === "Backtracking") { + alert( + "Backtracking is a Naive Algorithm. It tends to do well when the majority of entries near the top are prefilled.\nThe program is taking too long to find a solution. It will be terminated to prevent hanging." + ); + } + clickedClear(); +} + +// Normal animation when we have found the solution +function succeededNormalAnimation(currentTimeCount, currentDuration) { + let currentTime = currentTimeCount * currentDuration; + let succeededDuration = 20; + let newCount = 0; + for (let row = 0; row < 9; row++) { + for (let col = 0; col < 9; col++) { + timeOutIDSameForAnyAnimation = setTimeout( + colorCell, + currentTime + newCount++ * succeededDuration, + row, + col + ); + } + } + + timeOutIDSameForAnyAnimation = setTimeout( + setAllowSolveSpeedAndAlgorithms, + currentTime + newCount++ * succeededDuration + ); +} + +//------------------------------------------------END Backtracking------------------------------------------------- +//------------------------------------------------END Backtracking------------------------------------------------- +//------------------------------------------------END Backtracking------------------------------------------------- + +// Count possibilities for an entry +function countChoices(matrix, i, j) { + let canPick = [true, true, true, true, true, true, true, true, true, true]; // From 0 to 9 - drop 0 + + // Check row + for (let k = 0; k < 9; k++) { + canPick[matrix[i][k]] = false; + } + + // Check col + for (let k = 0; k < 9; k++) { + canPick[matrix[k][j]] = false; + } + + // Check 3x3 square + let r = Math.floor(i / 3); + let c = Math.floor(j / 3); + for (let row = r * 3; row < r * 3 + 3; row++) { + for (let col = c * 3; col < c * 3 + 3; col++) { + canPick[matrix[row][col]] = false; + } + } + + // Count + let count = 0; + for (let k = 1; k <= 9; k++) { + if (canPick[k]) count++; + } + + return count; +} + +function noSolutionFromStart(matrix) { + for (let i = 0; i < 9; i++) { + for (let j = 0; j < 9; j++) { + if (!canBeCorrect(matrix, i, j)) + // If one entry cannot be correct right from the start + return true; // Stop solving + } + } + return false; +} + +//-------------------------------------------------END clickedSolve-------------------------------------------------- +//-------------------------------------------------END clickedSolve-------------------------------------------------- +//-------------------------------------------------END clickedSolve-------------------------------------------------- + +function emptyCell(row, col) { + inputs[row * 9 + col].classList.remove("active"); + grid.rows[row].cells[col].firstChild.value = ""; +} + +function fillCell(row, col, val) { + inputs[row * 9 + col].classList.add("active"); + grid.rows[row].cells[col].firstChild.value = val; +} + + +const jsConfetti = new JSConfetti(); +function colorCell(row, col) { + inputs[row * 9 + col].classList.add("succeeded"); + + jsConfetti.addConfetti({ + confettiRadius: 6, + confettiNumber: 5, + }); + setTimeout(function () { + jsConfetti.clearCanvas(); + }, 50 * 100); // to avoid system crash due to large amount of confetti +} + +function canBeCorrect(matrix, row, col) { + // Check row + for (let c = 0; c < 9; c++) { + if ( + matrix[row][col] !== 0 && + col !== c && + matrix[row][col] === matrix[row][c] + ) + return false; + } + + // Check column + for (let r = 0; r < 9; r++) { + if ( + matrix[row][col] !== 0 && + row !== r && + matrix[row][col] === matrix[r][col] + ) + return false; + } + + // Check 3x3 square + let r = Math.floor(row / 3); + let c = Math.floor(col / 3); + for (let i = r * 3; i < r * 3 + 3; i++) { + for (let j = c * 3; j < c * 3 + 3; j++) { + if ( + (row !== i || col !== j) && + matrix[i][j] !== 0 && + matrix[i][j] === matrix[row][col] + ) + return false; + } + } + + return true; +} + +function allBoardNonZero(grid) { + for (let i = 0; i < 9; i++) { + for (let j = 0; j < 9; j++) { + if (grid[i][j] === 0) return false; + } + } + return true; +} + +// Read value from web board to 2d array +function readValue() { + let matrix = new Array(9); + for (let i = 0; i < 9; i++) { + matrix[i] = new Array(9); + for (let j = 0; j < 9; j++) { + val = grid.rows[i].cells[j].firstChild.value; + matrix[i][j] = val === "" ? 0 : parseInt(val); + } + } + return matrix; +} + +document.addEventListener("DOMContentLoaded", function () { + for (let i = 0; i < 9; i++) { + for (let j = 0; j < 9; j++) { + // let val = grid.rows[i].cells[j].firstChild.value; + let input = grid.rows[i].cells[j].firstChild; + // console.log(input); + input.addEventListener("input", function () { + const val = input.value; + if (val === "") { + input.classList.remove("invalid"); + input.classList.remove("shake"); + } else if (!/^[1-9]$/.test(val)) { + // Invalid input + input.classList.add("invalid", "shake"); + // Remove the shake animation after 5ms + setTimeout(() => { + input.classList.remove("shake"); + }, 500); // Duration should match the animation duration + } else { + // Valid input + input.classList.remove("invalid"); + input.classList.remove("shake"); + } + }); + } + } +}); + +// See if the input is valid +function verifyInput() { + for (let i = 0; i < 9; i++) { + for (let j = 0; j < 9; j++) { + let val = grid.rows[i].cells[j].firstChild.value; + if ( + (val != "" && Number.isNaN(parseInt(val))) || + 0 >= parseInt(val) || + 9 < parseInt(val) + ) { + inputs[i * 9 + j].style.border = "3px solid red"; + inputs[i * 9 + j].classList.add("shake"); + // alert("Please enter numbers from 1 to 9"); + + return false; + } + } + } + return true; +} + +// Get the current Algorithm from Algorithms dropdown menu +function getCurrentAlgorithm() { + let currentAlgo = "Backtracking"; // Default is Backtracking + + return currentAlgo; +} + +function alertNoSolution() { + alert("No Solution!"); +} + +function printBoardOnWeb(matrix) { + for (let i = 0; i < 9; i++) { + for (let j = 0; j < 9; j++) { + if (matrix[i][j] == 0) grid.rows[i].cells[j].firstChild.value = ""; + else grid.rows[i].cells[j].firstChild.value = matrix[i][j]; + } + } +} +/* ---------------------------========== END of program logics ============-------------------------- */ +/* ---------------------------========== END of program logics ============-------------------------- */ +/* ---------------------------========== END of program logics ============-------------------------- */ \ No newline at end of file diff --git a/Calculators/Sudoku-Calculator/style.css b/Calculators/Sudoku-Calculator/style.css new file mode 100644 index 000000000..85ac19c7b --- /dev/null +++ b/Calculators/Sudoku-Calculator/style.css @@ -0,0 +1,337 @@ +/* START General style */ + +* { + padding: 0px; + margin: 0px; +} + +a { + text-decoration: none; +} + +input { + color: rgb(102, 102, 102); + padding: 0; + margin: 0; +} + +.cell input.active { + border-color: rgb(141, 177, 255); + border-color: rgb(252, 93, 14); + color: #000 !important; +} + +.cell input.succeeded { + border: 4px solid rgb(25, 237, 57); +} + +.btn { + border: none; + box-sizing: border-box; + display: inline-block; + padding: 15px 20px; + text-decoration: none; + color: white; + background-color: rgb(49, 155, 255); + font-size: 20px; + cursor: pointer; +} + +.btn:hover { + background-color: rgb(0, 81, 255); + color: #fff; + font-weight: 600; +} + +.btn:active { + background-color: rgb(0, 81, 205); +} + +body { + background: linear-gradient(to right, rgb(41, 181, 231), rgb(238, 238, 83)); + /* background: #333; */ + font-family: cursive, Arial, Helvetica, sans-serif; +} + +/* END General style */ + + + +/* START Menu style */ +/* START Menu-style.General Style */ +#nav-bar { + background-color: #222; + text-align: center; + opacity: 0.9; + z-index: 2; + position: relative; +} + +#nav-bar li { + position: relative; + color: #fff; + padding: 15px 20px; + display: inline-block; + min-width: 105px; + font-size: 20px; + font-weight: bold; + transition: all 0.3s ease-in-out; +} + +/* END Menu-style.General Style */ + +/* START Menu-Style.heading "Sudoku Solver Visualizer"*/ +#nav-bar a { + display: grid; + align-items: center; + justify-content: center; + cursor: default; +} + +#nav-bar h2 { + color: white; + font-family: cursive; + padding: 10px; + font-size: 35px; + width: max-content; + /* margin-left: 50%; + transform: translateX(-50%); */ +} + +#nav-bar h2:hover { + text-decoration: underline; + cursor: pointer; +} + +/* END Menu-Style.heading "Sudoku Solver Visualizer"*/ + +/* START Menu-Style.Speed Dropdown */ +#nav-bar li:hover { + /* background-color: rgb(0, 81, 255); */ + /* background-color: rgb(201, 124, 9); */ + border-radius: 24px 24px 0 0; + background-color: white; + color: #222; + cursor: pointer; +} + +#nav-bar li:hover .sub-menu { + display: block; + /* transition: all 0.3s linear; */ +} + +/* Initial state */ +#nav-bar li:nth-child(5) i { + transform: rotate(0deg); + transition: transform 0.3s ease; +} + +/* Hovered state */ +#nav-bar li:nth-child(5):hover i { + transform: rotate(180deg); +} + +.sub-menu { + position: absolute; + left: 0; + top: 53px; + background-color: #333; + display: none; + text-align: center; + font-size: 20px; + font-weight: bold; + border-radius: 0 0 16px 16px; +} + +.sub-menu li.speed-options:hover { + /* border-radius: 0 0 16px 16px !important; */ + border-radius: 55% !important; + opacity: 0.9; +} + + +/* END Menu-Style.Speed Dropdown */ + +/* START Menu-Style.Algorithms dropdown */ +#nav-bar li:nth-child(6) { + min-width: 210px; +} + +/* Initial state */ +#nav-bar li:nth-child(6) i { + transform: rotate(0deg); + transition: transform 0.3s ease; +} + +/* Hovered state */ +#nav-bar li:nth-child(6):hover i { + transform: rotate(180deg); +} + + +#nav-bar li:hover .algo-sub-menu { + display: block; +} + +.algo-sub-menu { + position: absolute; + left: 0; + top: 53px; + background-color: #333; + display: none; + text-align: left; + font-size: 20px; + font-weight: bold; + min-width: 210px; + border-radius: 0 0 16px 16px; +} + +.algo-sub-menu li.algo-options:hover { + /* border-radius: 0 0 16px 16px !important; */ + border-radius: 55% !important; + opacity: 0.9; +} + +#nav-bar .algo-sub-menu li { + min-width: 210px; +} + +#nav-bar .algo-selected { + min-width: 210px; +} + +#nav-bar li#clear { + transition: all 0.3s ease-in-out; +} + +/* END Menu-Style.Algorithms dropdown */ + + +/* START main div style */ +/* START main-div.General Style */ + + +#main { + margin-top: 40px; + /* position: relative; */ + text-align: center; + z-index: 0; +} + +/* END main-div.General Style */ + +/* START main-div.Table style */ +#grid { + /* margin: auto; */ + margin: auto; + border-collapse: collapse; + width: 486px; + height: 495px; + border: 1px solid rgb(255, 100, 100); + box-shadow: 0 0 10px 5px rgba(95, 223, 223, 0.827), + 0 0 20px 10px rgba(28, 28, 28, 0.615); +} + +table { + margin: 1em auto; +} + +td { + height: 30px; + width: 30px; + /* border:3px solid rgb(0, 0, 0); */ + text-align: center; + padding: 0; + margin:0px; +} + +td:first-child { + border-left: 0px solid rgb(24, 24, 24); + /* color: #1728dfdf; */ +} + +td:nth-child(3n) { + border-right: 3px solid rgb(0, 0, 0); +} + +tr:first-child { + border-top: 0px solid rgb(24, 24, 24); +} + +tr:nth-child(3n) td { + border-bottom: 0px solid rgb(24, 24, 24); +} + +.cell { + border: 2px solid black; + transition: scale 0.25s ease; +} + +.cell:hover { + scale: 1.1; +} + +.cell input { + opacity: 0.9; + display: inline-block; + border-radius: 8px; + /* margin: auto; */ + margin: 0px; + width: 45px; + height: 45px; + + text-align: center; + font-size: 35px; + outline: none; + /* cursor: default; */ + + border: 2px solid; +} + +/* Class to apply the shake animation */ +.shake { + animation: shake 0.5s ease; +} + +/* Keyframes for the shake animation */ +@keyframes shake { + 0% { + transform: translateX(0); + } + + 25% { + transform: translateX(-5px); + } + + 50% { + transform: translateX(5px); + } + + 75% { + transform: translateX(-5px); + } + + 100% { + transform: translateX(0); + } +} + +/* When invalid input entered by user */ +.invalid { + border: 3px solid red !important; +} + + +/* END main-div.Table style */ + +@media screen and (max-width:1320px) { + #algo-info{ + display: none; + } + } + +@media screen and (max-width:767px) { + #algo-info{ + display: none; + } + } \ No newline at end of file