1var speedOfSolve = 500;
  2
  3var sudokuBoard = generateEmptyBoard();
  4
  5function generateEmptyBoard() {
  6  return [
  7    [[], [], [], [], [], [], [], [], []],
  8    [[], [], [], [], [], [], [], [], []],
  9    [[], [], [], [], [], [], [], [], []],
 10    [[], [], [], [], [], [], [], [], []],
 11    [[], [], [], [], [], [], [], [], []],
 12    [[], [], [], [], [], [], [], [], []],
 13    [[], [], [], [], [], [], [], [], []],
 14    [[], [], [], [], [], [], [], [], []],
 15    [[], [], [], [], [], [], [], [], []],
 16  ];
 17}
 18
 19function getCoordsOfBoxesInSameSubBox(x, y) {
 20  let subBoxX = Math.floor(x / 3);
 21  let subBoxY = Math.floor(y / 3);
 22  let coords = [];
 23  for (let subBoxBoxX = 0; subBoxBoxX < 3; subBoxBoxX++) {
 24    for (let subBoxBoxY = 0; subBoxBoxY < 3; subBoxBoxY++) {
 25      let globalX = subBoxBoxX + subBoxX * 3;
 26      let globalY = subBoxBoxY + subBoxY * 3;
 27      if (!(globalX === x && globalY === y)) {
 28        coords.push({ x: globalX, y: globalY });
 29      }
 30    }
 31  }
 32  return coords;
 33}
 34
 35function getCoordsOfBoxesInSameXandY(x, y) {
 36  let coords = [];
 37  for (let boxX = 0; boxX < 9; boxX++) {
 38    if (!(boxX === x)) {
 39      coords.push({ x: boxX, y: y });
 40    }
 41  }
 42  for (let boxY = 0; boxY < 9; boxY++) {
 43    if (!(boxY === y)) {
 44      coords.push({ x: x, y: boxY });
 45    }
 46  }
 47  return coords;
 48}
 49
 50function getPossibleNumbers(x, y, sudokuBoard) {
 51  let possibleNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
 52  let coordsToCheck = getCoordsOfBoxesInSameSubBox(x, y).concat(
 53    getCoordsOfBoxesInSameXandY(x, y)
 54  );
 55  for (let coordIndex in coordsToCheck) {
 56    let coord = coordsToCheck[coordIndex];
 57    let boxData = sudokuBoard[coord.x][coord.y];
 58    if (boxData.length === 1) {
 59      var index = possibleNumbers.indexOf(boxData[0]);
 60      if (index !== -1) {
 61        possibleNumbers.splice(index, 1);
 62      }
 63    }
 64  }
 65  return possibleNumbers;
 66}
 67
 68function iterativelySolveSudokuBoard(sudokuBoard) {
 69  let newSudokuBoard = sudokuBoard;
 70  for (let x in sudokuBoard) {
 71    let sudokuBoardRow = sudokuBoard[x];
 72    for (let y in sudokuBoard) {
 73      let sudokuBox = sudokuBoardRow[y];
 74      if (!(sudokuBox.length === 1)) {
 75        newSudokuBoard[x][y] = getPossibleNumbers(x, y, sudokuBoard);
 76      }
 77    }
 78  }
 79  for (let x in sudokuBoard) {
 80    let sudokuBoardRow = newSudokuBoard[x];
 81    for (let y in sudokuBoard) {
 82      let sudokuBox = sudokuBoardRow[y];
 83      if (!(sudokuBox.length === 1)) {
 84        let coordsToCheck = getCoordsOfBoxesInSameSubBox(x, y);
 85        let commonNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
 86        for (let coordIndex in coordsToCheck) {
 87          let coord = coordsToCheck[coordIndex];
 88          let boxData = newSudokuBoard[coord.x][coord.y];
 89          if (boxData.length !== 1) {
 90            let numbersToRemove = [];
 91            for (let numberIndex in commonNumbers) {
 92              if (!boxData.includes(commonNumbers[numberIndex])) {
 93                numbersToRemove.push(commonNumbers[numberIndex]);
 94              }
 95            }
 96            for (let numberIndex in numbersToRemove) {
 97              const index = commonNumbers.indexOf(numbersToRemove[numberIndex]);
 98              if (index > -1) {
 99                commonNumbers.splice(index, 1);
100              }
101            }
102          }
103        }
104        if (x == 1 && y == 6) {
105          console.log(commonNumbers);
106        }
107
108        let potentialSolutionA = newSudokuBoard[x][y];
109        for (let numberIndex in commonNumbers) {
110          const index = potentialSolutionA.indexOf(commonNumbers[numberIndex]);
111          if (index > -1) {
112            potentialSolutionA.splice(index, 1);
113          }
114        }
115        if (potentialSolutionA.length == 1) {
116          let noOtherBoxHasIt = true;
117          for (let coordIndex in coordsToCheck) {
118            let coord = coordsToCheck[coordIndex];
119            let boxData = newSudokuBoard[coord.x][coord.y];
120            let potentialSolution = boxData;
121            for (let numberIndex in commonNumbers) {
122              const index = potentialSolution.indexOf(
123                commonNumbers[numberIndex]
124              );
125              if (index > -1) {
126                potentialSolution.splice(index, 1);
127              }
128            }
129            if (potentialSolution.length == 1) {
130              if (potentialSolutionA[0] === potentialSolution[0]) {
131                noOtherBoxHasIt = false;
132              }
133            }
134          }
135          if (noOtherBoxHasIt) {
136            newSudokuBoard[x][y] = potentialSolutionA;
137          }
138        }
139      }
140    }
141  }
142  return newSudokuBoard;
143}
144
145function initDisplay() {
146  let sudokuBoardElement = document.getElementById("sudoku-board");
147  sudokuBoardElement.innerHTML = "";
148  for (let boxX = 0; boxX < 9; boxX++) {
149    let sudokuBoardRow = document.createElement("div");
150    sudokuBoardRow.className = "sudoku-board-row";
151    for (let boxY = 0; boxY < 9; boxY++) {
152      let sudokuBoardBox = document.createElement("div");
153      sudokuBoardBox.className = "sudoku-board-box";
154      sudokuBoardBox.id = "sudoku-board-box-" + boxX + "-" + boxY;
155      sudokuBoardBox.innerHTML = `<input type="text" class="sudoku-board-box-input" id="${
156        "sudoku-board-box-input-" + boxX + "-" + boxY
157      }">`;
158      sudokuBoardRow.appendChild(sudokuBoardBox);
159    }
160    sudokuBoardElement.appendChild(sudokuBoardRow);
161  }
162}
163
164function updateDisplay(sudokuBoard) {
165  for (let boxX = 0; boxX < 9; boxX++) {
166    for (let boxY = 0; boxY < 9; boxY++) {
167      let currentSudokuBoardBox = document.getElementById(
168        "sudoku-board-box-" + boxX + "-" + boxY
169      );
170      let currentBoxData = sudokuBoard[boxX][boxY];
171      if (currentBoxData.length === 1) {
172        currentSudokuBoardBox.className =
173          "sudoku-board-box sudoku-board-box-solved";
174        currentSudokuBoardBox.innerHTML = currentBoxData[0];
175      } else if (currentBoxData.length === 0) {
176        currentSudokuBoardBox.className =
177          "sudoku-board-box sudoku-board-box-empty";
178      } else {
179        currentSudokuBoardBox.className =
180          "sudoku-board-box sudoku-board-box-unsolved";
181        currentSudokuBoardBox.innerHTML = currentBoxData[0];
182        for (let boxDataIndex in currentBoxData.slice(1)) {
183          currentSudokuBoardBox.innerHTML +=
184            ", " + currentBoxData.slice(1)[boxDataIndex];
185        }
186      }
187    }
188  }
189}
190
191function runIteration() {
192  if (shouldBeSolving) {
193    sudokuBoard = iterativelySolveSudokuBoard(sudokuBoard);
194    updateDisplay(sudokuBoard);
195    let solved = true;
196    for (let x in sudokuBoard) {
197      let sudokuBoardRow = sudokuBoard[x];
198      for (let y in sudokuBoard) {
199        let sudokuBox = sudokuBoardRow[y];
200        if (!(sudokuBox.length === 1)) {
201          solved = false;
202        }
203      }
204    }
205    if (!solved) {
206      setTimeout(runIteration, speedOfSolve);
207    } else {
208      shouldBeSolving = false;
209    }
210  }
211}
212
213function loadUserInput() {
214  for (let boxX = 0; boxX < 9; boxX++) {
215    for (let boxY = 0; boxY < 9; boxY++) {
216      let currentSudokuBoardBoxInput = document.getElementById(
217        "sudoku-board-box-input-" + boxX + "-" + boxY
218      );
219      if (currentSudokuBoardBoxInput.value == "") {
220        sudokuBoard[boxX][boxY] = [];
221      } else {
222        sudokuBoard[boxX][boxY] = [parseInt(currentSudokuBoardBoxInput.value)];
223      }
224    }
225  }
226}
227
228let shouldBeSolving = false;
229function runSolver() {
230  shouldBeSolving = true;
231  loadUserInput();
232  sudokuBoard = [
233    [[7], [9], [], [2], [], [], [1], [3], [5]],
234    [[], [], [3], [9], [], [], [], [], []],
235    [[5], [6], [], [], [3], [], [4], [2], [9]],
236    [[], [], [], [], [2], [], [], [], []],
237    [[], [4], [5], [], [1], [], [8], [], [2]],
238    [[], [7], [], [3], [], [], [], [], [1]],
239    [[4], [], [], [], [9], [], [2], [], [6]],
240    [[], [], [], [4], [], [6], [3], [1], []],
241    [[1], [3], [], [8], [7], [], [9], [5], []],
242  ];
243  runIteration();
244}
245
246function haltSolverAndReset() {
247  shouldBeSolving = false;
248  sudokuBoard = generateEmptyBoard();
249  console.log(sudokuBoard);
250  updateDisplay(sudokuBoard);
251  setTimeout(initDisplay, 500);
252}
253
254initDisplay();