//Global Variables
enteredSudokuVals = []; //Array to track User Entered Data
blockGroupElements = [];
for (gBlkGrpCnt = 1; gBlkGrpCnt <= 9; gBlkGrpCnt++) {
blockGroupElements[gBlkGrpCnt - 1] = [];
}
possibleValues = [];
for (r = 1; r <= 9; r++) {
possibleValues[r - 1] = [];
for (c = 1; c <= 9; c++) {
possibleValues[r - 1][c - 1] = [];
}
}
openFields = 0;
function dispMsg(msgTxt, msgType) {
if (msgType == "ERROR") {
document.getElementById("msgDtls").className = "msgDtlsErr";
} else {
document.getElementById("msgDtls").className = "";
}
document.getElementById("msgDtls").innerHTML=msgTxt;
}
function buildSudoku() {
sudoku = "";
sudoku = sudoku + "<table id=\"sudoku\">";
for (r=1; r<=9; r++) {
sudoku = sudoku + "<tr>";
for (c=1; c<=9; c++) {
sudokuCellClass = "sudokuCell";
if(c % 3 == 0 && c != 9) {
sudokuCellClass = sudokuCellClass + " sudokuCellRight";
}
if(r % 3 == 0 && r != 9) {
sudokuCellClass = sudokuCellClass + " sudokuCellBottom";
}
sudoku = sudoku + "<td class=\"" + sudokuCellClass + "\"><input id=\"R" + r + "C" + c + "\" class=\"sudokuInput\" type=\"text\"></td>";
}
sudoku = sudoku + "</tr>";
}
sudoku = sudoku + "</table>";
document.getElementById("puzzle").innerHTML = sudoku;
}
function assignBlockGroups() {
const blockGroupings = [1.1, 1.2, 1.3, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3];
for (r = 1; r <= 9; r++) {
rwBlockVal = Number(Math.trunc((r - 1) / 3) + 1);
for (c = 1; c <= 9; c++) {
colBlockVal = Number((Math.trunc((c - 1) / 3) + 1) / 10);
grpCalcVal = Number(rwBlockVal + colBlockVal);
blockGrp = blockGroupings.indexOf(grpCalcVal) + 1;
blockGroupElements[blockGrp - 1].push(document.getElementById("R" + r + "C" + c));
}
}
}
function identifyBlockGroups(r, c) {
const blockGroupings = [1.1, 1.2, 1.3, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3];
rwBlockVal = Number(Math.trunc((r - 1) / 3) + 1);
colBlockVal = Number((Math.trunc((c - 1) / 3) + 1) / 10);
grpCalcVal = Number(rwBlockVal + colBlockVal);
blockGrp = blockGroupings.indexOf(grpCalcVal) + 1;
return blockGrp;
}
function initSudoku() {
enteredSudokuVals = [];
//Init blank sudoku 2x array
for (i = 1; i <= 9; i++) {
let blankRow = new Array(9);
enteredSudokuVals.push(blankRow);
}
dispMsg("Enter initial puzzle values to solve", "NORMAL");
}
function clearSudoku() {
//Clear Sudoku 2x array variable
initSudoku();
//Clear Sudoku Puzzle Display
for (r = 1; r <= 9; r++) {
for (c = 1; c <= 9; c++) {
document.getElementById("R" + r + "C" + c).value="";
document.getElementById("R" + r + "C" + c).className = "sudokuInput";
}
}
}
function countOpenFields() {
let r = 0;
let c = 0;
openFields = 0;
for (r = 1; r <= 9; r++) {
for (c = 1; c <= 9; c++) {
if (document.getElementById("R" + r + "C" + c).value == "") {
openFields++;
}
}
}
}
function solveSudoku() {
if (validateSudoku("INIT") == 0) {
countOpenFields();
calculateSudoku();
}
}
function validateSudoku(valType) {
let minEnteredCnt = 18;
let enteredValCnt = 0;
let r = 0;
let c = 0;
if (valType == "INIT") {
//Track Initial User Entered Values
for (r = 1; r <= 9; r++) {
for (c = 1; c <= 9; c++) {
objID = "R" + r + "C" + c;
enteredSudokuVals[r - 1][c - 1] = document.getElementById(objID).value;
document.getElementById(objID).className = "sudokuInput";
//Count Number of Entered Data Points
if(enteredSudokuVals[r - 1][c - 1] != "") {
enteredValCnt ++;
}
}
}
//Validate Enough Values Entered
if (enteredValCnt < minEnteredCnt) {
dispMsg("Please enter at least " + minEnteredCnt + " values.", "ERROR");
return -1;
}
}
//Validate Duplicates w/i Row
for (r = 1; r <= 9; r++) {
if (!checkDupes(enteredSudokuVals[r-1])) {
dispMsg("Duplicate values within Row " + r + ".", "ERROR");
for(errStyleCnt=1; errStyleCnt<=9; errStyleCnt++){
document.getElementById("R" + r + "C" + errStyleCnt).className = "sudokuInputErr";
}
return -1;
}
}
//Validate Duplicates w/i Column
for (c = 1; c <= 9; c++) {
colValues = [];
for (r = 1; r <= 9; r++) {
colValues.push(enteredSudokuVals[r-1][c-1]);
}
if (!checkDupes(colValues)) {
dispMsg("Duplicate values within Column " + c + ".", "ERROR");
for(errStyleCnt=1; errStyleCnt<=9; errStyleCnt++){
document.getElementById("R" + errStyleCnt + "C" + c).className = "sudokuInputErr";
}
return -1;
}
}
//Validate Duplicates w/i Blocks
for (blckGrpCnt = 1; blckGrpCnt <= blockGroupElements.length; blckGrpCnt++) {
aryBlock = [];
for (blkGrpElmntCnt = 1; blkGrpElmntCnt <= blockGroupElements[blckGrpCnt - 1].length; blkGrpElmntCnt++) {
aryBlock.push(blockGroupElements[blckGrpCnt - 1][blkGrpElmntCnt - 1].value);
}
if (!checkDupes(aryBlock)) {
dispMsg("Duplicate values within Block " + blckGrpCnt + ".", "ERROR");
for(errStyleCnt = 1; errStyleCnt <= blockGroupElements[blckGrpCnt - 1].length; errStyleCnt++) {
blockGroupElements[blckGrpCnt - 1][errStyleCnt - 1].className = "sudokuInputErr";
}
return -1;
}
}
return 0;
}
function checkDupes(inputArray) {
nonEmptyVals = [];
nonEmptyVals = inputArray.filter((str) => str !== '');
if (nonEmptyVals.length > 1) {
nonEmptyVals.sort();
for (dupeCnt = 1; dupeCnt <= nonEmptyVals.length - 1; dupeCnt++) {
if (nonEmptyVals[dupeCnt - 1] == nonEmptyVals[dupeCnt]) {
return false;
}
}
}
return true;
}
function calculateSudoku() {
//Simple Fill Missing Numbers
blnSudokuUpdated = true;
blnSudokuSolved = false;
while (!blnSudokuSolved) {
// alert("Remaining Fields 1: " + openFields);
while (blnSudokuUpdated) {
blnSudokuUpdated = calcSimpleMissing();
// alert("Remaining Fields 2: " + openFields);
}
if (!buildPossibleValues()) {
blnSudokuSolved = true;
} else {
blnSudokuUpdated = true;
}
// alert("Remaining Fields 3: " + openFields);
}
if (validateSudoku("CHECK") == 0) {
alert("SOLVED");
}
}
function calcSimpleMissing() {
let blnSudokuUpdated = false;
let n = 0;
let b = 0;
let f = 0;
let r = 0;
let c = 0;
for (n = 1; n <= 9; n++) { //Number Loop
for (b = 1; b <= blockGroupElements.length; b++) { //Block Loop
let blnFldMatchVal = false;
let possibleFields = [];
//Validate Block Open for Number
blnFldMatchVal = inBlk(0, 0, n, b);
//Loop Through Each Block Field - Validate Column & Row
if(!blnFldMatchVal) {
for (f = 1; f <= blockGroupElements[b-1].length; f++) { //Field Loop
let blnRowMatchVal = false;
let blnColMatchVal = false;
//Field Not Populated?
if (blockGroupElements[b-1][f-1].value == "") {
//Validate Row Values Match
r = String(blockGroupElements[b-1][f-1].id).substring(1,2);
blnRowMatchVal = inRow(r, n);
//Validate Column Values Match
if (!blnRowMatchVal) {
c = String(blockGroupElements[b-1][f-1].id).substring(3,4);
blnColMatchVal = inCol(c, n);
}
if (!blnFldMatchVal && !blnRowMatchVal && !blnColMatchVal) {
possibleFields.push(f);
}
}
}
if (possibleFields.length == 1) {
openFields--;
blockGroupElements[b-1][possibleFields[0]-1].value = n;
blockGroupElements[b-1][possibleFields[0]-1].className = "sudokuInputSolved";
blnSudokuUpdated = true;
}
}
}
}
return blnSudokuUpdated;
}
function buildPossibleValues() {
let blnSudokuUpdated = false;
let r = 0;
let c = 0;
let n = 0;
for (r = 1; r <= 9; r++) {
for (c = 1; c <= 9; c++) {
possibleValues[r-1][c-1] = [];
if (document.getElementById("R" + r + "C" + c).value == "") {
for (n = 1; n <= 9; n++) {
let blnRowMatch = false;
let blnColMatch = false;
let blnBlkMatch = false;
//In Row?
blnRowMatch = inRow(r, n);
if (!blnRowMatch) {
//In Col?
blnColMatch = inCol(c, n);
if (!blnColMatch) {
//In Block?
blnBlkMatch = inBlk(r, c, n);
}
}
if (!blnRowMatch && !blnColMatch && !blnBlkMatch) {
possibleValues[r-1][c-1].push(n);
}
}
if (possibleValues[r-1][c-1].length == 1) {
openFields--;
document.getElementById("R" + r + "C" + c).value = possibleValues[r-1][c-1][0];
document.getElementById("R" + r + "C" + c).className = "sudokuInputSolved";
blnSudokuUpdated = true;
}
}
}
}
return blnSudokuUpdated;
}
function inRow(r, n) {
blnMatch = false;
let c = 0;
for (c = 1; c <= 9; c++) {
if (document.getElementById("R" + r + "C" + c).value == n) {
return true;
}
}
return blnMatch;
}
function inCol(c, n) {
blnMatch = false;
let r = 0;
for (r = 1; r <= 9; r++) {
if (document.getElementById("R" + r + "C" + c).value == n) {
return true;
}
}
return blnMatch;
}
function inBlk(r, c, n, bg = 0) {
blnMatch = false;
let b = 0;
if (bg == 0){
blockGrp = identifyBlockGroups(r, c);
} else {
blockGrp = bg;
}
for (b = 1; b <= 9; b++) {
if (blockGroupElements[blockGrp-1][b-1].value == n) {
return true;
}
}
return blnMatch;
}
function hoverPossibleValues() {
// alert(this.id);
let r = String(this.id).substring(1,2);
let c = String(this.id).substring(3,4);
// alert("R:" + r + " C:" + c);
dispMsg(possibleValues[r-1][c-1], "INFO");
}
function testSudoku() {
let r = 0;
let c = 0;
// let testValues = [,2,,8,,,,,1,9,,,2,,3,6,,,6,,,,9,1,,,5,7,6,,,,,,,,5,,8,4,,7,1,,,,,,5,8,,,,7,,,,,,9,,5,,3,,,,,,,,2,,5,,,,,8,1,9];
// let testValues = [7,,,,4,6,,,,6,,,1,,8,,,5,,,2,5,,3,,9,,,,,,1,,,2,9,,,4,,,,3,,,8,,,7,3,4,,,,1,,,4,,,,,,,,6,3,,,,8,,,,5,2,,,,1,3];
// let testValues = [,6,,,,,8,,9,,,4,,,,,6,,8,,,,,,,5,,4,,2,,6,8,,,,9,5,,,,2,,,8,6,,,1,9,7,,,,,,3,,,5,,,7,,8,,7,3,,,,,,4,,2,1,,,,,];
let testValues = [,,1,,2,,,8,6,,,,,3,6,,1,,,,,1,,,,7,2,9,,,,,2,,,,,8,,,5,,,3,,,,,6,,,,,7,4,3,,,,1,,,,,7,,9,6,,,,,6,1,,,7,,5,,,];
testValues.reverse();
for (r = 1; r <= 9; r++) {
for (c = 1; c <= 9; c++) {
val = testValues.pop();
if (typeof val != "undefined") {
document.getElementById("R" + r + "C" + c).value = val;
}
}
}
}