Let's make a sudoku solver - Part 1 - Representation & Validation
October 02, 2021
In this brief article, we’ll set the stage for a Sudoku Solver program using Dart.
To quote from Wikipedia:
A standard Sudoku contains 81 cells, in a 9×9 grid, and has 9 boxes, each box being the intersection of the first, middle, or last 3 rows, and the first, middle, or last 3 columns. Each cell may contain a number from one to nine, and each number can only occur once in each row, column, and box. A Sudoku starts with some cells containing numbers (clues), and the goal is to solve the remaining cells.
Let’s assemble the parts together like little legos
1. Representation
A standard sudoku puzzle is the one with a 9 x 9
grid with only valid digits 1
to 9
that can be filled in the cells.
For example, let’s look at a typical sudoku puzzle:
One easy way to represent this puzzle in the code is using a two dimensional array ( just a fancy array of arrays ).
We can use 0
to represent the empty cell since it’s not a valid digit.
// Simple two dimensional array representation// of above sudoku puzzle// let's put it in a variable `board`var board = [[5, 3, 0, 0, 7, 0, 0, 0, 0],[6, 0, 0, 1, 9, 5, 0, 0, 0],[0, 9, 8, 0, 0, 0, 0, 6, 0],[8, 0, 0, 0, 6, 0, 0, 0, 3],[4, 0, 0, 8, 0, 3, 0, 0, 1],[7, 0, 0, 0, 2, 0, 0, 0, 6],[0, 6, 0, 0, 0, 0, 2, 8, 0],[0, 0, 0, 4, 1, 9, 0, 0, 5],[0, 0, 0, 0, 8, 0, 0, 7, 9],];
2. Validation
Before we actually go about building a solution. It might help to understand what qualifies as a correct solution.
Let’s explore if the sudoku puzzle that we’re going to deal with is actually valid to begin with. Also, once we figured the ‘algorithm’ to solve the puzzle, it’ll be important that we have way to determine if the puzzle is really solved.
1. To verify if a puzzle is VALID
We can perform following checks to verify if a given sudoku puzzle is valid.
Check all columns and verify no repeated digits:
Check all rows and verify no repeated digits
Verify all the
3x3
grids ( Let’s call it a ‘Box’) and make sure no digits are repeated within these boxes.
We may simplify the above validations by looking at it from the perspective of a single cell. We’re NOT really trying to optimize, but this way, it will be easier to wrap our heads around it!
Let’s dig into some code.
First of all, let’s start by declaring some useful constants:
// size of the sudoku puzzle ( Since it's a standard puzzle it's 9)const int SIZE = 9;// size of the internal grid ('Box') (we have a 3x3 grid, it'll be 3)const int BOXSIZE = 3;
Now, let’s write a function to check for a given cell in sudoku ( specified using row and column index of our two dimensional array with the name board
) is valid according to above validations :
/// A function to verify if the value at given/// row and column is valid/// will return `true` if valid, otherwise `false`bool checkIfCellIsValid(int row, int col) {int val = board[row][col]; // get the digit at given row and columnif(val != 0){ // do the below checks if it's not empty cell// SECTION 1 : check if cell is unique in Rowif(!isUniqueInRow(row, col, val)){return false;}// SECTION 2: check if cell is unique in Columnif(!isUniqueInColumn(row, col, val)){return false;}// SECTION 3: check if cell is unique in Boxif(!isUniqueInBox(row, col, val)){return false;}}// if none of the above checks has returned `false` so far,// It has to be valid, so return truereturn true;}
Now, let’s try implementing each sections.
SECTION 1: check if the
val
is unique in a given ROW :bool isUniqueInRow(int row, int col, int val){// check val is unique in given ROW// by going through all columns in that rowfor (int c = 0; c < SIZE; c++) {if (c == col) continue; // don't compare with itselfint _otherCellVal = board[row][c];if (_otherCellVal != 0 && _otherCellVal == val) {return false;}}return true;}SECTION 2: check if the
val
is unique in a given COLUMNbool isUniqueInColumn(int row, int col, int val){// check val is uniuqe in given COLUMN// by going through all rows in that columnfor (int r = 0; r < SIZE; r++) {if (r == row) continue; // don't compare with itselfint _otherCellVal = board[r][col];if (_otherCellVal != 0 && _otherCellVal == val) {return false;}}return true;}SECTION 3: check if the
val
is unique in a given ‘Box’bool isUniqueInBox(int row, int col, int val){int bxRowStart = row - row % BOXSIZE;int bxColStart = col - col % BOXSIZE;// check if val is unique in BOX// by going through all rows and columns in the// given 'BOX'for (int r = bxRowStart; r < bxRowStart + BOXSIZE; r++) {for (int c = bxColStart; c < bxColStart + BOXSIZE; c++) {if (r == row && c == col) continue; // don't compare with itselfint _otherCellVal = board[r][c];if (_otherCellVal != 0 && _otherCellVal == val) {return false;}}}return true;}
2. Verify if the puzzle is SOLVED
It’ll be very useful to know if the puzzle is solved already, and done so correctly.
Now that we know how to check if the cells in a sudoku puzzle are valid, verifying if the puzzle is solved is much simpler:
- Use the above steps to verify if the puzzle is valid ( Ie, all the cells are valid)
- Make sure no empty cells left (that is, no more zeroes in our 2D array)
The code for which would look something like this :
bool isSolved(){// go through all the cells in the puzzlefor(int r = 0; r < SIZE; r++){for(int c = 0; c < SIZE; c++){int val = board[r][c];if(val == 0){// found an empty cell,// so it's not solvedreturn false;}if(!checkIfCellIsValid(r,c)){// found an invalid cell// so it's not solvedreturn false;}}}// no empty cells left and// all cells are valid, so puzzle is solvedreturn true;}
That’s it for the first part of this series!
In next one, we’ll explore how we can come up with an ‘algorithm’ to actually solve the puzzle!
See you in the next one!