Sudoku Solver - 2018 Number Checking Class Example

In this example below you will see how to do a Sudoku Solver - 2018 Number Checking Class Example with some HTML / CSS and Javascript

Thumbnail
This awesome code was written by thomaskhugo, you can see more from this user in the personal repository.
You can find the original code on Codepen.io
Copyright thomaskhugo ©
  • HTML
  • CSS
  • JavaScript
    <div class="full-page">
  <h1>Sudoku Solver</h1>
  <div class="squareContainer">
    <div id="boardContainer">
    </div>
  </div>
<!--   <div id="edit-lock-toggle">
    <div id="toggle-background"></div>
    <div id="edit" class="toggle">edit</div>
    <div id="lock" class="toggle">lock</div>
  </div>
  <div id="solve_button" class="button">solve</div>
  <div id="clear_button" class="button">clear</div> -->
</div>

/*Downloaded from https://www.codeseek.co/thomaskhugo/sudoku-solver-2018-number-checking-class-example-dmwXGE */
    * {
  box-sizing: border-box;
}

body {
  background-color: #ddd;
}

h1 {
  text-align: center;
}

.full-page {
  height: 100vh;
  width: 100%;
  padding: 25px;
}

.grid_3x3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  grid-gap: 4px;
  overflow: hidden;
}

.section {
  background-color: #999;
  grid-gap: 1px;
}

.space {
  background-color: #ccc;
  display: flex;
  justify-content: center;
  align-items: center;
  overflow: hidden;
  max-width: 100%;
}

.space:focus {
  outline: none;
  background-color: #bbb;
}

.space:hover {
  background-color: #bbb;
}

.squareContainer {
  margin: auto;
  max-width: 500px;
  background-color: #eee;
}

#boardContainer {
  width: 100%;
  padding-top: 100%;
  background-color: #eee;
  position: relative;
}

#board {
  margin: auto;
  padding: 4px;
  background-color: #999;
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
}

#solve_button {
  background-color: #ccc;
  color: #333;
}

#solve_button:hover {
  background-color: #bbb;
}

#clear_button {
  background-color: #c55;
  color: #eee;
}

#clear_button:hover {
  background-color: #d33;
}

.button {
  max-width: 500px;
  margin: 10px auto;
  text-align: center;
  padding: 10px;
  font-size: 1.5em;
  border: 4px solid #aaa;
}

#edit-lock-toggle {
  cursor: pointer;
  max-width: 500px;
  display: flex;
  margin: 10px auto;
  color: #333;
  font-size: 1.5em;
  border: 4px solid #aaa;
  overflow: hidden;
  position: relative;
}

#toggle-background {
  position: absolute;
  left: 50%;
  top: 0;
  background-color: #383;
  width: 50%;
  height: 100%;
  z-index: -1;
  transition: 0.3s;
}

#edit-lock-toggle .toggle {
  flex: 1 1 auto;
  padding: 10px;
  text-align: center;
}

.space[contentEditable=true]:empty {
  padding-top: 40%;
  padding-left: 50%;
}


/*Downloaded from https://www.codeseek.co/thomaskhugo/sudoku-solver-2018-number-checking-class-example-dmwXGE */
    var boardContainer = document.getElementById("boardContainer");

/////////// Global board variable that holds our live board data

var board;

////////// Object with methods that creates and displays the board HTML

var boardMaker = {
  createNewBoardObject: function(){
    var board = {
        section: {},
        column: {},
        row: {},
        space: {},
    };
    
    // start with section 1
    var section = 1;
    
    for (var section_row = 1; section_row < 4; section_row++){
      // The row offset will increase the row value for spaces by 3 section each row
      var row_offset = (section_row - 1) * 3;
      for (var section_column = 1; section_column < 4; section_column++){
        // The column offset will increase the column value for spaces by 3 each row
        var column_offset = (section_column - 1) * 3;
        
        for (var space_row = 1; space_row < 4; space_row++){
          for (var space_column = 1; space_column < 4; space_column++){
            
            var _column = space_column + column_offset;
            var _row = space_row + row_offset;
            var _id = "s" + section.toString() + _row.toString() + _column.toString();
            
            // define a space
            var space = {
              section: section,
              column: _column,
              row: _row,
              id: _id,
              number: null,
              availableNumbers: [1,2,3,4,5,6,7,8,9],
              blacklist: [],
            };
            
            // add the space to the board.space object
            board.space[_id] = space;
            
            // check if the section, row, and column exists in the board and add them if necessary
            if (!board.section[space.section]){
              board.section[space.section] = [];
            }
            if (!board.column[space.column]){
              board.column[space.column] = [];
            }
            if (!board.row[space.row]){
              board.row[space.row] = [];
            }
            
            // add the space ID to the matching section, row, and column
            board.section[space.section].push(space.id);
            board.column[space.column].push(space.id);
            board.row[space.row].push(space.id);
            
          }
        }
        section += 1;
      }
    }
    
    return board;
  },
  
  makeSectionDiv: function(){
    var sectionDiv = document.createElement("div");
    sectionDiv.classList.add("section", "grid_3x3");
    return sectionDiv;
  },
  makeSpaceDiv: function(spaceID){
    var spaceDiv = document.createElement("div");
    spaceDiv.id = spaceID;
    spaceDiv.classList.add("space");
    return spaceDiv;
  },
  displayBoard: function(boardContainerElement, boardData){
    var boardMaker = this;
    var board = document.createElement("div");
    board.id = "board";
    board.classList.add("grid_3x3");
    
    for (var i = 1; i < 10; i++){
      
      var sectionDIV = boardMaker.makeSectionDiv();
      
      boardData.section[i].forEach(function(spaceID){
        sectionDIV.appendChild(boardMaker.makeSpaceDiv(spaceID));
      });
      
      board.appendChild(sectionDIV);
    }
    
    boardContainerElement.innerHTML = "";
    boardContainerElement.appendChild(board);
  },
  clearBoard: function(boardContainerElement){
    board = this.createNewBoardObject();
    this.displayBoard(boardContainerElement, board);
  },
}

/////////// Sudoku object with methods that control how our board works and solving the puzzle

var sudoku = {
  markSpace: function(boardObj, spaceID, num){
    if (/[1-9]/.test(num) && sudoku.numberIsAvailable(boardObj, spaceID, num)) {
      boardObj.space[spaceID].number = Number(num);
      sudoku.updateAvailableNumbersForAllSpaces(boardObj);
    }
    if (num === "Backspace") {
      boardObj.space[spaceID].number = null;
      sudoku.updateAvailableNumbersForAllSpaces(boardObj);
    }
  },
  
  numberIsAvailable: function(boardObj, spaceID, num){
    
    var availableNumbersArr = boardObj.space[spaceID].availableNumbers;
    
    num = Number(num);
    
    ///////// Imperative
    
    for (var i = 0; i < availableNumbersArr.length; i++){
      if (availableNumbersArr[i] == num){
        return true
      }
    }
    
    return false
    
    ///////// Delcarative
    
    
    var numIsAvailable = availableNumbersArr.includes(num);
    
    if (numIsAvailable){
      return true;
    } else {
      return false;
    }
    
    ///////////
    
    return boardObj.space[spaceID].availableNumbers.includes(num);
  },
  
  numberIsAvailableInSpaceArray: function(boardObj, spaceArray, num){    
    return spaceArray.every(function(spaceID){
      return boardObj.space[spaceID].number != num;
    });
  },
  updateAvailableNumbersForSpace: function(boardObj, spaceID){
    
    var sectionNum = boardObj.space[spaceID].section;
    var rowNum = boardObj.space[spaceID].row;
    var columnNum = boardObj.space[spaceID].column;
    
    var sectionArr = boardObj.section[sectionNum];
    var rowArr = boardObj.row[rowNum];
    var columnArr = boardObj.column[columnNum];
    
    var allNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
    
    var newAvailableNumbers = allNumbers.filter(function(num){
      var availableInSection = sudoku.numberIsAvailableInSpaceArray(boardObj, sectionArr, num);
      var availableInRow = sudoku.numberIsAvailableInSpaceArray(boardObj, rowArr, num);
      var availableInColumn = sudoku.numberIsAvailableInSpaceArray(boardObj, columnArr, num);
      
      return (availableInSection && availableInRow && availableInColumn);
    });
    
    boardObj.space[spaceID].availableNumbers = newAvailableNumbers;
    
  },
  updateAvailableNumbersForAllSpaces: function(boardObj){
    
    var spaces = boardObj.space;
    
    for (var space in spaces){
      sudoku.updateAvailableNumbersForSpace(boardObj, space);
    }
    
  },
}

board = boardMaker.createNewBoardObject();
boardMaker.displayBoard(boardContainer, board);

// Buttons // UI // Display

function updateUI(boardObj){
  var spaces = Array.from(document.getElementsByClassName("space"));
  spaces.forEach(function(space){
      space.innerHTML = boardObj.space[space.id].number;
  });
}

function unlockBoard(elementArray){
  elementArray.forEach(function(element){
    element.setAttribute("contenteditable", true);
    element.style.backgroundColor = "#ddd";
  });
};


unlockBoard(Array.from(document.getElementsByClassName("space")));

// Event Listeners


document.addEventListener("keydown", function(e){
  e.preventDefault();
  sudoku.markSpace(board, e.target.id, e.key);
  updateUI(board);
});

Comments