Facebook Twitter Instagram
    DeepCrazyWorld
    Facebook Twitter Instagram Pinterest YouTube
    • FLUTTER
      • FLUTTER APP
        • QRCode
        • Quiz App
        • Chat GPT
        • PDF App
        • News App
        • Fitness App
        • Weather App
        • BMI Calculator
        • GAME APP
        • Ecommerce App
        • wallpaper App
        • Finance app
        • Chat App
        • Wallet App
        • Taxi App
        • Quran app
        • Music player app
      • FLUTTER UI
        • Splash Screen
        • Onboarding Screen
        • Login Screen
        • Card Design
        • Drawer
    • PROJECT
      • Android Projects
      • College Projects
      • FLUTTER APP
      • Project Ideas
      • PHP Projects
      • Python Projects
    • SOURCE CODE
    • ANDROID
      • ANDROID APP
      • GAME APP
      • ANDROID STUDIO
    • MCQ
      • AKTU MCQ
        • RPA MCQ
        • COA MCQ
        • HPC MCQ
        • SPM MCQ
        • Renewable Energy All MCQ
        • Data Compression MCQ
        • Data Structure MCQ
        • Digital Image Processing MCQ
        • Software Engineering MCQ
        • Machine Learning MCQ
        • Artificial Intelligence MCQ
      • D PHARMA MCQ
        • Pharmaceutics – I MCQ
        • Pharmacognosy MCQ
        • Pharmaceutical Chemistry MCQ
        • Biochemistry and Clinical Pathology MCQ
        • Human Anatomy and Physiology MCQ
        • Heath Education and Community Pharmacy MCQ
    • INTERVIEW QUESTIONS
      • Flutter Interview Questions
      • INTERVIEW QUESTIONS
      • Python Interview Questions
      • Coding ninjas solution
    • MORE
      • WORDPRESS
        • SEO
        • TOP 10 WORDPRESS THEME
      • PRODUCTIVITY
      • Program
      • QUOTES
    DeepCrazyWorld
    Home»Web Developer»Sudoku : How to Create a Sudoku Puzzle game with Html
    Web Developer

    Sudoku : How to Create a Sudoku Puzzle game with Html

    DeepikaBy DeepikaAugust 1, 2020Updated:April 17, 2023No Comments19 Mins Read

    When We first started playing with writing a Sudoku game, I took the long way to create Sudoku templates. Using a recursive backtracking algorithm, I would slowly fill up the grid with random numbers – checking the validity of the puzzle at each step. If I hit a roadblock where the puzzle cannot be completed, the algorithm would backtrack until it could move forward, and then move forward again. how to make a sudoku game in javascript,
    how to make a sudoku puzzle in html

    There is a more elegant solution however: create a solved sudoku and then shuffle it.

    Table of Contents

    Toggle
    • Generate and Solved Sudoku
    • Sudoku Puzzle Game Source Code
    • index.html
    • Sudoku puzzle in html
    • style.css
    • script.js
    • YouTube Video
    • READ MORE

    Generate and Solved Sudoku

    1.Step : Creating the solved Sudoku is easy: just shift the row above to the left by 3 unless its vertical index is equally divisible by 3 (starting with index 0) in which case shift the row above by 4.

    1 2 3 4 5 6 7 8 9
    4 5 6 7 8 9 1 2 3
    7 8 9 1 2 3 4 5 6
    2 3 4 5 6 7 8 9 1
    5 6 7 8 9 1 2 3 4
    8 9 1 2 3 4 5 6 7
    3 4 5 6 7 8 9 1 2
    6 7 8 9 1 2 3 4 5
    9 1 2 3 4 5 6 7 8


    2.Step : Shuffle the rows and columns. Of course, to do so and ensure the Sudoku rules maintain integrity, you can only shuffle rows and columns of the same group: groups being 1-3, 4-6, and 7-9. Demonstrated below, the columns 1 and 3 are shuffled

    <img decoding=

    Sudoku Puzzle Game Source Code

    index.html

    <!DOCTYPE html>
    <html lang="en" >
    <head>
      <meta charset="UTF-8">
      <title>DeepCrazyWorld - Sudoku Puzzle Project</title>
      <meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
    <link rel="stylesheet" href="./style.css">
    
    </head>
    <body>
    
    
    <div id='sudoku-app'></div>
    
    <script type='text/javascript'>
      "use strict";!function(a){if("function"==typeof bootstrap)bootstrap("bem",a);else if("object"==typeof exports&&"object"==typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define(a);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeBem=a}else{if("undefined"==typeof window&&"undefined"==typeof self)throw new Error("This environment was not anticipated by bem. Please file a bug.");var b="undefined"!=typeof window?window:self,c=b.bem;b.bem=a(),b.bem.noConflict=function(){return b.bem=c,this}}}(function(){function a(a){"undefined"!=typeof a.modifier&&(c.modifier=a.modifier),"undefined"!=typeof a.element&&(c.element=a.element)}function b(a){if(!d.validate(a))return null;var b=a.block,e=a.element,f=a.modifiers,g=b,h=[];return!!e&&(g+=""+c.element+e),!!f&&Object.keys(f).forEach(function(a){var d=f[a],i="function"==typeof d?d(b,e,f):d;!!i&&h.push(""+g+c.modifier+a+" ")}),(g+" "+h.join("")).slice(0,-1)}var c={element:"__",modifier:"--"},d={messages:{block:"You must specify the name of block.",element:"Element name must be a string.",modifier:"Modifiers must be supplied in the `{name : bool || fn}` style."},blockName:function(a){return"undefined"!=typeof a&&"string"==typeof a&&a.length?!0:(console.warn(this.messages.block),!1)},element:function(a){return"undefined"!=typeof a&&"string"!=typeof a?(console.warn(this.messages.element),!1):!0},modifiers:function(a){return"undefined"==typeof a||"object"==typeof a&&"[object Object]"===toString.call(a)?!0:(console.warn(this.messages.modifier),!1)},validate:function(a){return this.blockName(a.block)&&this.element(a.element)&&this.modifiers(a.modifiers)}};return{setDelimiters:a,makeClassName:b}});
    </script>
    
    <!-- Include Babel to transform code in browser -->
    <script type='text/javascript'
            src='https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.25/browser-polyfill.min.js'>
    </script>
    <script type='text/javascript'
            src='https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.25/browser.min.js'>
    </script>
    
    <!-- worker, sudoku api -->
    <script type='text/babel' id='worker'>
      
      self.sudoku = null
      
      // Worker Setup
      self.addEventListener('message', (event) => {
        var options = { method: null }
        try {
          options = JSON.parse(event.data);
        } catch (e) {
          console.warn('event.data is misformed', event)
        }
      
        switch (options.method) {
      
          case 'generate':
            var { hints, limit } = options
            self.sudoku = new Sudoku(hints, limit).generate()
    
            self.postMessage({
              success: self.sudoku.success,
              board: self.sudoku.getBoard(),
              solution: self.sudoku.getSolution()
            });
            break;
      
          case 'validate':
            var { map, number, index } = options
            self.postMessage({
              result: sudoku.validate(map, number, index)
            });
            break;
      
        }
      }, false);
    
      // API
      class Sudoku {
        constructor(hints, limit) {
          this.hints = hints
          this.limit = limit || 10000
      
          this._logs = {
            raw: [],
            incidents: {
              limitExceeded: 0,
              notValid: 0,
              noNumbers: 0
            }
          }
      
          this.success = null
    
          this.numbers = () =>
            new Array(9)
              .join(" ")
              .split(" ")
              .map((num , i) => i + 1)
    
          /*
            Will be used in initial map. Each row will be
            consisted of randomly ordered numbers
          */
          this.randomRow = () => {
            var row = []
            var numbers = this.numbers()
            while (row.length < 9) {
              var index = Math.floor(Math.random() * numbers.length)
              row.push(numbers[index])
              numbers.splice(index, 1)
            }
    
            return row
          }
    
          /*
            This is the dummy placeholder for the
            final results. Will be overridden through the
            backtracking process, and at the and, this will
            be the real results.
          */
          this.result = new Array(9 * 9)
            .join(" ")
            .split(" ")
            .map(entry => null)
    
          /*
            Will be used as the nodeTree in the
            process of backtracking. Each cell has 9 alternative
            paths (randomly ordered).
          */
          this.map = new Array(9 * 9)
            .join(" ")
            .split(" ")
            .map(path => this.randomRow())
    
          /*
            Will be used as history in the backtracking
            process for checking if a candidate number is valid.
          */
          this.stack = []
    
          return this
        }
      
        toRows(arr) {
          var row = 0
          var asRows = new Array(9)
            .join(" ")
            .split(" ")
            .map(row => [])
      
          for (let [index, entry] of arr.entries()) {
            asRows[row].push(entry)
    
            if ( !((index + 1) % 9) ) {
              row += 1
            }
          }
    
          return asRows
        }
    
        no(path, index, msg) {
          var number = path[path.length - 1]
          this._logs.raw.push(`no: @${index} [${number}] ${msg} ${path} `)
        }
    
        yes(path, index) {
          this._logs.raw.push(`yes: ${index} ${path}`)
        }
      
        finalLog() {
          console.groupCollapsed('Raw Logs')
          console.groupCollapsed(this._logs.raw)
          console.groupEnd()
          console.groupEnd()
          console.groupCollapsed('Incidents')
          console.groupCollapsed(this._logs.incidents)
          console.groupEnd()
          console.groupEnd()
        }
    
        getBoard() {
          return this.toRows(this.substractCells())
        }
    
        getSolution() {
          return this.toRows(this.result)
        }
    
        substractCells() {
          var _getNonEmptyIndex = () => {
            var index = Math.floor(Math.random() * _result.length)
            return _result[index] ? index : _getNonEmptyIndex()
          }
    
          var _result = this.result.filter(() => true)
    
          while (
            _result.length - this.hints >
            _result.filter(n => !n).length
          ) {
            _result[_getNonEmptyIndex()] = ''
          }
    
          return _result
        }
      
        validate(map, number, index) {
          var rowIndex = Math.floor(index / 9)
          var colIndex = index % 9
    
          var row = map.slice(
            rowIndex * 9, 9 * (rowIndex + 1)
          )
    
          var col = map.filter((e, i) =>
            i % 9 === colIndex
          )
    
          var boxRow = Math.floor(rowIndex / 3)
          var boxCol = Math.floor(colIndex / 3)
    
          var box = map.filter((e, i) =>
            Math.floor(Math.floor(i / 9) / 3) === boxRow &&
            Math.floor((i % 9) / 3) === boxCol
          )
    
          return {
            row: {
              first: row.indexOf(number),
              last: row.lastIndexOf(number)
            },
            col: {
              first: col.indexOf(number),
              last: col.lastIndexOf(number)
            },
            box: {
              first: box.indexOf(number),
              last: box.lastIndexOf(number)
            }
          }
        }
    
        _validate(map, index) {
          if (!map[index].length) {
            return false
          }
    
          this.stack.splice(index, this.stack.length)
      
          var path = map[index]
          var number = path[path.length - 1]
      
          var didFoundNumber = this.validate(this.stack, number, index)
      
          return (
            didFoundNumber.col.first === -1 &&
            didFoundNumber.row.first === -1 &&
            didFoundNumber.box.first === -1
          )
        }
    
        _generate(map, index) {
          if (index === 9 * 9) {
            return true
          }
    
          if (--this.limit < 0) {
            this._logs.incidents.limitExceeded++
            this.no(map[index], index, 'limit exceeded')
            return false
          }
    
          var path = map[index]
    
          if (!path.length) {
            map[index] = this.numbers()
            map[index - 1].pop()
            this._logs.incidents.noNumbers++
            this.no(path, index, 'no numbers in it')
            return false
          }
    
          var currentNumber = path[path.length - 1]
    
          var isValid = this._validate(map, index)
          if (!isValid) {
            map[index].pop()
            map[index + 1] = this.numbers()
            this._logs.incidents.notValid++
            this.no(path, index, 'is not valid')
            return false
          } else {
            this.stack.push(currentNumber)
          }
    
          for (let number of path.entries()) {
            if (this._generate(map, index + 1)) {
              this.result[index] = currentNumber
              this.yes(path, index)
              return true
            }
          }
    
          return false
        }
    
        generate() {
          if (this._generate(this.map, 0)) {
            this.success = true
          }
    
          this.finalLog()
    
          return this
        }
    
      }
    </script>
    <!-- partial -->
      <script src='https://cdn.rawgit.com/MaxArt2501/object-observe/master/dist/object-observe-lite.min.js'></script><script  src="./script.js"></script>
    
    </body>
    </html>
    

    Sudoku puzzle in html

    style.css

    
    \**********************************/
    @font-face {
      src: url("http://enes.in/GillSansTr-LightNr.otf");
      font-family: Gill;
      font-weight: 100;
    }
    @font-face {
      src: url("http://enes.in/GillSansTr-Normal.otf");
      font-family: Gill;
      font-weight: 300;
    }
    @font-face {
      src: url("http://enes.in/GillSansTr-Bold.otf");
      font-family: Gill;
      font-weight: 600;
    }
    @font-face {
      src: url("http://enes.in/GillSansTr-ExtraBold.otf");
      font-family: Gill;
      font-weight: 700;
    }
    @font-face {
      src: url("http://enes.in/GillSansTr-UltraBold.otf");
      font-family: Gill;
      font-weight: 900;
    }
    html, body {
      width: 100%;
      height: 100%;
    }
    
    body {
      margin: 0;
      background: #f0f0f0;
    }
    
    @media (max-width: 260px) {
      .show-on-sm {
        display: none;
      }
    
      .show-on-md {
        display: none;
      }
    
      .show-on-lg {
        display: none;
      }
    
      .show-on-xs {
        display: block;
      }
    }
    @media (max-width: 420px) {
      .show-on-xs {
        display: none;
      }
    
      .show-on-md {
        display: none;
      }
    
      .show-on-lg {
        display: none;
      }
    
      .show-on-sm {
        display: block;
      }
    }
    @media (min-width: 421px) and (max-width: 615px) {
      .show-on-xs {
        display: none;
      }
    
      .show-on-sm {
        display: none;
      }
    
      .show-on-lg {
        display: none;
      }
    
      .show-on-md {
        display: block;
      }
    }
    @media (min-width: 615px) {
      .show-on-xs {
        display: none;
      }
    
      .show-on-sm {
        display: none;
      }
    
      .show-on-md {
        display: none;
      }
    
      .show-on-lg {
        display: block;
      }
    }
    @-webkit-keyframes progress {
      0% {
        box-shadow: none;
      }
      25% {
        box-shadow: 2px -2px 0 1px;
      }
      50% {
        box-shadow: 2px -2px 0 1px, 7px -2px 0 1px;
      }
      100% {
        box-shadow: 2px -2px 0 1px, 7px -2px 0 1px, 12px -2px 0 1px;
      }
    }
    @keyframes progress {
      0% {
        box-shadow: none;
      }
      25% {
        box-shadow: 2px -2px 0 1px;
      }
      50% {
        box-shadow: 2px -2px 0 1px, 7px -2px 0 1px;
      }
      100% {
        box-shadow: 2px -2px 0 1px, 7px -2px 0 1px, 12px -2px 0 1px;
      }
    }
    .fr {
      float: right;
    }
    
    .fl {
      float: left;
    }
    
    @media (max-width: 260px) {
      .button {
        padding: 0.25em 0.5em;
        font-size: 0.6em;
      }
      .button:not(:last-of-type) {
        margin-right: 0.15em;
      }
      .button--loading {
        padding-right: 1.5em;
      }
    }
    @media (min-width: 261px) and (max-width: 420px) {
      .button {
        padding: 0.25em 0.5em 0.15em;
        font-size: 0.75em;
      }
      .button:not(:last-of-type) {
        margin-right: 0.25em;
      }
      .button--loading {
        padding-right: 1.5em;
      }
    }
    @media (min-width: 421px) and (max-width: 615px) {
      .button {
        padding: 0.5em 0.75em 0.4em;
        font-size: 0.9em;
      }
      .button:not(:last-of-type) {
        margin-right: 0.5em;
      }
      .button--loading {
        padding-right: 1.5em;
      }
    }
    @media (min-width: 615px) {
      .button {
        padding: 0.75em 1em 0.6em;
        font-size: 1em;
      }
      .button:not(:last-of-type) {
        margin-right: 0.75em;
      }
      .button--loading {
        padding-right: 1.5em;
      }
    }
    .button {
      border: 1px solid;
      font-weight: normal;
      border-radius: 3px;
      background: none;
      box-shadow: none;
      -webkit-transition: all 0.2s;
      transition: all 0.2s;
    }
    .button--primary {
      color: #4242d7;
      font-weight: 600;
    }
    .button--primary:hover, .button--primary:focus, .button--primary:active {
      border-color: #4242d7;
      background: #4242d7;
    }
    .button--primary:focus {
      box-shadow: 0 0 5px #4242d7;
    }
    .button--secondary {
      color: #d74242;
    }
    .button--secondary:hover, .button--secondary:focus, .button--secondary:active {
      border-color: #d74242;
      background: #d74242;
    }
    .button--secondary:focus {
      box-shadow: 0 0 5px #d74242;
    }
    .button--tertiary {
      color: #fff;
      border-color: #2ECC40;
      background: #2ECC40;
    }
    .button--neutral {
      color: #333;
    }
    .button--neutral:hover, .button--neutral:focus, .button--neutral:active {
      border-color: #333;
      background: #333;
    }
    .button--neutral:focus {
      box-shadow: 0 0 5px #333;
    }
    .button--compound {
      border-radius: 0;
      border-right: none;
    }
    .button--compound-first {
      border-bottom-left-radius: 3px;
      border-top-left-radius: 3px;
    }
    .button--compound-last {
      border-bottom-right-radius: 3px;
      border-top-right-radius: 3px;
      border-right: 1px solid;
    }
    .button--muted {
      pointer-events: none;
    }
    .button--disabled {
      border-color: #bbb;
      color: #bbb;
      pointer-events: none;
    }
    .button--loading-text::after {
      display: inline-block;
      width: 1px;
      height: 1px;
      content: '';
      box-shadow: 2px -2px 1px 0;
      -webkit-animation: progress 1s infinite;
              animation: progress 1s infinite;
    }
    .button:hover, .button:focus, .button:active {
      color: #fff;
    }
    .button:focus {
      outline: none;
    }
    .button:active {
      box-shadow: inset 0 -2px 10px rgba(0, 0, 0, 0.4);
    }
    
    .message {
      font-size: .9em;
      padding: 2em;
      margin: 0;
      border-radius: 3px;
      color: rgba(0, 0, 0, 0.75);
    }
    .message--busy {
      background: rgba(0, 0, 255, 0.1);
    }
    .message--fail {
      background: rgba(255, 0, 0, 0.1);
    }
    
    @media (max-width: 260px) {
      .sudoku {
        margin: 0 auto;
        padding-top: 0.5em;
        padding-bottom: 0.5em;
      }
      .sudoku__header {
        padding-bottom: 0.6em;
      }
      .sudoku__title {
        font-size: 1em;
      }
      .sudoku__table {
        font-size: 0.9em;
        border-top: 2px solid #444;
        border-left: 2px solid #444;
        border-collapse: collapse;
      }
      .sudoku__table-row {
        border-bottom: 1px solid #444;
        border-right: 2px solid #444;
      }
      .sudoku__table-row--separator {
        border-bottom: 2px solid #444;
      }
      .sudoku__table-cell {
        width: 16px;
        height: 16px;
        border-right: 1px solid #444;
      }
      .sudoku__table-cell--separator {
        border-right: 2px solid #444;
      }
    
      .sudoku {
        max-width: calc(260px / 1.5);
        min-width: calc(260px / 2);
      }
    }
    @media (min-width: 261px) and (max-width: 420px) {
      .sudoku {
        margin: 0 auto;
        padding-top: 1em;
        padding-bottom: 1em;
      }
      .sudoku__header {
        padding-bottom: 0.9em;
      }
      .sudoku__title {
        font-size: 1.2em;
      }
      .sudoku__table {
        font-size: 1.2em;
        border-top: 3px solid #444;
        border-left: 3px solid #444;
        border-collapse: collapse;
      }
      .sudoku__table-row {
        border-bottom: 1px solid #444;
        border-right: 3px solid #444;
      }
      .sudoku__table-row--separator {
        border-bottom: 3px solid #444;
      }
      .sudoku__table-cell {
        width: 32px;
        height: 32px;
        border-right: 1px solid #444;
      }
      .sudoku__table-cell--separator {
        border-right: 3px solid #444;
      }
    
      .sudoku {
        width: 260px;
      }
    }
    @media (min-width: 421px) and (max-width: 615px) {
      .sudoku {
        margin: 0 auto;
        padding-top: 2em;
        padding-bottom: 2em;
      }
      .sudoku__header {
        padding-bottom: 1.3em;
      }
      .sudoku__title {
        font-size: 1.5em;
      }
      .sudoku__table {
        font-size: 1.5em;
        border-top: 4px solid #444;
        border-left: 4px solid #444;
        border-collapse: collapse;
      }
      .sudoku__table-row {
        border-bottom: 1px solid #444;
        border-right: 4px solid #444;
      }
      .sudoku__table-row--separator {
        border-bottom: 4px solid #444;
      }
      .sudoku__table-cell {
        width: 48px;
        height: 48px;
        border-right: 1px solid #444;
      }
      .sudoku__table-cell--separator {
        border-right: 4px solid #444;
      }
    
      .sudoku {
        width: 420px;
      }
    }
    @media (min-width: 615px) {
      .sudoku {
        margin: 0 auto;
        padding-top: 3em;
        padding-bottom: 3em;
      }
      .sudoku__header {
        padding-bottom: 1.618em;
      }
      .sudoku__title {
        font-size: 2em;
      }
      .sudoku__table {
        font-size: 1.75em;
        border-top: 6px solid #444;
        border-left: 6px solid #444;
        border-collapse: collapse;
      }
      .sudoku__table-row {
        border-bottom: 2px solid #444;
        border-right: 6px solid #444;
      }
      .sudoku__table-row--separator {
        border-bottom: 6px solid #444;
      }
      .sudoku__table-cell {
        width: 64px;
        height: 64px;
        border-right: 2px solid #444;
      }
      .sudoku__table-cell--separator {
        border-right: 6px solid #444;
      }
    
      .sudoku {
        width: 615px;
      }
    }
    .sudoku {
      color: #444;
    }
    .sudoku__header {
      font-family: Gill, sans-serif;
    }
    .sudoku__title {
      font-weight: 600;
    }
    .sudoku__description {
      max-width: 640px;
      line-height: 1.4;
      font-weight: 100;
    }
    .sudoku__table {
      background: #fff;
    }
    .sudoku__table-cell {
      overflow: hidden;
      text-align: center;
      -webkit-transition: all .25s;
      transition: all .25s;
    }
    .sudoku__table-cell--editable {
      color: #2020df;
    }
    .sudoku__table-cell--editable:focus {
      background: rgba(0, 0, 255, 0.1);
      outline: none;
    }
    .sudoku__table-cell--error {
      color: red;
      background: #fdd;
    }
    .sudoku__table-cell--editable-error {
      text-shadow: 0 0 15px;
    }
    .sudoku__table-cell--editable-error:focus {
      color: #eee;
      background: #f45;
    }

    script.js

    // Utility
    var utils = (() => {
      function dom(selector) {
        if (selector[0] === '#') {
          return document.getElementById(selector.slice(1));
        }
        return document.querySelectorAll(selector);
      }
    
      function copyJSON(obj) {
        return JSON.parse(JSON.stringify(obj));
      }
    
      function isTouchDevice() {
        return navigator.userAgent.
        match(/(iPhone|iPod|iPad|Android|BlackBerry)/);
      }
    
      function getWorkerURLFromElement(selector) {
        var element = dom(selector);
        var content = babel.transform(element.innerText).code;
        var blob = new Blob([content], { type: 'text/javascript' });
        return URL.createObjectURL(blob);
      }
    
        var cursorManager = function () {
        var cursorManager = {};
    
        var voidNodeTags = [
        'AREA', 'BASE', 'BR', 'COL', 'EMBED',
        'HR', 'IMG', 'INPUT', 'KEYGEN', 'LINK',
        'MENUITEM', 'META', 'PARAM', 'SOURCE',
        'TRACK', 'WBR', 'BASEFONT', 'BGSOUND',
        'FRAME', 'ISINDEX'];
    
    
        Array.prototype.contains = function (obj) {
          var i = this.length;
          while (i--) {
            if (this[i] === obj) {
              return true;
            }
          }
          return false;
        };
    
        function canContainText(node) {
          if (node.nodeType == 1) {
            return !voidNodeTags.contains(node.nodeName);
          } else {
            return false;
          }
        };
    
        function getLastChildElement(el) {
          var lc = el.lastChild;
          while (lc && lc.nodeType != 1) {
            if (lc.previousSibling)
            lc = lc.previousSibling;else
    
            break;
          }
          return lc;
        }
        cursorManager.setEndOfContenteditable = function (contentEditableElement) {
    
          while (getLastChildElement(contentEditableElement) &&
          canContainText(getLastChildElement(contentEditableElement))) {
            contentEditableElement = getLastChildElement(contentEditableElement);
          }
    
          var range, selection;
          if (document.createRange) {
            range = document.createRange();
            range.selectNodeContents(contentEditableElement);
            range.collapse(false);
            selection = window.getSelection();
            selection.removeAllRanges();
            selection.addRange(range);
          } else
          if (document.selection)
          {
            range = document.body.createTextRange();
            range.moveToElementText(contentEditableElement);
            range.collapse(false);
            range.select();
          }
        };
    
        return cursorManager;
      }();
    
      return {
        copyJSON, cursorManager, dom,
        getWorkerURLFromElement, isTouchDevice };
    
    })();
    
    
    // API Adapter
    class SudokuAdapter {
      constructor(url) {
        this.worker = new Worker(url);
        return this;
      }
    
      _postMessage(options) {
        this.worker.postMessage(JSON.stringify(options));
        return new Promise((resolve, reject) => {
          this.worker.onmessage = event => {
            resolve(event.data);
          };
        });
      }
    
      generate(options) {
        options = Object.assign(
        {}, options, { method: 'generate' });
    
        return this._postMessage(options);
      }
    
      validate(options) {
        options = Object.assign(
        {}, options, { method: 'validate' });
    
        return this._postMessage(options);
      }}
    
    
    
    // Client Side Settings
    const SUDOKU_APP_CONFIG = {
      HINTS: 34,
      TRY_LIMIT: 100000,
      WORKER_URL: utils.getWorkerURLFromElement('#worker'),
      DOM_TARGET: utils.dom('#sudoku-app') };
    
    
    
    // Client Side
    var SudokuApp = (config => {
      const {
        HINTS, TRY_LIMIT,
        WORKER_URL, DOM_TARGET } =
      config;
    
      var sudokuAdapter = new SudokuAdapter(WORKER_URL);
    
      var state = {
        success: null,
        board: null,
        solution: null,
        solved: null,
        errors: [] };
    
      Object.observe(state, render);
    
      var history = [state];
      var historyStash = [];
    
    
      // Event listeners
      var onClickGenerate = initialize;
    
      var onClickSolve = function () {
        setState({
          board: state.solution,
          solved: true,
          errors: [] });
    
      };
    
      var onKeyUpCell = function (event) {
        var key = event.keyCode;
        if ( // a
        key === 36 || // r
        key === 37 || // r
        key === 38 || // o
        key === 39 || // w
        key === 9 || // tab
        // mod key flags are always false in keyup event
        // keyIdentifier doesn't seem to be implemented
        // in all browsers
        key === 17 || // Control
        key === 16 || // Shift
        key === 91 || // Meta
        key === 19 || // Alt
        event.keyIdentifier === 'Control' ||
        event.keyIdentifier === 'Shift' ||
        event.keyIdentifier === 'Meta' ||
        event.keyIdentifier === 'Alt')
        return;
    
        var cell = event.target;
        var value = cell.innerText;
    
        if (value.length > 4) {
          cell.innerText = value.slice(0, 4);
          return false;
        }
    
        var cellIndex = cell.getAttribute('data-cell-index');
        cellIndex = parseInt(cellIndex, 10);
        var rowIndex = Math.floor(cellIndex / 9);
        var cellIndexInRow = cellIndex - rowIndex * 9;
    
        var board = Object.assign([], state.board);
        board[rowIndex].splice(cellIndexInRow, 1, value);
    
        validate(board).then(errors => {
          historyStash = [];
          history.push({});
          var solved = null;
          if (errors.indexOf(true) === -1) {
            solved = true;
            board.forEach(row => {
              row.forEach(value => {
                if (!value || !parseInt(value, 10) || value.length > 1) {
                  solved = false;
                }
              });
            });
          }
          if (solved) {
            board = Object.assign([], board).map(row => row.map(n => +n));
          }
          setState({ board, errors, solved }, newState => {
            history[history.length - 1] = newState;
            restoreCaretPosition(cellIndex);
          });
        });
      };
    
      function keyDown(event) {
        var keys = {
          ctrlOrCmd: event.ctrlKey || event.metaKey,
          shift: event.shiftKey,
          z: event.keyCode === 90 };
    
    
        if (keys.ctrlOrCmd && keys.z) {
          if (keys.shift && historyStash.length) {
            redo();
          } else if (!keys.shift && history.length > 1) {
            undo();
          }
        }
      }
    
      function undo() {
        historyStash.push(history.pop());
        setState(utils.copyJSON(history[history.length - 1]));
      }
    
      function redo() {
        history.push(historyStash.pop());
        setState(utils.copyJSON(history[history.length - 1]));
      }
    
    
      function initialize() {
        unbindEvents();
        render();
        getSudoku().then(sudoku => {
          setState({
            success: sudoku.success,
            board: sudoku.board,
            solution: sudoku.solution,
            errors: [],
            solved: false },
          newState => {
            history = [newState];
            historyStash = [];
          });
        });
      }
    
      function setState(newState, callback) {
        requestAnimationFrame(() => {
          Object.assign(state, newState);
          if (typeof callback === 'function') {
            var param = utils.copyJSON(state);
            requestAnimationFrame(callback.bind(null, param));
          }
        });
      }
    
      function bindEvents() {
        var generateButton = utils.dom('#generate-button');
        var solveButton = utils.dom('#solve-button');
        var undoButton = utils.dom('#undo-button');
        var redoButton = utils.dom('#redo-button');
        generateButton &&
        generateButton.
        addEventListener('click', onClickGenerate);
        solveButton &&
        solveButton.
        addEventListener('click', onClickSolve);
        undoButton &&
        undoButton.
        addEventListener('click', undo);
        redoButton &&
        redoButton.
        addEventListener('click', redo);
    
        var cells = utils.dom('.sudoku__table-cell');
        [].forEach.call(cells, cell => {
          cell.addEventListener('keyup', onKeyUpCell);
        });
    
        window.addEventListener('keydown', keyDown);
      }
    
      function unbindEvents() {
        var generateButton = utils.dom('#generate-button');
        var solveButton = utils.dom('#solve-button');
        var undoButton = utils.dom('#undo-button');
        var redoButton = utils.dom('#redo-button');
        generateButton &&
        generateButton.
        removeEventListener('click', onClickGenerate);
        solveButton &&
        solveButton.
        removeEventListener('click', onClickSolve);
        undoButton &&
        undoButton.
        removeEventListener('click', undo);
        redoButton &&
        redoButton.
        removeEventListener('click', redo);
    
        var cells = utils.dom('.sudoku__table-cell');
        [].forEach.call(cells, cell => {
          cell.removeEventListener('keyup', onKeyUpCell);
        });
    
        window.removeEventListener('keydown', keyDown);
      }
    
      function restoreCaretPosition(cellIndex) {
        utils.cursorManager.setEndOfContenteditable(
        utils.dom(`[data-cell-index="${cellIndex}"]`)[0]);
    
      }
    
      function getSudoku() {
        return sudokuAdapter.generate({
          hints: HINTS,
          limit: TRY_LIMIT });
    
      }
    
      function validate(board) {
        var map = board.reduce((memo, row) => {
          for (let num of row) {
            memo.push(num);
          }
          return memo;
        }, []).map(num => parseInt(num, 10));
    
        var validations = [];
    
        // Will validate one by one
        for (let [index, number] of map.entries()) {
          if (!number) {
            validations.push(
            new Promise(res => {
              res({ result: { box: -1, col: -1, row: -1 } });
            }));
    
          } else {
            let all = Promise.all(validations);
            validations.push(all.then(() => {
              return sudokuAdapter.validate({ map, number, index });
            }));
          }
        }
    
        return Promise.all(validations).
        then(values => {
          var errors = [];
          for (let [index, validation] of values.entries()) {
            let { box, col, row } = validation.result;
            let errorInBox = box.first !== box.last;
            let errorInCol = col.first !== col.last;
            let errorInRow = row.first !== row.last;
    
            let indexOfRow = Math.floor(index / 9);
            let indexInRow = index - indexOfRow * 9;
    
            errors[index] = errorInRow || errorInCol || errorInBox;
          }
    
          return errors;
        });
      }
    
      function render() {
        unbindEvents();
    
        DOM_TARGET.innerHTML = `
          <div class='sudoku'>
            ${headerComponent()}
            ${contentComponent()}
          </div>
        `;
    
        bindEvents();
      }
    
      function buttonComponent(props) {
        var { id, text, mods, classes } = props;
    
        var blockName = 'button';
        var modifiers = {};
        var modType = toString.call(mods);
        if (modType === '[object String]') {
          modifiers[mods] = true;
    
        } else if (modType === '[object Array]') {
          for (let modName of mods) {
            modifiers[modName] = true;
          }
        }
    
        var blockClasses = bem.makeClassName({
          block: blockName,
          modifiers: modifiers });
    
    
        var buttonTextClass = `${blockName}-text`;
        if (Object.keys(modifiers).length) {
          buttonTextClass +=
          Object.keys(modifiers).reduce((memo, curr) => {
            return memo + ` ${blockName}--${curr}-text`;
          }, '');
    
        }
    
        var lgText = typeof text === 'string' ?
        text : text[0];
        var mdText = typeof text === 'string' ?
        text : text[1];
        var smText = typeof text === 'string' ?
        text : text[2];
    
        return `
          <button
            id='${id}'
            class='${blockClasses} ${classes || ""}'>
            <span class='show-on-sm ${buttonTextClass}'>
              ${smText}
            </span>
            <span class='show-on-md ${buttonTextClass}'>
              ${mdText}
            </span>
            <span class='show-on-lg ${buttonTextClass}'>
              ${lgText}
            </span>
          </button>
        `;
      }
    
      function messageComponent(options) {
        var { state, content } = options;
    
        var messageClass = bem.makeClassName({
          block: 'message',
          modifiers: state ? {
            [state]: true } :
          {} });
    
    
        return `
          <p class='${messageClass}'>
            ${content}
          </p>
        `;
      }
    
      function descriptionComponent(options) {
        var { className, infoLevel } = options;
    
        var technical = `
          In this demo,
          <a href='https://en.wikipedia.org/wiki/Backtracking'>
            backtracking algorithm
          </a> is used for <em>making</em>
          the sudoku project.`;
    
        var description = `
          Difficulty and solvability is
          totally random as I randomly left a certain number of hints
          from a full-filled board.
        `;
    
        if (infoLevel === 'full') {
          return `
            <p class='${className || ''}'>
              ${technical} ${description}
            </p>
          `;
    
        } else if (infoLevel === 'mini') {
          return `
            <p class='${className || ''}'>
              ${description}
            </p>
          `;
        }
      }
    
      function restoreScrollPosComponent() {
        return `<div style='height: 540px'></div>`;
      }
    
      function headerComponent() {
        return `
          <div class='sudoku__header'>
    
            <h1 class='sudoku__title'>
    
              <span class='show-on-sm'>
                Sudoku
              </span>
    
              <span class='show-on-md'>
                Sudoku Puzzle
              </span>
    
              <span class='show-on-lg'>
                Sudoku Puzzle Project
              </span>
    
            </h1>
    
            ${descriptionComponent({
          infoLevel: 'mini',
          className: 'sudoku__description show-on-md' })
        }
    
            ${descriptionComponent({
          infoLevel: 'full',
          className: 'sudoku__description show-on-lg' })
        }
    
            ${
        state.success ? `
        
                  ${buttonComponent({
          id: 'generate-button',
          text: ['New Board', 'New Board', 'New'],
          mods: 'primary' })
        }
        
                  ${state.solved ?
        buttonComponent({
          id: 'solve-button',
          text: 'Solved',
          mods: ['tertiary', 'muted'] }) :
    
        buttonComponent({
          id: 'solve-button',
          text: 'Solve',
          mods: 'secondary' })
    
        }
    
                ` :
    
        `
        
                  ${buttonComponent({
          id: 'generate-button',
          text: ['Generating', '', ''],
          mods: ['disabled', 'loading'] })
        }
        
                  ${buttonComponent({
          id: 'solve-button',
          text: 'Solve',
          mods: 'disabled' })
        }
                `
    
        }
    
            ${utils.isTouchDevice() ? `
    
              ${buttonComponent({
          id: 'redo-button',
          text: ['&raquo;', '&raquo;', '&gt;', '&gt;'],
          classes: 'fr',
          mods: [
          'neutral',
          'compound',
          'compound-last',
          `${!historyStash.length ?
          'disabled' :
          ''
          }`] })
    
        }
              ${buttonComponent({
          id: 'undo-button',
          text: ['&laquo;', '&laquo;', '&lt;', '&lt;'],
          classes: 'fr',
          mods: [
          'neutral',
          'compound',
          'compound-first',
          `${history.length > 1 ?
          '' :
          'disabled'
          }`] })
    
        }
    
          ` : ''}
    
          </div>
        `;
      }
    
      function contentComponent() {
        var _isSeparator = (index) =>
        !!index && !((index + 1) % 3);
    
        var resultReady = !!state.board;
        var fail = resultReady && !state.success;
    
        if (!resultReady) {
          return `
            ${messageComponent({
            state: 'busy',
            content: `Generating new board...` })
          }
            ${restoreScrollPosComponent()}
          `;
        }
    
        if (fail) {
          return `
            ${messageComponent({
            state: 'fail',
            content: `Something went wrong with this board, try generating another one.` })
          }
            ${restoreScrollPosComponent()}
          `;
        }
    
        var rows = state.board;
    
        return `
          <table class='sudoku__table'>
    
            ${rows.map((row, index) => {
          let className = bem.makeClassName({
            block: 'sudoku',
            element: 'table-row',
            modifiers: {
              separator: _isSeparator(index) } });
    
    
    
          return (
            `<tr class='${className}'>
    
                  ${row.map((num, _index) => {
              let cellIndex = index * 9 + _index;
              let separator = _isSeparator(_index);
              let editable = typeof num !== 'number';
              let error = state.errors[cellIndex];
              let className = bem.makeClassName({
                block: 'sudoku',
                element: 'table-cell',
                modifiers: {
                  separator,
                  editable,
                  error,
                  'editable-error': editable && error } });
    
    
    
              return (
                `\n\t
                      <td class='${className}'
                          data-cell-index='${cellIndex}'
                          ${editable ? 'contenteditable' : ''}>
                            ${num}
                      </td>`);
    
            }).join('')}
    
                \n</tr>\n`);
    
    
        }).join('')}
    
          </table>
        `;
      }
    
      return { initialize };
    
    })(SUDOKU_APP_CONFIG).initialize();

    YouTube Video

    And this can be continued for as many iterations as one likes, shuffling random rows and columns from a random group, resulting in a pseudo-random puzzle. I am still waiting for the day when I solve a puzzle and the result is the first puzzle above: not sure I would even notice until it is close to being solved.

    READ MORE

    Don’t forget to share this post!

    Share. Facebook Twitter LinkedIn WhatsApp Telegram Pinterest Reddit Email
    Previous ArticleLove Shayari in Hindi – Amazing Collection of Love Shayaris
    Next Article Sudoku : How to Make a Sudoku puzzle Game in html

    Related Posts

    Top 20 Amazing Websites जिनके बारे में आपको जानना चाहिए 2023

    Web Developer 4 Mins Read

    Top 5 Beginner’s Guide: How To Learn Web Design At Home 2023

    Web Developer 12 Mins Read

    Model View Controller Pattern – MVC Architecture & Frameworks(updated)

    Web Developer 6 Mins Read

    Unable to load class ‘javax.xml.bind.JAXBException’

    Web Developer 3 Mins Read

    Leave A Reply Cancel Reply

    Recent Posts
    • Implementing a Dynamic FAQ Screen UI in Flutter Using ExpansionTile March 29, 2025
    • Creating an Instruction UI Screen in Flutter Application March 29, 2025
    • Animated Backgrounds in Flutter: A Complete Guide March 15, 2025
    • How to make Diary App using flutter stepwise using getx August 31, 2024
    • How to Create Music Player UI screen with fully functional in flutter August 30, 2024
    • How to make ListView Builder Ui in flutter with Source Code August 29, 2024
    • Create a TabBar View in flutter with fully functional stepwise August 28, 2024
    • How to create TabBar view in flutter with source code step wise August 27, 2024
    • How to make Heart rate measure app with Flutter stepwise August 26, 2024
    • How to make ChatGpt App in flutter with source code Stepwise August 25, 2024
    Facebook Twitter Instagram Pinterest YouTube
    • About
    • Contact
    • Disclaimer
    • Privacy Policy
    Copyright by DeepCrazyWorld © 2025

    Type above and press Enter to search. Press Esc to cancel.