diff --git a/sudoku-app/src/App.js b/sudoku-app/src/App.js
index 0f42af2..9aa047a 100644
--- a/sudoku-app/src/App.js
+++ b/sudoku-app/src/App.js
@@ -5,11 +5,13 @@ import Theme from './Theme.js';
import Sudoku from './logic/SudokuGenerator.js';
import Header from './Header.js';
import Buttons from './Buttons.js';
-import { sudokuSolver, sudokuChecker } from './logic/SudokuSolver.js';
+import { sudokuSolver, sudokuChecker, generateErrorGrid } from './logic/SudokuSolver.js';
function App() {
- const [grid, setGrid] = useState(Array(9).fill(Array(9).fill('')));
- const [givenGrid, setGivenGrid] = useState(Array(9).fill(Array(9).fill('')));
+ const [grid, setGrid] = useState(new Array(9).fill().map(() => new Array(9).fill('')));
+ const [givenGrid, setGivenGrid] = useState(new Array(9).fill().map(() => new Array(9).fill('')));
+ const [errorsGrid, setErrorsGrid] = useState(new Array(9).fill().map(() => new Array(9).fill(false)));
+ const [errorsVisibility, setErrorsVisibility] = useState(false)
const [solution, setSolution] = useState([]);
const [remainingTime, setRemainingTime] = useState(0);
const [timerId, setTimerId] = useState(null)
@@ -46,6 +48,8 @@ function App() {
);
setGrid(newGrid);
+ setErrorsGrid(generateErrorGrid(newGrid))
+
if (isFullyFilled(newGrid)) {
if (sudokuChecker(newGrid.map(row => row.slice()))) {
alert("Sudoku solved!");
@@ -79,6 +83,7 @@ function App() {
setGivenGrid(sudoku.mat);
setGrid(sudoku.mat);
setSolution([]);
+ setErrorsGrid(new Array(9).fill().map(() => new Array(9).fill(false)));
};
const solveForMe = () => {
@@ -92,6 +97,10 @@ function App() {
stopTimer()
};
+ const toggleErrors = () => {
+ setErrorsVisibility(!errorsVisibility)
+ }
+
const startTimer = (minutes) => {
setRemainingTime(minutes * 60);
};
@@ -104,11 +113,13 @@ function App() {
handleGridChange(rowIndex, colIndex, value)}
+ errorsVisibility={errorsVisibility}
/>
@@ -116,6 +127,8 @@ function App() {
solveForMe={solveForMe}
startTimer={startTimer}
remainingTime={remainingTime}
+ toggleErrors={toggleErrors}
+ errorsVisibility={errorsVisibility}
/>
diff --git a/sudoku-app/src/Buttons.js b/sudoku-app/src/Buttons.js
index 82c80ff..d896717 100644
--- a/sudoku-app/src/Buttons.js
+++ b/sudoku-app/src/Buttons.js
@@ -1,6 +1,6 @@
import { useEffect } from "react";
-function Buttons({ startTimer, handleDifficultySelection, solveForMe, remainingTime }) {
+function Buttons({ startTimer, handleDifficultySelection, solveForMe, remainingTime, toggleErrors, errorsVisibility }) {
const modes = ["easy", "medium", "hard"];
useEffect(() => {
@@ -16,6 +16,7 @@ function Buttons({ startTimer, handleDifficultySelection, solveForMe, remainingT
+
Time remaining: {Math.floor(remainingTime / 60)}:{(remainingTime % 60).toString().padStart(2, '0')}
diff --git a/sudoku-app/src/SudokuGrid.css b/sudoku-app/src/SudokuGrid.css
index 6e3aff2..994717e 100644
--- a/sudoku-app/src/SudokuGrid.css
+++ b/sudoku-app/src/SudokuGrid.css
@@ -19,11 +19,11 @@
}
.r-border {
- border-right: 2px solid rgb(49, 43, 51);
+ border-right: 3px solid rgb(49, 43, 51);
}
.t-border {
- border-top: 2px solid rgb(49, 43, 51);
+ border-top: 3px solid rgb(49, 43, 51);
}
.sudoku-cell-given {
@@ -51,4 +51,9 @@
.button {
font-size: 1rem;
}
+}
+
+input.cell-error {
+ color: red;
+ caret-color: black;
}
\ No newline at end of file
diff --git a/sudoku-app/src/SudokuGrid.js b/sudoku-app/src/SudokuGrid.js
index 183e09c..54ecca4 100644
--- a/sudoku-app/src/SudokuGrid.js
+++ b/sudoku-app/src/SudokuGrid.js
@@ -1,6 +1,6 @@
import './SudokuGrid.css';
-const SudokuGrid = ({ givenGrid, grid, solution, handleInputChange }) => {
+const SudokuGrid = ({ errorsGrid, givenGrid, grid, solution, handleInputChange, errorsVisibility }) => {
return (
{grid.map((rowArray, rowIndex) => (
@@ -11,8 +11,9 @@ const SudokuGrid = ({ givenGrid, grid, solution, handleInputChange }) => {
type="text"
className={
`sudoku-cell
- ${rowIndex === 2 || rowIndex === 5 ? "t-border" : ""}
- ${colIndex === 2 || colIndex === 5 ? "r-border" : ""}`}
+ ${rowIndex === 3 || rowIndex === 6 ? "t-border" : ""}
+ ${colIndex === 2 || colIndex === 5 ? "r-border" : ""}
+ ${errorsVisibility && errorsGrid[rowIndex][colIndex] ? "cell-error" : ""}`}
value={solution.length > 0 ? solution[rowIndex][colIndex] : cellValue}
maxLength="1"
readOnly={givenGrid[rowIndex][colIndex] !== ""}
@@ -28,9 +29,3 @@ const SudokuGrid = ({ givenGrid, grid, solution, handleInputChange }) => {
};
export default SudokuGrid;
-
-
-
-
-
-
diff --git a/sudoku-app/src/logic/SudokuGenerator.js b/sudoku-app/src/logic/SudokuGenerator.js
index 75af2b8..f573e84 100644
--- a/sudoku-app/src/logic/SudokuGenerator.js
+++ b/sudoku-app/src/logic/SudokuGenerator.js
@@ -30,7 +30,7 @@ export default class Sudoku {
let index = 0;
for (let i = 0; i < this.SRN; i++) {
for (let j = 0; j < this.SRN; j++) {
- this.mat[row + i][col + j] = numbers[index];
+ this.mat[row + i][col + j] = `${numbers[index]}`;
index++;
}
}
diff --git a/sudoku-app/src/logic/SudokuSolver.js b/sudoku-app/src/logic/SudokuSolver.js
index 23b78e5..16cff08 100644
--- a/sudoku-app/src/logic/SudokuSolver.js
+++ b/sudoku-app/src/logic/SudokuSolver.js
@@ -23,7 +23,7 @@ const isEmpty = grid => grid === '';
* @returns {boolean} returns a boolean that determines if
* the puzzle was solved.
*/
-export function sudokuSolver(data) {
+function sudokuSolver(data) {
for (let row = 0; row < 9; row++) {
for (let col = 0; col < 9; col++) {
if (!isEmpty(data[row][col])) continue;
@@ -45,7 +45,7 @@ export function sudokuSolver(data) {
}
-export function sudokuChecker(board) {
+function sudokuChecker(board) {
const AVAILABLE_NUMBERS = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let set = new Set();
@@ -54,16 +54,16 @@ export function sudokuChecker(board) {
if (parseInt(board[row][col] == NaN |
!AVAILABLE_NUMBERS.includes(parseInt(board[row][col]))) |
isEmpty(board[row][col])) return false;
-
+
if (set.has(
"row" + row + "" + board[row][col],
"col" + col + "" + board[row][col],
- "grid" + Math.ceil((row + 1)/3.0) + Math.ceil((col + 1)/3.0) + board[row][col])) return false;
+ "grid" + Math.ceil((row + 1) / 3.0) + Math.ceil((col + 1) / 3.0) + board[row][col])) return false;
else {
set.add(
"row" + row + "" + board[row][col],
"col" + col + "" + board[row][col],
- "grid" + Math.ceil((row + 1)/3.0) + Math.ceil((col + 1)/3.0) + board[row][col]);
+ "grid" + Math.ceil((row + 1) / 3.0) + Math.ceil((col + 1) / 3.0) + board[row][col]);
}
}
}
@@ -71,3 +71,36 @@ export function sudokuChecker(board) {
return true;
}
+/**
+ * Generate error grid that maps to grid data
+ * @param {string[][]} data 2D array of number strings in sudoku board
+ * @returns {boolean[][]} 2D array of boolean with true as invalid cells and false as valid cells
+ */
+function generateErrorGrid(data) {
+ const errors = new Array(9).fill().map(() => new Array(9).fill(0));
+
+ // Adapted from isValid function above
+ const isValid = (row, col, k) => {
+ for (let i = 0; i < 9; i++) {
+ const m = 3 * Math.floor(row / 3) + Math.floor(i / 3);
+ const n = 3 * Math.floor(col / 3) + i % 3;
+ // Ignore the same (row, col) to not mark it as invalid
+ if ((data[row][i] == k && i !== col) || (data[i][col] == k && i !== row) || (data[m][n] == k && (m !== row || n !== col))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ for (let row = 0; row < 9; row++) {
+ for (let col = 0; col < 9; col++) {
+ if (isEmpty(data[row][col])) continue;
+ if (!isValid(row, col, data[row][col])) {
+ errors[row][col] = true;
+ }
+ }
+ }
+ return errors;
+}
+
+export { sudokuSolver, isValid, sudokuChecker, generateErrorGrid }