Generate a Complete Sudoku Grid with Backtracking Algorithm in JavaScript

Published: May 19, 2024

Generate a Complete Sudoku Grid with Backtracking Algorithm in JavaScript

In this blogpost, we will see how to generate a complete sudoku grid like below using a backtracking algorithm in JavaScript. Click on to see the algorithm in action!

3

Filled # cells:

1

Step count:

1

Background

It may sound easy to create a sudoku grid complete with numbers. It may also sound foolish to do this, because eventually what matters is solving an incomplete sudoku grid, not creating a complete one from scratch. Even if we were interested in creating one, we would like to create one with missing numbers, which can be used to play an actual game of sudoku.

However, these arguments would be wrong because:

1. It is not easy to create a complete sudoku grid.

As an exercise, try creating a 9x9 sudoku grid that satisfies the rules of the game (i.e., all numbers between 1-9 should be used across each row, column and block with no repetition) You will soon realize that it is not as easy as it looks, because while it is easy to satisfy these conditions for the first ~20 numbers, it becomes harder as you get closer to the end of the grid.

This is because the numbers you fill in earlier may/will cause problems in later cells by eliminiating all potential numbers for a cell. You then need to backtrack couple cells, change number in that earlier cell so that the later cell can be filled with a number.

2. It is not irrelevant to create a complete sudoku grid.

Complete grid creation is actually first step of creating a sudoku game board. We start with a complete grid, and remove numbers from that grid, so that the player can fill in those numbers. More numbers we delete, the harder the game becomes.

So, we can see that completed grid creation is an essential part of crafting this game. Before we dive into the game, let's look at the rules of grid creation:

Basics of Sudoku

The basic rule of of 9x9 sudoku is that, each row, column, and block in the grid should have numbers between 1-9. There are 9 cells in a row, column, or a block, so it fits numbers between 1-9 perfectly, hence no repetition is allowed.

1. Rows

Below you can see each row highlighted with row name in each cell.

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

2. Columns

Below you can see each column highlighted with column name in each cell.

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

3. Blocks

Blocks are 3x3 squares that make up the grid. Similar to rows and columns, each block should have numbers between 1-9 and no repetition is allowed.

Below you can see each block highlighted with block name in each cell.

1-1
1-1
1-1
1-2
1-2
1-2
1-3
1-3
1-3
1-1
1-1
1-1
1-2
1-2
1-2
1-3
1-3
1-3
1-1
1-1
1-1
1-2
1-2
1-2
1-3
1-3
1-3
2-1
2-1
2-1
2-2
2-2
2-2
2-3
2-3
2-3
2-1
2-1
2-1
2-2
2-2
2-2
2-3
2-3
2-3
2-1
2-1
2-1
2-2
2-2
2-2
2-3
2-3
2-3
3-1
3-1
3-1
3-2
3-2
3-2
3-3
3-3
3-3
3-1
3-1
3-1
3-2
3-2
3-2
3-3
3-3
3-3
3-1
3-1
3-1
3-2
3-2
3-2
3-3
3-3
3-3

Method

As I touched upon briefly in the above section, creating a complete grid is devilishly difficult to do by hand, because to be able to do this, you'd need to foresee all possible issues that might arise from using a certain number for a cell.

You eventually run into a cell where no number can be used, because the row, column, and block conditions rule out all possible numbers. When this happens, we backtrack.

Backtracking Algorithm

Below shows how this algorithm runs in principle:

  1. Check if there is a number between 1-9 that satisfy row, column, block constraints and that are not already tried for the cell you are on.
  2. If there is such number, set the value of your cell to that number. Add that number to the list of tried numbers for this cell. Move forward 1 cell, go to step 1.
  3. Else, clean the list of tried numbers for this cell. Move back 1 cell. Clean the value of that cell (previous cell) and go to step 1.

Below is the code version:

// Initialize variables
let grid = []; // To keep grid data
let numbers = []; // Numbers from 1-9
let cellIndex = 0; // Cell number

// initialize grid and numbers
for (let i = 0; i < 9; i++) {
	// Add to numbers
	numbers.push(i + 1);

	// For each cell, record its row, column, block, initial value, and tried values
	// Initial value and tried values are initialized as '' and [] respectively
	for (let j = 0; j < 9; j++) {
		let cell = {};
		cell.row = i;
		cell.col = j;
		cell.block = `${Math.floor(i / 3)}-${Math.floor(j / 3)}`;
		cell.value = '';
		cell.tried = [];
		grid.push(cell);
	}
}

// Function that fills the grid with backtracking algorithm
function fillGrid() {
	// Continue only if there are still cells remaining to be filled
	while (cellIndex < grid.length) {
		// Get the relevant cell
		let cell = grid[cellIndex];

		// If we tried all numbers for a cell, set the tried to []
		// Otherwise the algorithm gets blocked
		if (cell.tried.length == 9) {
			cell.tried = [];
		}

		// For this cell, get cells that belong to the same row, column and block
		let row = grid.filter((d) => d.row === cell.row).map((d) => d.value);
		let col = grid.filter((d) => d.col === cell.col).map((d) => d.value);
		let block = grid.filter((d) => d.block === cell.block).map((d) => d.value);

		// Shuffle numbers because we want to have a different grid at each time
		// Otherwise your grid will always produce the same output
		numbers = numbers
			.map((value) => ({ value, sort: Math.random() }))
			.sort((a, b) => a.sort - b.sort)
			.map(({ value }) => value);

		// Variable that keeps whether a feasible number found for a cell
		let found = false;

		// Loop through random numbers
		for (let n = 0; n < 9; n++) {
			// Get a random number
			let number = numbers[n];

			// Check if this number satisfies the conditions
			// Ie 1. it is not tried yet
			// 2. it doesnt appear in the same row as the cell
			// 3. it doesnt appear in the same column as the cell
			// 4. it doesnt appear in the same block as the cell
			if (
				!cell.tried.includes(number) &&
				!row.includes(number) &&
				!col.includes(number) &&
				!block.includes(number)
			) {
				// If the number satisfies set cell value to that number
				cell.value = number;

				// Add the number to tried numbers for this cell
				cell.tried.push(number);

				// Move to next cell
				cellIndex++;

				// Set found to true, because a number solution is found
				found = true;
				break;
			}
		}

		// If no number is found for the cell
		// Ie there is no number that satisfies above 4 conditions
		if (!found) {
			// Set tried numbers array to [] for this cell
			grid[cellIndex].tried = [];

			// Move back 1 cell
			cellIndex--;

			// Set previous cell's value to "" in order to try other numbers
			grid[cellIndex].value = '';
		}
	}
}

fillGrid();

Result

Now let's see whether above code works with below grid using the button:

It does, doesn't it? And backtracking algorithm is very efficient as it finds an answer to this problem within 1 second most of the time.

Conclusion

In this blogpost, we have seen that the task of complete sudoku grid is not trivial, because it can't be done by hand and it is an essential part of sudoku game-making.

For this task, we have developed a simple yet efficient algorithm called backtracking algorithm to create a complete sudoku grid. I could usually generate a complete sudoku grid of 81 cells within 200 steps, in less than a second.

I will follow-up this blogpost with 2 more posts:

  1. Algorithmically create sudoku games with varying difficulty by removing numbers from the complete grid
  2. Solve sudoku game algorithmically

In the meantime, happy hacking!

Leave comment

Comments

Check out other blog posts

Create A Simple and Dynamic Tooltip With Svelte and JavaScript

2024/06/19

Create A Simple and Dynamic Tooltip With Svelte and JavaScript

JavaScriptSvelteSimpleDynamicTooltipFront-end
Create an Interactive Map of Tokyo with JavaScript

2024/06/17

Create an Interactive Map of Tokyo with JavaScript

SvelteSVGJavaScriptTailwindInteractive MapTokyoJapanTokyo Metropolitan Area23 Wards
How to Easily Fix Japanese Character Issue in Matplotlib

2024/06/14

How to Easily Fix Japanese Character Issue in Matplotlib

MatplotlibGraphChartPythonJapanese charactersIssueBug
Book Review | Talking to Strangers: What We Should Know about the People We Don't Know by Malcolm Gladwell

2024/06/13

Book Review | Talking to Strangers: What We Should Know about the People We Don't Know by Malcolm Gladwell

Book ReviewTalking to StrangersWhat We Should Know about the People We Don't KnowMalcolm Gladwell
Most Commonly Used 3,000 Kanjis in Japanese

2024/06/07

Most Commonly Used 3,000 Kanjis in Japanese

Most CommonKanji3000ListUsage FrequencyJapaneseJLPTLanguageStudyingWordsKanji ImportanceWord Prevalence
Replace With Regex Using VSCode

2024/06/07

Replace With Regex Using VSCode

VSCodeRegexFindReplaceConditional Replace
Do Not Use Readable Store in Svelte

2024/06/06

Do Not Use Readable Store in Svelte

SvelteReadableWritableState ManagementStoreSpeedMemoryFile Size
Increase Website Load Speed by Compressing Data with Gzip and Pako

2024/06/05

Increase Website Load Speed by Compressing Data with Gzip and Pako

GzipCompressionPakoWebsite Load SpeedSvelteKit
Find the Word the Mouse is Pointing to on a Webpage with JavaScript

2024/05/31

Find the Word the Mouse is Pointing to on a Webpage with JavaScript

JavascriptMousePointerHoverWeb Development
Create an Interactive Map with Svelte using SVG

2024/05/29

Create an Interactive Map with Svelte using SVG

SvelteSVGInteractive MapFront-end
Book Review | Originals: How Non-Conformists Move the World by Adam Grant & Sheryl Sandberg

2024/05/28

Book Review | Originals: How Non-Conformists Move the World by Adam Grant & Sheryl Sandberg

Book ReviewOriginalsAdam Grant & Sheryl SandbergHow Non-Conformists Move the World
How to Algorithmically Solve Sudoku Using Javascript

2024/05/27

How to Algorithmically Solve Sudoku Using Javascript

Solve SudokuAlgorithmJavaScriptProgramming
How I Increased Traffic to my Website by 10x in a Month

2024/05/26

How I Increased Traffic to my Website by 10x in a Month

Increase Website TrafficClicksImpressionsGoogle Search Console
Life is Like Cycling

2024/05/24

Life is Like Cycling

CyclingLifePhilosophySuccess
Why Tailwind is Amazing and How It Makes Web Dev a Breeze

2024/05/16

Why Tailwind is Amazing and How It Makes Web Dev a Breeze

TailwindAmazingFront-endWeb Development
Generate Sitemap Automatically with Git Hooks Using Python

2024/05/15

Generate Sitemap Automatically with Git Hooks Using Python

Git HooksPythonSitemapSvelteKit
Book Review | Range: Why Generalists Triumph in a Specialized World by David Epstein

2024/05/14

Book Review | Range: Why Generalists Triumph in a Specialized World by David Epstein

Book ReviewRangeDavid EpsteinWhy Generalists Triumph in a Specialized World
What is Svelte and SvelteKit?

2024/05/13

What is Svelte and SvelteKit?

SvelteSvelteKitFront-endVite
Internationalization with SvelteKit (Multiple Language Support)

2024/05/12

Internationalization with SvelteKit (Multiple Language Support)

InternationalizationI18NSvelteKitLanguage Support
Reduce Svelte Deploy Time With Caching

2024/05/11

Reduce Svelte Deploy Time With Caching

SvelteEnhanced ImageCachingDeploy Time
Lazy Load Content With Svelte and Intersection Oberver

2024/05/10

Lazy Load Content With Svelte and Intersection Oberver

Lazy LoadingWebsite Speed OptimizationSvelteIntersection Observer
Find the Optimal Stock Portfolio with a Genetic Algorithm

2024/05/10

Find the Optimal Stock Portfolio with a Genetic Algorithm

Stock marketPortfolio OptimizationGenetic AlgorithmPython
Convert ShapeFile To SVG With Python

2024/05/09

Convert ShapeFile To SVG With Python

ShapeFileSVGPythonGeoJSON
Reactivity In Svelte: Variables, Binding, and Key Function

2024/05/08

Reactivity In Svelte: Variables, Binding, and Key Function

SvelteReactivityBindingKey Function
Book Review | The Art Of War by Sun Tzu

2024/05/07

Book Review | The Art Of War by Sun Tzu

Book ReviewThe Art Of WarSun TzuThomas Cleary
Specialists Are Dead. Long Live Generalists!

2024/05/06

Specialists Are Dead. Long Live Generalists!

SpecialistGeneralistParadigm ShiftSoftware Engineering
Analyze Voter Behavior in Turkish Elections with Python

2024/05/03

Analyze Voter Behavior in Turkish Elections with Python

TurkeyAge Analysis2018 ElectionsVoter Behavior
Create Turkish Voter Profile Database With Web Scraping

2024/05/01

Create Turkish Voter Profile Database With Web Scraping

PythonSeleniumWeb ScrapingTurkish Elections
Make Infinite Scroll With Svelte and Tailwind

2024/04/30

Make Infinite Scroll With Svelte and Tailwind

SvelteTailwindInfinite ScrollFront-end
How I Reached Japanese Proficiency In Under A Year

2024/04/29

How I Reached Japanese Proficiency In Under A Year

JapaneseProficiencyJLPTBusiness
Use-ready Website Template With Svelte and Tailwind

2024/04/25

Use-ready Website Template With Svelte and Tailwind

Website TemplateFront-endSvelteTailwind
Lazy Engineers Make Lousy Products

2024/01/29

Lazy Engineers Make Lousy Products

Lazy engineerLousy productStarbucksSBI
On Greatness

2024/01/28

On Greatness

GreatnessMeaning of lifeSatisfactory lifePurpose
Converting PDF to PNG on a MacBook

2024/01/28

Converting PDF to PNG on a MacBook

PDFPNGMacBookAutomator
Recapping 2023: Compilation of 24 books read

2023/12/31

Recapping 2023: Compilation of 24 books read

BooksReading2023Reflections
Create a Photo Collage with Python PIL

2023/12/30

Create a Photo Collage with Python PIL

PythonPILImage ProcessingCollage
Detect Device & Browser of Visitors to Your Website

2024/01/09

Detect Device & Browser of Visitors to Your Website

JavascriptDevice DetectionBrowser DetectionWebsite Analytics
Anatomy of a ChatGPT Response

2024/01/19

Anatomy of a ChatGPT Response

ChatGPTLarge Language ModelMachine LearningGenerative AI