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  return newSudokuBoard;
 80}
 81
 82function initDisplay() {
 83  let sudokuBoardElement = document.getElementById("sudoku-board");
 84  sudokuBoardElement.innerHTML = "";
 85  for (let boxX = 0; boxX < 9; boxX++) {
 86    let sudokuBoardRow = document.createElement("div");
 87    sudokuBoardRow.className = "sudoku-board-row";
 88    for (let boxY = 0; boxY < 9; boxY++) {
 89      let sudokuBoardBox = document.createElement("div");
 90      sudokuBoardBox.className = "sudoku-board-box";
 91      sudokuBoardBox.id = "sudoku-board-box-" + boxX + "-" + boxY;
 92      sudokuBoardBox.innerHTML = `<input type="text" class="sudoku-board-box-input" id="${
 93        "sudoku-board-box-input-" + boxX + "-" + boxY
 94      }">`;
 95      sudokuBoardRow.appendChild(sudokuBoardBox);
 96    }
 97    sudokuBoardElement.appendChild(sudokuBoardRow);
 98  }
 99}
100
101function updateDisplay(sudokuBoard) {
102  for (let boxX = 0; boxX < 9; boxX++) {
103    for (let boxY = 0; boxY < 9; boxY++) {
104      let currentSudokuBoardBox = document.getElementById(
105        "sudoku-board-box-" + boxX + "-" + boxY
106      );
107      let currentBoxData = sudokuBoard[boxX][boxY];
108      if (currentBoxData.length === 1) {
109        currentSudokuBoardBox.className =
110          "sudoku-board-box sudoku-board-box-solved";
111        currentSudokuBoardBox.innerHTML = currentBoxData[0];
112      } else if (currentBoxData.length === 0) {
113        currentSudokuBoardBox.className =
114          "sudoku-board-box sudoku-board-box-empty";
115      } else {
116        currentSudokuBoardBox.className =
117          "sudoku-board-box sudoku-board-box-unsolved";
118        currentSudokuBoardBox.innerHTML = currentBoxData[0];
119        for (let boxDataIndex in currentBoxData.slice(1)) {
120          currentSudokuBoardBox.innerHTML +=
121            ", " + currentBoxData.slice(1)[boxDataIndex];
122        }
123      }
124    }
125  }
126}
127
128function runIteration() {
129  if (shouldBeSolving) {
130    sudokuBoard = iterativelySolveSudokuBoard(sudokuBoard);
131    updateDisplay(sudokuBoard);
132    let solved = true;
133    for (let x in sudokuBoard) {
134      let sudokuBoardRow = sudokuBoard[x];
135      for (let y in sudokuBoard) {
136        let sudokuBox = sudokuBoardRow[y];
137        if (!(sudokuBox.length === 1)) {
138          solved = false;
139        }
140      }
141    }
142    if (!solved) {
143      setTimeout(runIteration, speedOfSolve);
144    } else {
145      shouldBeSolving = false;
146    }
147  }
148}
149
150function loadUserInput() {
151  for (let boxX = 0; boxX < 9; boxX++) {
152    for (let boxY = 0; boxY < 9; boxY++) {
153      let currentSudokuBoardBoxInput = document.getElementById(
154        "sudoku-board-box-input-" + boxX + "-" + boxY
155      );
156      if (currentSudokuBoardBoxInput.value == "") {
157        sudokuBoard[boxX][boxY] = [];
158      } else {
159        sudokuBoard[boxX][boxY] = [parseInt(currentSudokuBoardBoxInput.value)];
160      }
161    }
162  }
163}
164
165let shouldBeSolving = false;
166function runSolver() {
167  shouldBeSolving = true;
168  loadUserInput();
169  runIteration();
170}
171
172function haltSolverAndReset() {
173  shouldBeSolving = false;
174  sudokuBoard = generateEmptyBoard();
175  console.log(sudokuBoard);
176  updateDisplay(sudokuBoard);
177  setTimeout(initDisplay, 500);
178}
179
180initDisplay();