Sudoku Solver with HTML, CSS, and JavaScript

Faraz

By Faraz -

Learn how to build a Sudoku Solver using HTML, CSS, and JavaScript. Make interactive puzzles and enhance your web development skills.


Sudoku Solver with HTML, CSS, and JavaScript.jpg

Table of Contents

  1. Project Introduction
  2. HTML Code
  3. CSS Code
  4. JavaScript Code
  5. Preview
  6. Conclusion

Sudoku, a beloved puzzle game that tests your logical prowess, has captivated minds around the world. Now, imagine not only playing Sudoku but also creating a web-based Sudoku Solver, all within your skill set. In this project, we embark on an exciting journey to build a Sudoku Solver from scratch, using the trifecta of web development: HTML, CSS, and JavaScript. Our mission is to provide an interactive and user-friendly platform for Sudoku enthusiasts to solve puzzles, share their own, and explore the magic of web-based Sudoku.

Join us as we dive into the world of Sudoku and web development, crafting a delightful experience for puzzle aficionados.

Join My Telegram Channel to Download the Project Source Code: Click Here

Prerequisites:

Before starting this tutorial, you should have a basic understanding of HTML, CSS, and JavaScript. Additionally, you will need a code editor such as Visual Studio Code or Sublime Text to write and save your code.

Source Code

Step 1 (HTML Code):

To get started, we will first need to create a basic HTML file. In this file, we will include the main structure for our sudoku solver.

After creating the files just paste the following codes into your file. Make sure to save your HTML document with a .html extension, so that it can be properly viewed in a web browser.

Let's break down the code step by step:

1. <!DOCTYPE html>: This is the document type declaration, specifying that the document is an HTML5 document.

2. <html lang="en">: This is the opening tag for the HTML document. The lang attribute is set to "en" to specify that the language of the document is English.

3. <head>: The <head> element contains metadata about the document and links to external resources. Within the <head>, you have:

  • <meta charset="UTF-8">: This specifies the character encoding for the document, which is set to UTF-8, ensuring that text and special characters are encoded correctly.
  • <meta http-equiv="X-UA-Compatible" content="IE=edge">: This meta tag is typically used to set the compatibility mode for Internet Explorer. It's set to "IE=edge," which is a standard value to ensure the best compatibility with modern versions of Internet Explorer.
  • <meta name="viewport" content="width=device-width, initial-scale=1.0">: This meta tag defines the viewport properties for responsive web design. It sets the initial scale to 1.0 and adjusts the width to the device's width, making the page adapt to different screen sizes.
  • <title>Sudoku Solver</title>: This sets the title of the web page to "Sudoku Solver," which will be displayed in the browser's title bar or tab.
  • <link rel="stylesheet" href="styles.css">: This is a link to an external CSS file named "styles.css." It's used to apply styles and formatting to the HTML elements on the page.

4. <body>: The <body> element contains the actual content of the web page. Inside the <body>, you have:

  • <div id="container">: This is a <div> element with the ID "container" and serves as a container for the page's content.
  • <h1 class="padd">Sudoku Solver</h1>: This is a top-level heading (<h1>) with the class "padd." It displays the text "Sudoku Solver."
  • <table id="sudoku-board">: This is a table element with the ID "sudoku-board," which is used to represent the Sudoku game board. It contains a series of rows and cells to form the Sudoku grid.
  • <colgroup>: These elements define groups of columns within the table, presumably to set column widths for the Sudoku grid.
  • <tbody>: This element contains the actual Sudoku grid's content, organized into rows (<tr>) and cells (<td>). Each cell has the attribute contenteditable="true" to allow users to input values into the Sudoku grid.
  • <div class="btns">: This is a <div> element with the class "btns," which contains two buttons.
  • <button id="solve-button">Solve</button>: This is a button with the ID "solve-button" and the text "Solve." It is used to trigger the Sudoku-solving functionality.
  • <button id="clear-button">Clear board</button>: This is another button with the ID "clear-button" and the text "Clear board." It is probably used to reset or clear the Sudoku board.

<script src="script.js"></script>: This is a reference to an external JavaScript file named "script.js." It links the JavaScript code to the HTML page, allowing for dynamic functionality and interactivity on the web page.

This is the basic structure of our sudoku solver using HTML, and now we can move on to styling it using CSS.

Step 2 (CSS Code):

Once the basic HTML structure of the sudoku solver is in place, the next step is to add styling to the game using CSS.

Next, we will create our CSS file. In this file, we will use some basic CSS rules to style our game. Let me explain each part of the code:

1. @import:

  • This rule is used to import an external font from Google Fonts. It imports the "Water Brush" font and specifies that it should be used as a fallback if it's not available on the user's system. The display=swap parameter tells the browser to display text using the fallback font until the custom font is loaded.

2. body:

  • This selector targets the <body> element of the HTML document.
  • It sets the background color of the entire page to a light pinkish color (#f6cce5).

3. #container:

  • This selector targets an element with the ID of "container."
  • It centers the text within this container element.

4. .padd:

  • This selector targets elements with the class "padd."
  • It sets the font size to 5.6 rem and specifies the font family as 'Water Brush' and cursive. This class is used to style specific text elements on the page.

5. table:

  • This selector targets all <table> elements in the HTML.
  • It sets the table's border-collapse property to "collapse," which removes the spacing between table cells.
  • It sets the font size to 2 em and centers the table on the page.

6. colgroup, tbody:

  • These selectors target <colgroup> and <tbody> elements within the table.
  • They apply a solid medium border to these elements, which may not be a common practice but is used for styling purposes.

7. td:

  • This selector targets all table cells (i.e., <td> elements) within the table.
  • It sets various styles for the table cells, including borders, height, width, text alignment, and padding.

8. .btns:

  • This selector targets elements with the class "btns."
  • It arranges the child elements with display: flex, horizontally centers the text, and centers the content within the container.

9. button:

  • This selector targets all <button> elements.
  • It sets various styles for buttons, including font size, background color, text color, margins, border, border radius, and padding.
  • On hover, the text color and background color change, creating a visual effect.

10. @media:

  • This is a media query that applies styles based on the screen width.
  • When the screen width is 460px or less, it reduces the font size for elements with the class "padd," the font size for the table, and the font size for buttons. This helps in creating a responsive design for smaller screens.

This will give our sudoku solver an upgraded presentation. Create a CSS file with the name of styles.css and paste the given codes into your CSS file. Remember that you must create a file with the .css extension.

@import url('https://fonts.googleapis.com/css2?family=Water+Brush&display=swap');
body {
    background-color: #f6cce5;
}

#container {
  text-align: center;
}

.padd {
    font-size: 5.6rem;
    font-family: 'Water Brush', cursive;
}

table {
  border-collapse: collapse;
  font-size: 2em;
  margin: 0 auto;
}

colgroup,
tbody {
  border: solid medium;
}

td {
  border: solid thin;
  height: 1.4em;
  width: 1.4em;
  text-align: center;
  padding: 0;
}

.btns{
    display: flex;
    text-align: center;
    justify-content: center;
}

button {
  margin-top: 15px;
  font-size: 1.5em;
  background-color: #A288E3;
  color: white;
  margin-left: 5px;
  border: #A288E3;
  border-radius: 5px;
  padding: 5px 10px;
}

button:hover{
    color: #A288E3;
    background-color: white;
    outline: #A288E3;
    transition: all 1s ease;
    cursor: pointer;
}

@media screen and (max-width: 460px) {
    .padd {
        font-size: 4.2rem;
    }
    
    table {
      font-size: 1.5em;
    }

    button{
        font-size: 1rem;
    }
} 

Step 3 (JavaScript Code):

Finally, we need to create a function in JavaScript. Let me break down the code into its main components and explain what it does:

1. Event Listeners:

  • The code begins with event listeners attached to various elements in the HTML document. These event listeners respond to user interactions.
  • The first event listener listens for "keyup" events on elements with the ID "sudoku-board," which corresponds to a Sudoku puzzle board. It checks if the event target is a table cell (TD element) and then validates the input to ensure it's a number between 1 and 9. If the input is valid, it keeps only the first digit and clears the cell if the input is not valid.
  • The second event listener listens for a "click" event on an element with the ID "solve-button." When clicked, it extracts the current state of the puzzle board, attempts to solve it using the SudokuSolver, and updates the puzzle board with the solution or displays an alert if the board is invalid.
  • The third event listener listens for a "click" event on an element with the ID "clear-button." When clicked, it clears all cells on the Sudoku board.

2. Functions:

  • clearBoard(): Clears all cells on the Sudoku board by setting their content to an empty string.
  • boardToString(): Converts the current state of the Sudoku board to a string. It iterates through the table cells, validates their content, and converts it to a string, where empty cells are represented as hyphens ("-").
  • stringToBoard(string): Takes a string representation of the Sudoku board and populates the table cells with the corresponding values.
  • SudokuSolver: This object contains various functions related to solving Sudoku puzzles. It uses a recursive approach to solve Sudoku puzzles.

3. Sample Puzzles:

  • The code defines three sample Sudoku puzzles of different difficulty levels: EASY_PUZZLE, MEDIUM_PUZZLE, and HARD_PUZZLE.

4. SudokuSolver Object:

  • The SudokuSolver object contains multiple functions for solving Sudoku puzzles. These functions include:
  • solve(boardString): Takes a string representation of the Sudoku board and attempts to solve it, returning the solved board as a string or false if the board is unsolvable.
  • recursiveSolve(boardString): A recursive function that attempts to solve the Sudoku puzzle.
  • Various utility functions for checking the validity of the board, including boardIsInvalid, boardIsValid, boardIsSolved, and functions for validating rows, columns, and boxes.
  • Functions for extracting possibilities for an empty cell (getNextCellAndPossibilities) and checking all values that intersect with a particular cell (getAllIntersections).
  • toString(boardArray): Converts a Sudoku board represented as an array to a string for display.

5. TESTABLE Flag:

  • The TESTABLE flag is used to determine whether to expose all the helper functions for testing purposes. If set to true, these functions are available for testing.

Create a JavaScript file with the name script.js and paste the given codes into your JavaScript file and make sure it's linked properly to your HTML document so that the scripts are executed on the page. Remember, you’ve to create a file with .js extension.

document
  .getElementById("sudoku-board")
  .addEventListener("keyup", function (event) {
    if (event.target && event.target.nodeName == "TD") {
      var validNum = /[1-9]/;
      var tdEl = event.target;
      if (tdEl.innerText.length > 0 && validNum.test(tdEl.innerText[0])) {
        tdEl.innerText = tdEl.innerText[0];
      } else {
        tdEl.innerText = "";
      }
    }
  });

document
  .getElementById("solve-button")
  .addEventListener("click", function (event) {
    var boardString = boardToString();
    var solution = SudokuSolver.solve(boardString);
    if (solution) {
      stringToBoard(solution);
    } else {
      alert("Invalid board!");
    }
  });

document.getElementById("clear-button").addEventListener("click", clearBoard);

function clearBoard() {
  var tds = document.getElementsByTagName("td");
  for (var i = 0; i < tds.length; i++) {
    tds[i].innerText = "";
  }
}

function boardToString() {
  var string = "";
  var validNum = /[1-9]/;
  var tds = document.getElementsByTagName("td");
  for (var i = 0; i < tds.length; i++) {
    if (validNum.test(tds[i].innerText[0])) {
      string += tds[i].innerText;
    } else {
      string += "-";
    }
  }
  return string;
}

function stringToBoard(string) {
  var currentCell;
  var validNum = /[1-9]/;
  var cells = string.split("");
  var tds = document.getElementsByTagName("td");
  for (var i = 0; i < tds.length; i++) {
    currentCell = cells.shift();
    if (validNum.test(currentCell)) {
      tds[i].innerText = currentCell;
    }
  }
}

("use strict");

var EASY_PUZZLE =
  "1-58-2----9--764-52--4--819-19--73-6762-83-9-----61-5---76---3-43--2-5-16--3-89--";
var MEDIUM_PUZZLE =
  "-3-5--8-45-42---1---8--9---79-8-61-3-----54---5------78-----7-2---7-46--61-3--5--";
var HARD_PUZZLE =
  "8----------36------7--9-2---5---7-------457-----1---3---1----68--85---1--9----4--";

var TESTABLE = true;

var SudokuSolver = (function (testable) {
  var solver;

  function solve(boardString) {
    var boardArray = boardString.split("");
    if (boardIsInvalid(boardArray)) {
      return false;
    }
    return recursiveSolve(boardString);
  }

  function solveAndPrint(boardString) {
    var solvedBoard = solve(boardString);
    console.log(toString(solvedBoard.split("")));
    return solvedBoard;
  }

  function recursiveSolve(boardString) {
    var boardArray = boardString.split("");
    if (boardIsSolved(boardArray)) {
      return boardArray.join("");
    }
    var cellPossibilities = getNextCellAndPossibilities(boardArray);
    var nextUnsolvedCellIndex = cellPossibilities.index;
    var possibilities = cellPossibilities.choices;
    for (var i = 0; i < possibilities.length; i++) {
      boardArray[nextUnsolvedCellIndex] = possibilities[i];
      var solvedBoard = recursiveSolve(boardArray.join(""));
      if (solvedBoard) {
        return solvedBoard;
      }
    }
    return false;
  }

  function boardIsInvalid(boardArray) {
    return !boardIsValid(boardArray);
  }

  function boardIsValid(boardArray) {
    return (
      allRowsValid(boardArray) &&
      allColumnsValid(boardArray) &&
      allBoxesValid(boardArray)
    );
  }

  function boardIsSolved(boardArray) {
    for (var i = 0; i < boardArray.length; i++) {
      if (boardArray[i] === "-") {
        return false;
      }
    }
    return true;
  }

  function getNextCellAndPossibilities(boardArray) {
    for (var i = 0; i < boardArray.length; i++) {
      if (boardArray[i] === "-") {
        var existingValues = getAllIntersections(boardArray, i);
        var choices = ["1", "2", "3", "4", "5", "6", "7", "8", "9"].filter(
          function (num) {
            return existingValues.indexOf(num) < 0;
          }
        );
        return { index: i, choices: choices };
      }
    }
  }

  function getAllIntersections(boardArray, i) {
    return getRow(boardArray, i)
      .concat(getColumn(boardArray, i))
      .concat(getBox(boardArray, i));
  }

  function allRowsValid(boardArray) {
    return [0, 9, 18, 27, 36, 45, 54, 63, 72]
      .map(function (i) {
        return getRow(boardArray, i);
      })
      .reduce(function (validity, row) {
        return collectionIsValid(row) && validity;
      }, true);
  }

  function getRow(boardArray, i) {
    var startingEl = Math.floor(i / 9) * 9;
    return boardArray.slice(startingEl, startingEl + 9);
  }

  function allColumnsValid(boardArray) {
    return [0, 1, 2, 3, 4, 5, 6, 7, 8]
      .map(function (i) {
        return getColumn(boardArray, i);
      })
      .reduce(function (validity, row) {
        return collectionIsValid(row) && validity;
      }, true);
  }

  function getColumn(boardArray, i) {
    var startingEl = Math.floor(i % 9);
    return [0, 1, 2, 3, 4, 5, 6, 7, 8].map(function (num) {
      return boardArray[startingEl + num * 9];
    });
  }

  function allBoxesValid(boardArray) {
    return [0, 3, 6, 27, 30, 33, 54, 57, 60]
      .map(function (i) {
        return getBox(boardArray, i);
      })
      .reduce(function (validity, row) {
        return collectionIsValid(row) && validity;
      }, true);
  }

  function getBox(boardArray, i) {
    var boxCol = Math.floor(i / 3) % 3;
    var boxRow = Math.floor(i / 27);
    var startingIndex = boxCol * 3 + boxRow * 27;
    return [0, 1, 2, 9, 10, 11, 18, 19, 20].map(function (num) {
      return boardArray[startingIndex + num];
    });
  }

  function collectionIsValid(collection) {
    var numCounts = {};
    for (var i = 0; i < collection.length; i++) {
      if (collection[i] != "-") {
        if (numCounts[collection[i]] === undefined) {
          numCounts[collection[i]] = 1;
        } else {
          return false;
        }
      }
    }
    return true;
  }

  function toString(boardArray) {
    return [0, 9, 18, 27, 36, 45, 54, 63, 72]
      .map(function (i) {
        return getRow(boardArray, i).join(" ");
      })
      .join("\n");
  }

  if (testable) {
    solver = {
      solve: solve,
      solveAndPrint: solveAndPrint,
      recursiveSolve: recursiveSolve,
      boardIsInvalid: boardIsInvalid,
      boardIsValid: boardIsValid,
      boardIsSolved: boardIsSolved,
      getNextCellAndPossibilities: getNextCellAndPossibilities,
      getAllIntersections: getAllIntersections,
      allRowsValid: allRowsValid,
      getRow: getRow,
      allColumnsValid: allColumnsValid,
      getColumn: getColumn,
      allBoxesValid: allBoxesValid,
      getBox: getBox,
      collectionIsValid: collectionIsValid,
      toString: toString,
    };
  } else {
    solver = { solve: solve, solveAndPrint: solveAndPrint };
  }

  return solver;
})(TESTABLE);

Final Output:

Sudoku Solver with HTML, CSS, and JavaScript.gif

Conclusion:

In conclusion, we've successfully created a web-based Sudoku Solver using HTML, CSS, and JavaScript. This project not only enhances your web development skills but also provides an enjoyable Sudoku-solving experience. To further improve this project, consider adding features like difficulty levels, score tracking, and multiplayer options.

Start your journey in web development with this exciting Sudoku Solver project. Enjoy the logical challenge while mastering the art of creating interactive web applications.

Credit: ZeroOctave

That’s a wrap!

I hope you enjoyed this post. Now, with these examples, you can create your own amazing page.

Did you like it? Let me know in the comments below 🔥 and you can support me by buying me a coffee.

And don’t forget to sign up to our email newsletter so you can get useful content like this sent right to your inbox!

Thanks!
Faraz 😊

End of the article

Subscribe to my Newsletter

Get the latest posts delivered right to your inbox


Latest Post