I'm coding a sudoku solver by backtrack algorithm.
Why does the slice() method in my code return a reference? I know that slice method will return a reference if the array is not Number, String or Boolean. But in my code, the array is filled with 0, how it's possible?
backtrack() {
if (this.state.IsEnd === true)
return true;
const board = this.state.squares.slice(); //board array is the reference of this.state.squares array;
let row, col;
if (!this.findvalid()) {
this.setState({
IsEnd: true
});
return true;
}
let ok = false;
for (let i = 0; i < 9; i++) {
if (ok)
break;
for (let j = 0; j < 9; j++) {
if (board[i][j] === 0 || isNaN(board[i][j])) {
row = i;
col = j;
ok = true;
break;
}
}
}
for (let num = 1; num <= 9; num++) {
if (this.isSafe(row, col, num)) {
board[row][col] = num; /*when board changes,the state squares also changes.*/
this.setState({
squares: board
}); /*when I delete this line,every thing is still the same.*/
if (this.backtrack()) {
return true;
}
board[row][col] = 0;
this.setState({
squares: board
});
}
}
return false;
}
this is my full code
//this function create a board
function createBoard() {
let board = Array(9);
for (let i = 0; i < 9; i++) {
let subarray = Array(9).fill(0);
board[i] = subarray;
}
return board;
}
class App_sudoku_solver extends React.Component {
constructor(props) {
super(props);
this.state = {
squares: createBoard(),
IsEnd: false
}
this.handleChange = this.handleChange.bind(this);
this.SafeBox = this.SafeBox.bind(this);
this.SafeCol = this.SafeCol.bind(this);
this.SafeRow = this.SafeRow.bind(this);
this.isSafe = this.isSafe.bind(this);
this.findvalid = this.findvalid.bind(this);
this.backtrack = this.backtrack.bind(this);
this.solve = this.solve.bind(this);
this.check = this.check.bind(this);
this.restart = this.restart.bind(this);
}
//check if any cell is empty
findvalid() {
const board = this.state.squares.slice();
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
if (board[i][j] === 0 || isNaN(board[i][j]))
return true;
}
}
return false;
}
// check valid 3x3 squares
SafeBox(startrow, startcol, num) {
const board = this.state.squares.slice();
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (board[startrow + i][startcol + j] === num)
return false;
}
}
return true;
}
//check valid row
SafeRow(row, num) {
const board = this.state.squares.slice();
for (let i = 0; i < 9; i++) {
if (board[row][i] === num)
return false;
}
return true;
}
//check valid column
SafeCol(col, num) {
const board = this.state.squares.slice();
for (let i = 0; i < 9; i++) {
if (board[i][col] === num)
return false;
}
return true;
}
//check if this number is valid in the cell
isSafe(row, col, num) {
return (this.SafeRow(row, num) === true && this.SafeCol(col, num) === true && this.SafeBox(row - row % 3, col - col % 3, num) === true);
}
//solve sudoku by backtrack
backtrack() {
if (this.state.IsEnd === true)
return true;
const board = this.state.squares.slice();
let row, col;
if (!this.findvalid()) {
this.setState({
IsEnd: true
});
return true;
}
let ok = false;
for (let i = 0; i < 9; i++) {
if (ok)
break;
for (let j = 0; j < 9; j++) {
if (board[i][j] === 0 || isNaN(board[i][j])) {
row = i;
col = j;
ok = true;
break;
}
}
}
for (let num = 1; num <= 9; num++) {
if (this.isSafe(row, col, num)) {
board[row][col] = num;
this.setState({
squares: board
});
if (this.backtrack()) {
return true;
}
board[row][col] = 0;
this.setState({
squares: board
});
}
}
return false;
}
this.state.squares is an array of arrays, it is not filled with 0
If the array you are running slice on contains any objects, the method will only copy a reference to them. Since your array contains arrays - and thus, objects, all it's elements are copied only by reference.
function createBoard() {
let board = new Array(9);
for (let i = 0; i < 9; i++) {
let subarray = Array(9).fill(0);
board[i] = subarray;
}
return board;
}
// 2d array containing 9 arrays
let board = createBoard();
console.log("Board: ", board);
const modifiedSlicedBoard = board.slice()[0][2] = 9999999; // I'm modifying the contents of one of the sub-arrays. Because the arrays are copied shallowly, this will also modify the original board
console.log(board, modifiedSlicedBoard);
Related
If someone can test this and find the problem that would be very helpful. The algorithm solves the puzzles but has some zeros in the solution which is not valid sudoku. I think the problem is if the number is possible in that cell then it assumes its correct.
export const initiate = (board) => {
const updatedBoard = board.map((i) =>
i.map((j) => (j === 0 ? (j = null) : j))
);
const validInput = validBoard(updatedBoard);
if (!validInput) {
return false;
}
return solve(updatedBoard);
};
const solve = (board) => {
if (isSolved(board)) {
return board;
}
const possibilities = findPossibilities(board);
const validBoards = keepValid(possibilities);
return searchForSolution(validBoards);
};
const searchForSolution = (boards) => {
if (boards.length < 1) {
return false;
}
const first = boards.shift();
const tryPath = solve(first);
if (tryPath) {
return tryPath;
}
return searchForSolution(boards);
};
const isSolved = (board) => {
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
if (board[i][j] === null) {
return false;
}
}
}
return true;
};
const findPossibilities = (board) => {
let res = [];
const firstEmptySqr = findEmptySqr(board);
if (firstEmptySqr !== undefined) {
const y = firstEmptySqr[0];
const x = firstEmptySqr[1];
for (let i = 0; i <= 9; i++) {
const newBoard = [...board];
const row = [...newBoard[y]];
row[x] = i;
newBoard[y] = row;
res.push(newBoard);
}
}
return res;
};
const findEmptySqr = (board) => {
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
if (board[i][j] === null) {
return [i, j];
}
}
}
};
const keepValid = (boards) => {
let res = [];
for (let i = 0; i < boards.length; i++) {
if (validBoard(boards[i])) {
res.push(boards[i]);
}
}
return res;
};
const validBoard = (board) => {
return rowsValid(board) && columnsValid(board) && boxesValid(board);
};
function rowsValid(board) {
for (let i = 0; i < 9; i++) {
let cur = [];
for (let j = 0; j < 9; j++) {
if (cur.includes(board[i][j])) {
return false;
} else if (board[i][j] !== null) {
cur.push(board[i][j]);
}
}
}
return true;
}
function columnsValid(board) {
for (let i = 0; i < 9; i++) {
let cur = [];
for (let j = 0; j < 9; j++) {
if (cur.includes(board[j][i])) {
return false;
} else if (board[j][i] !== null) {
cur.push(board[j][i]);
}
}
}
return true;
}
function boxesValid(board) {
const boxCoordinates = [
[0, 0],
[0, 1],
[0, 2],
[1, 0],
[1, 1],
[1, 2],
[2, 0],
[2, 1],
[2, 2],
];
for (let y = 0; y < 9; y += 3) {
for (let x = 0; x < 9; x += 3) {
let cur = [];
for (let i = 0; i < 9; i++) {
let coordinates = [...boxCoordinates[i]];
coordinates[0] += y;
coordinates[1] += x;
if (cur.includes(board[coordinates[0]][coordinates[1]])) {
return false;
} else if (board[coordinates[0]][coordinates[1]] !== null) {
cur.push(board[coordinates[0]][coordinates[1]]);
}
}
}
}
return true;
}
I was expecting this to solve the sudoku puzzle without returning zeros because in sudoku there are no zeros
In findPossibilities you have a bug in the loop.
In the first iteration 0 is assigned to row[x], which is not what you want to happen. So change this line:
for (let i = 0; i <= 9; i++) {
to:
for (let i = 1; i <= 9; i++) {
I am iterating over the matrix object. Which I create inside the loop, then I modify its values through the increment. When the increment reaches its maximum (51), I have to go back to the outer loop and create a new object one more element. Example: {0: 0, 1: 0}. Now I iterate over the last element in the object, if it = maximum, I go to the next one, from "1" to "0", and increment "0", and "1" = 0. Then I have to iterate over "1".
But the values are not cleared when element = maximum a bug is displayed.
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
function check(maxLength) {
for (let matrixLength = 0; matrixLength < maxLength; matrixLength++) {
let a = matrix(matrixLength);
let Indx;
while (Indx!==null) {
Indx = getIndex(a,letters.length);
console.log(increment(a,Indx,Indx));
}
}
}
function matrix(length) {
let matrix = {};
for (let i = 0; i <= length; i++) {
matrix[i] = 0;
}
return matrix;
}
function getIndex(matrix, arrLength) {
for (let i = Object.values(matrix).length - 1; i >= 0; i--) {
if (matrix[i]!==arrLength - 1) {
return i;
}
}
console.log('null');
return null;
}
function increment(matrix, index, prevIndex = null) {
matrix[index]++;
if (prevIndex === null || index === prevIndex) {
return matrix;
}
for (let i = index + 1; i < Object.values(matrix).length; i++) {
matrix[i] = 0;
}
return matrix;
}
check(3);
Check whether Indx is null after calling getIndex, not before the next iteration.
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
function check(maxLength) {
for (let matrixLength = 0; matrixLength < maxLength; matrixLength++) {
let a = matrix(matrixLength);
while (true) {
let Indx = getIndex(a, letters.length);
if (Indx == null) {
break;
}
console.log(increment(a, Indx, Indx));
}
}
}
function matrix(length) {
let matrix = {};
for (let i = 0; i <= length; i++) {
matrix[i] = 0;
}
return matrix;
}
function getIndex(matrix, arrLength) {
for (let i = Object.values(matrix).length - 1; i >= 0; i--) {
if (matrix[i] !== arrLength - 1) {
return i;
}
}
console.log('null');
return null;
}
function increment(matrix, index, prevIndex = null) {
matrix[index]++;
if (prevIndex === null || index === prevIndex) {
return matrix;
}
for (let i = index + 1; i < Object.values(matrix).length; i++) {
matrix[i] = 0;
}
return matrix;
}
check(3);
I am currently doing a codewars problem, and I think I almost got it however, I ran across a problem when sorting index values with the same letter. link to problem is here. https://www.codewars.com/kata/5782dd86202c0e43410001f6
function doMath(s) {
let strSplit = s.split(' ');
let clonedArr = strSplit.slice();
for (let i = 0; i < strSplit.length; i++) {
for (let j = 0; j < strSplit[i].length; j++) {
let current = strSplit[i][j];
if (isNaN(current)) {
let letter = current;
strSplit[i] = strSplit[i].replace(letter, '');
strSplit[i] = letter + strSplit[i];
}
}
}
let sortedArr = strSplit.sort();
console.log(sortedArr);
// ["b900", "y369", "z123", "z246", "z89"]
let noLetterArr = sortedArr.map(x => {
return x.slice(1);
});
let numberArr = noLetterArr.map(y => {
return +y;
})
let firstEl = numberArr[0];
for (let i = 1; i < numberArr.length; i++) {
if (numberArr.indexOf(numberArr[i]) % 4 == 1) {
firstEl += numberArr[i];
}
if (numberArr.indexOf(numberArr[i]) % 4 == 2) {
firstEl -= numberArr[i];
}
if (numberArr.indexOf(numberArr[i]) % 4 == 3) {
firstEl *= numberArr[i];
}
}
return firstEl;
}
console.log(doMath('24z6 1z23 y369 89z 900b'));
I would like to sort the sortedArr the ones with the same letter by how they first appeared in string. So since "z246" appeared first in the original string. I would like to have that before "1z23". I had a hard time creating a function for that.
var al = [];
function doMath(s) {
var ar = s.split(" ");
for (let i = 0; i < ar.length; i++) {
for (let char of ar[i]) {
let temp = char.match(/[a-z]/i);
if (temp) {
al[i] = char;
ar[i] = ar[i].replace(char, '');
ar[i] = char + ar[i];
}
}
}
al = al.sort();
//New Sort Logic to pass above test case and others too
var n = [];
for (let i = 0; i < al.length; i++) {
for (let j = 0; j < ar.length; j++) {
if (ar[j].startsWith(al[i]) && !n.includes(ar[j])) {
n.push(ar[j]);
}
}
}
var result = parseInt(n[0].substr(1)),
count = 1;
for (let i = 1; i < n.length; i++) {
if (count == 1) {
result = result + parseInt(n[i].substr(1));
count++;
} else if (count == 2) {
result = result - parseInt(n[i].substr(1));
count++;
} else if (count == 3) {
result = result * parseInt(n[i].substr(1));
count++;
} else if (count == 4) {
result = result / parseInt(n[i].substr(1));
count = 1;
}
}
return Math.round(result);
}
Please i am trying to build a Sudoku solver in JavaScript but i face a problem when i get to the solve function, the recursive loop doesn't stop when the board is full. It executes till the end even after finding the solution. Please I'll be grateful if i can get some help. Here is what i tried doing:
class SudokuSolver {
// convert puzzle string to 2D array
boardParser(puzzleString) {
var board = [];
var i, j;
for (i = 0; i < 81; i += 9) {
var boardRow = [];
for (j = 0; j < 9; j++) {
boardRow.push(puzzleString.charAt(i + j));
}
board.push(boardRow)
}
// console.log(board);
return board;
}
// Look for empty space on board (empty space = ".")
// return [row, col] if empty space found
// return [-1,-1] if no empty space found (board is full)
getDot(board) {
var i, j;
for (i = 0; i < 9; i++) {
for (j = 0; j < 9; j++) {
if (board[i][j] == ".") {
return [i, j];
}
}
}
return [-1, -1];
}
checkRowPlacement(board, row, column, value) {
var i;
for (i = 0; i < 9; i++) {
if (board[row][i] == value) {
// console.log("row check false");
return {
valid: false
};
}
}
// console.log("row check true");
return {
valid: true
}
}
checkColPlacement(board, row, column, value) {
var i;
for (i = 0; i < 9; i++) {
if (board[i][column] == value) {
// console.log("col check false")
return {
valid: false
}
}
}
// console.log("col check true")
return {
valid: true
};
}
checkRegionPlacement(board, row, column, value) {
var i, j;
var regRow = Math.floor(row / 3) * 3;
var regCol = Math.floor(column / 3) * 3;
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
if (board[regRow + i][regCol + j] == value) {
// console.log("reg check false")
return {
valid: false
}
}
}
}
// console.log("reg check true");
return {
valid: true
}
}
checkvalue(board, row, column, value) {
var rowCheck = this.checkRowPlacement(board, row, column, value).valid
var colCheck = this.checkColPlacement(board, row, column, value).valid
var regCheck = this.checkRegionPlacement(board, row, column, value).valid
// console.log(rowCheck, colCheck, regCheck);
if (rowCheck && colCheck && regCheck) {
// console.log(true)
return true;
}
// console.log(false)
return false;
}
// convert 2D array back to string
stringifyBoard(board) {
var string = ""
var i, j;
for (i = 0; i < 9; i++) {
for (j = 0; j < 9; j++) {
string += board[i][j];
}
}
// console.log(string);
return string;
}
// check for any more empty spaces
solved(board) {
var i, j;
if (this.getDot(board)[0] == -1) {
return true
}
return false
}
solve(puzzleString) {
var board = this.boardParser(puzzleString)
var emptySpot = this.getDot(board);
var row = emptySpot[0];
var column = emptySpot[1];
// full board condition
if (this.solved(board)) {
return puzzleString;
}
for (var i = 1; i <= 9; i++) {
if (this.checkvalue(board, row, column, i)) {
board[row][column] = i;
var boardString = this.stringifyBoard(board);
this.solve(boardString);
}
}
// if board is unsolvable return false
return false;
}
}
const input = '5..91372.3...8.5.9.9.25..8.68.47.23...95..46.7.4.....5.2.......4..8916..85.72...3';
console.log(new SudokuSolver().solve(input));
it returns false, but the puzzle string is valid and also when i console log in the full board condition it shows the solved string but doesn't return it.
So once the board is solved and if this.solved(board) returns true, you are returning the solved board. But this is only at the last level of recursion chain. You need to check at every level of recursion if this particular move reaches a solved board state. Hence, validate the result of the next level and if it returns a solved board, return the solved board from the current level too. I have made appropriate changes in the solve function-
solve(puzzleString) {
var board = this.boardParser(puzzleString)
var emptySpot = this.getDot(board);
var row = emptySpot[0];
var column = emptySpot[1];
// full board condition
if (this.solved(board)) {
return puzzleString;
}
for (var i = 1; i <= 9; i++) {
if (this.checkvalue(board, row, column, i)) {
board[row][column] = i;
var boardString = this.stringifyBoard(board);
var result = this.solve(boardString);
if(result !== false){
return result;
}
}
}
// if board is unsolvable return false
return false;
}
for (var i = 1; i <= 9; i++) {
if (this.checkvalue(board, row, column, i)) {
board[row][column] = i;
var boardString = this.stringifyBoard(board);
if(this.solve(boardString)) return true; //I've a modification here
}
}
You didn't write any code to stop the function when the solution is found.
Here is my solution:
for (var i = 1; i <= 9; i++) {
if (this.checkvalue(board, row, column, i)) {
board[row][column] = i;
var boardString = this.stringifyBoard(board);
var sol = this.solve(boardString); // change
if (sol) return sol // change
}
}
I have done a similar one in python a while ago.
Check it here if you are interested: https://onlinegdb.com/SJt2PQrjP
My code currently has a bug where my 2-d array with the bool value false suddenly contains true values before it is assigned any. My current guesses is either console.log somehow is delayed and picks up the values after it is called, with the updated values or that there is some issue that I don't understand about how scope works in javascript.
As seen below console.log(visited[i][j]) results in false for all values but the
new visited line contains true values even before the following is called.
const field_size = 800;
const cells_in_row = 5;
const frames_per_second = 1;
const cell_size = field_size / cells_in_row;
class Cell {
constructor(x,y) {
this.value = 0;
this.x = x;
this.y = y;
this.coordinates = [x*cell_size,y*cell_size];
}
fill() {
this.value = 1;
}
clear() {
this.value = 0;
}
}
const get_new_grid = (random = 0) => {
const grid = new Array(cells_in_row);
for (let i = 0; i < grid.length; i++) {
grid[i] = new Array(cells_in_row);
for (let j = 0; j < grid.length; j++) {
grid[i][j] = new Cell(i,j);
v = 0;
if (random) {
v = Math.floor(Math.random() * 2);
}
grid[i][j].value = v;
}
}
return grid;
}
const get_islands = (grid) => {
// bool array to mark visited cells
let visited = new Array(cells_in_row);
for (let i = 0; i < grid.length; i++) {
visited[i] = new Array(cells_in_row);
for (let j = 0; j < grid[0].length; j++) {
visited[i][j] = false;
}
}
console.log("New Visited", visited);
let count = 0;
let islands = [];
let island_coords = [];
for (let i = 0; i < grid.length; i++) {
for (let j = 0; j < grid.length; j++) {
if (visited[i][j] == false && grid[i][j].value == 1) {
// visit all cells in this island and increment island count
// dfs will return array of coordinates of island
[visited, island_coords] = dfs(i, j, grid, visited, island_coords);
console.log(visited);
islands.push(island_coords);
count += 1;
}
}
}
return [count, islands];
}
const dfs = (i, j, grid, visited, island_coords) => {
let row_nbr = [-1, -1, -1, 0, 0, 1, 1, 1];
let col_nbr = [-1, 0, 1, -1, 1, -1, 0, 1];
visited[i][j] = true;
island_coords.push([i,j]);
for (let k = 0; k < 8; k++) {
if (is_safe(i + row_nbr[k], j + col_nbr[k], grid, visited)) {
console.log("DFSing " + i + "," + j);
[visited, island_coords] = dfs(i + row_nbr[k], j + col_nbr[k],
grid, visited, island_coords);
}
}
return [visited, island_coords];
}
const is_safe = (i, j, grid, visited) => {
return (i >= 0 && i < grid.length &&
j >= 0 && j < grid.length &&
!(visited[i][j]) && grid[i][j].value === 1);
}
(function () {
var old = console.log;
var logger = document.getElementById('log');
console.log = function () {
for (var i = 0; i < arguments.length; i++) {
if (typeof arguments[i] == 'object') {
logger.innerHTML += (JSON && JSON.stringify ? JSON.stringify(arguments[i], undefined, 2) : arguments[i]) + '<br />';
} else {
logger.innerHTML += arguments[i] + '<br />';
}
}
}
})();
window.onload = () => {
const canvas = document.getElementById('canvas');
const grid = get_new_grid(random = 0);
grid[0][0].value = true;
grid[0][1].value = true;
grid[1][0].value = true;
grid[1][1].value = true;
const islands = get_islands(grid);
console.log(grid);
console.log(islands);
}
<!DOCTYPE html>
<html>
<body>
<script src="gameoflife.js"></script>
<pre id="log"></pre>
</body>
</html>
EDIT:
So I updated the snippet but it looks like it works on this end, however it shows the behavior I mentioned before on my own browser even with the exact same javascript code and html in the snippet.
Mentioned in the comments by Niet, objects logged to the console are live.