A backtracking solver works by picking an unfilled cell and trying digits in it. Today you write the routine that finds the next blank - and reports when there are none, which is how the solver will know it is done.
Return the index of the first blank cell, or a sentinel when the grid is full.
Backtracking search fills a puzzle one blank at a time, so its first question each
step is “which cell do I work on next?” The simplest honest answer is the
lowest-indexed blank - scan cells 0 to 80 and return the first 0. Fixing a
single, deterministic choice of cell is what makes the whole solver reproducible:
given the same grid it always explores in the same order, so every solution you
assert against is exact.
The sentinel matters as much as the cell. When the scan finds no blank at all, the
grid is completely filled, and returning -1 is how the recursion will recognise
that it has reached a solution and should stop. A tiny routine, but it is both the
“pick a cell” and the “am I done?” half of the search you write next.
// lowest-indexed blank, or -1 if the grid is completely filledfunc FirstEmpty(g [81]int) int {for i := 0; i < 81; i++ {if g[i] == 0 { return i }}return -1}