2022-05-20 16:21:08 -05:00
|
|
|
class SudokuSolver {
|
|
|
|
|
|
|
|
|
|
validate(puzzleString) {
|
2022-05-20 21:25:24 -05:00
|
|
|
|
|
|
|
|
return puzzleString.length == 81 && !puzzleString.match(/[^\.\d]/g);
|
|
|
|
|
|
2022-05-20 16:21:08 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
checkRowPlacement(puzzleString, row, column, value) {
|
|
|
|
|
|
2022-05-20 22:23:54 -05:00
|
|
|
// rows
|
|
|
|
|
const rows = {
|
|
|
|
|
A: [],
|
|
|
|
|
B: [],
|
|
|
|
|
C: [],
|
|
|
|
|
D: [],
|
|
|
|
|
E: [],
|
|
|
|
|
F: [],
|
|
|
|
|
G: [],
|
|
|
|
|
H: [],
|
|
|
|
|
I: [],
|
|
|
|
|
};
|
2022-05-20 21:25:24 -05:00
|
|
|
|
2022-05-20 22:23:54 -05:00
|
|
|
// rowKeys
|
|
|
|
|
const rowKeys = Object.keys(rows);
|
|
|
|
|
|
|
|
|
|
// split puzzle strings and assign row values for each row
|
|
|
|
|
rowKeys.forEach((key, index) => {
|
|
|
|
|
|
|
|
|
|
const start = index * 9;
|
|
|
|
|
const end = start + 9;
|
|
|
|
|
|
|
|
|
|
const rowValues = puzzleString.slice(start, end).split('');
|
|
|
|
|
|
|
|
|
|
rows[key] = rowValues
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// get row values for placement
|
|
|
|
|
const rowValues = rows[row];
|
|
|
|
|
|
|
|
|
|
// get existing value placed
|
|
|
|
|
const existingValue = rowValues[column - 1];
|
|
|
|
|
|
2022-05-20 23:23:35 -05:00
|
|
|
// make sure placement location doesn't contain a value or contains same value
|
2022-05-20 22:23:54 -05:00
|
|
|
// make sure placements row does not already have value.
|
|
|
|
|
return (existingValue == '.' || existingValue == value) && !rowValues.includes(value);
|
2022-05-20 21:25:24 -05:00
|
|
|
|
2022-05-20 16:21:08 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
checkColPlacement(puzzleString, row, column, value) {
|
|
|
|
|
|
2022-05-20 22:23:54 -05:00
|
|
|
const columns = {
|
|
|
|
|
1: [],
|
|
|
|
|
2: [],
|
|
|
|
|
3: [],
|
|
|
|
|
4: [],
|
|
|
|
|
5: [],
|
|
|
|
|
6: [],
|
|
|
|
|
7: [],
|
|
|
|
|
8: [],
|
|
|
|
|
9: [],
|
|
|
|
|
};
|
2022-05-20 21:25:24 -05:00
|
|
|
|
2022-05-20 22:23:54 -05:00
|
|
|
const columnKeys = Object.keys(columns);
|
|
|
|
|
|
2022-05-22 23:55:59 -05:00
|
|
|
columnKeys.forEach((n, i) => {
|
2022-05-20 22:23:54 -05:00
|
|
|
|
2022-05-22 23:55:59 -05:00
|
|
|
columnKeys.forEach((key, j) => {
|
2022-05-20 22:23:54 -05:00
|
|
|
|
2022-05-22 23:55:59 -05:00
|
|
|
const start = j + (9 * i)
|
2022-05-20 23:23:35 -05:00
|
|
|
const end = start + 1;
|
|
|
|
|
|
|
|
|
|
const columnValue = puzzleString.slice(start, end);
|
|
|
|
|
|
|
|
|
|
columns[key].push(columnValue);
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
2022-05-20 22:23:54 -05:00
|
|
|
});
|
2022-05-20 21:25:24 -05:00
|
|
|
|
2022-05-20 23:23:35 -05:00
|
|
|
// get column values for placement
|
|
|
|
|
const columnValues = columns[column];
|
|
|
|
|
|
|
|
|
|
// get existing value placed
|
|
|
|
|
let existingValue;
|
|
|
|
|
|
|
|
|
|
switch (row) {
|
|
|
|
|
case 'A':
|
|
|
|
|
existingValue = columnValues[0];
|
|
|
|
|
break;
|
|
|
|
|
case 'B':
|
|
|
|
|
existingValue = columnValues[1];
|
|
|
|
|
break;
|
|
|
|
|
case 'C':
|
|
|
|
|
existingValue = columnValues[2];
|
|
|
|
|
break;
|
|
|
|
|
case 'D':
|
|
|
|
|
existingValue = columnValues[3];
|
|
|
|
|
break;
|
|
|
|
|
case 'E':
|
|
|
|
|
existingValue = columnValues[4];
|
|
|
|
|
break;
|
|
|
|
|
case 'F':
|
|
|
|
|
existingValue = columnValues[5];
|
|
|
|
|
break;
|
|
|
|
|
case 'G':
|
|
|
|
|
existingValue = columnValues[6];
|
|
|
|
|
break;
|
|
|
|
|
case 'H':
|
|
|
|
|
existingValue = columnValues[7];
|
|
|
|
|
break;
|
|
|
|
|
case 'I':
|
|
|
|
|
existingValue = columnValues[8];
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// make sure placement location doesn't contain a value or contains same value
|
|
|
|
|
// make sure placements row does not already have value.
|
|
|
|
|
return (existingValue == '.' || existingValue == value) && !columnValues.includes(value);
|
|
|
|
|
|
2022-05-20 16:21:08 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
checkRegionPlacement(puzzleString, row, column, value) {
|
|
|
|
|
|
2022-05-22 23:55:59 -05:00
|
|
|
const location = row + column;
|
2022-05-20 22:23:54 -05:00
|
|
|
|
2022-05-22 23:55:59 -05:00
|
|
|
let grids = {
|
|
|
|
|
gridOne: {
|
|
|
|
|
locations: ['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3'],
|
|
|
|
|
values: []
|
|
|
|
|
},
|
|
|
|
|
gridTwo: {
|
|
|
|
|
locations: ['A4', 'A5', 'A6', 'B4', 'B5', 'B6', 'C4', 'C5', 'C6'],
|
|
|
|
|
values: []
|
|
|
|
|
},
|
|
|
|
|
gridThree: {
|
|
|
|
|
locations: ['A7', 'A8', 'A9', 'B7', 'B8', 'B9', 'C7', 'C8', 'C9'],
|
|
|
|
|
values: []
|
|
|
|
|
},
|
|
|
|
|
gridFour: {
|
|
|
|
|
locations: ['D1', 'D2', 'D3', 'E1', 'E2', 'E3', 'F1', 'F2', 'F3'],
|
|
|
|
|
values: []
|
|
|
|
|
},
|
|
|
|
|
gridFive: {
|
|
|
|
|
locations: ['D4', 'D5', 'D6', 'E4', 'E5', 'E6', 'F4', 'F5', 'F6'],
|
|
|
|
|
values: []
|
|
|
|
|
},
|
|
|
|
|
gridSix: {
|
|
|
|
|
locations: ['D7', 'D8', 'D9', 'E7', 'E8', 'E9', 'F7', 'F8', 'F9'],
|
|
|
|
|
values: []
|
|
|
|
|
},
|
|
|
|
|
gridSeven: {
|
|
|
|
|
locations: ['G1', 'G2', 'G3', 'H1', 'H2', 'H3', 'I1', 'I2', 'I3'],
|
|
|
|
|
values: []
|
|
|
|
|
},
|
|
|
|
|
gridEight: {
|
|
|
|
|
locations: ['G4', 'G5', 'G6', 'H4', 'H5', 'H6', 'I4', 'I5', 'I6'],
|
|
|
|
|
values: []
|
|
|
|
|
},
|
|
|
|
|
gridNine: {
|
|
|
|
|
locations: ['G7', 'G8', 'G9', 'H7', 'H8', 'H9', 'I7', 'I8', 'I9'],
|
|
|
|
|
values: []
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const gridKeys = Object.keys(grids);
|
|
|
|
|
|
|
|
|
|
gridKeys.forEach((key, i) => {
|
|
|
|
|
|
|
|
|
|
let offset = 0;
|
|
|
|
|
|
|
|
|
|
if (i >= 3 && i <= 5) {
|
|
|
|
|
|
|
|
|
|
offset += 2;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i >= 6 && i <= 8) {
|
|
|
|
|
|
|
|
|
|
offset += 4;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (let j = 0; j < 3; j++) {
|
|
|
|
|
|
|
|
|
|
const start = (i * 3) + (9 * (j + offset));
|
|
|
|
|
const end = start + 3;
|
|
|
|
|
const gridValues = puzzleString.slice(start, end).split('');
|
|
|
|
|
|
|
|
|
|
grids[key].values = grids[key].values.concat(gridValues);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let gridValues = [];
|
|
|
|
|
|
|
|
|
|
gridKeys.forEach(key => {
|
|
|
|
|
|
|
|
|
|
const grid = grids[key]
|
|
|
|
|
|
|
|
|
|
if (grid.locations.includes(location)) {
|
|
|
|
|
gridValues = gridValues.concat(grid.values);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let existingValue;
|
|
|
|
|
|
|
|
|
|
switch (row) {
|
|
|
|
|
case 'A':
|
|
|
|
|
case 'D':
|
|
|
|
|
case 'H':
|
|
|
|
|
existingValue = gridValues[(parseInt(column) - 1)];
|
|
|
|
|
break;
|
|
|
|
|
case 'B':
|
|
|
|
|
case 'E':
|
|
|
|
|
case 'H':
|
|
|
|
|
existingValue = gridValues[3 + (parseInt(column) - 1)];
|
|
|
|
|
break;
|
|
|
|
|
case 'C':
|
|
|
|
|
case 'F':
|
|
|
|
|
case 'I':
|
|
|
|
|
existingValue = gridValues[6 + (parseInt(column) - 1)];
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (existingValue == '.' || existingValue == value) && !gridValues.includes(value);
|
2022-05-20 21:25:24 -05:00
|
|
|
|
2022-05-20 16:21:08 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
solve(puzzleString) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.exports = SudokuSolver;
|