Bitmasking + Backtracking Algorithm¶
My algorithm solves Sudoku using bitmasking, a technique that allows you to represent sets of numbers by bits in an integer. The basic idea is to represent which numbers (from 1 to 9) are still available for each row, column and 3x3 block of the Sudoku, using bitmasks. Let's see step by step how it works.
Bitmasking¶
The algorithm uses an array mask[HALF_SIZE][SIZE]
, where:
mask[0][...]
keeps track of the numbers already present per row,mask[1][...]
keeps track of the numbers present per column,mask[2][...]
keeps track of the numbers in each 3x3 block.
Each mask is a 16-bit integer (uint16_t
), where the bits represent whether or not a number exists in the row/column/block.
Note
For example, if mask[0][i] = 0b100000000
(in binary), it means that the number 9 is present in row i
(bit 9 is set to 1).
Bit manipulation functions¶
The set_bit()
function sets the bit corresponding to the presence of a number (from 1 to 9) in the mask. For example, if we enter the number k
in a Sudoku cell, the bit k
of the mask corresponding to the row, column and block is set to 1. This is done using the shift 1 << bit_position
operation which moves bit 1 to the corresponding position.
while the clear_bit()
function does the opposite: when we remove a number from a cell, the corresponding bit is cleared with &= ~(1 << bit_position)
.
Calculating the set of available numbers¶
When the algorithm needs to decide which number to insert into an empty cell, it computes the set of numbers already present in the row, column, and block of the cell. This is done using a bitwise OR operation:
// ? Calculate the set of possible numbers for the cell (row, column, block)
set = mask[0][i] | mask[1][j] | mask[2][COL_DISPLACEMENT(i, j)];
The OR operation combines the bits set in the three masks: row, column and block, resulting in a set representing the numbers already occupied in that position.
Calculating the possible options¶
To figure out how many numbers are still available (i.e. how many bits are 0 in the set), use the get_no_zeros()
function:
Here, the algorithm cycles through the bits from position 1 to 9 and counts how many bits are still 0, i.e. how many numbers can be inserted into the cell.
Search for the best candidate¶
The algorithm explores all the empty cells and looks for the one with the least number of available options, since this reduces the number of attempts. This is done with the following code:
Here we look for the cell with the smallest set of numbers (i.e. the one with the fewest options available).
Backtracking¶
Finally, the algorithm tries to insert every possible number (the 0 bits in the set) into the selected cell, then recursively calls the same function to solve the rest of the Sudoku:
If recursion fails, the entered number is removed and another is tried. If no number works, the cell is reset to 0, and the process is backtracked.
Solution¶
If all cells are filled correctly without contradictions, the Sudoku is solved.
The solve()
function initializes the masks based on the numbers already present and calls the recursive function bit_operations()
to solve the Sudoku.