A puzzle is a full grid with clues removed - but only as many as can go while a single solution remains. Today you write the generator, removing cells in a seeded order and keeping each removal only if the puzzle stays uniquely solvable.
Generate a uniquely-solvable puzzle by removing clues from a full grid while uniqueness holds.
Making a puzzle is digging holes: start from a full solution and remove clues, but never so many that the answer stops being unique. So you visit the cells in a seeded shuffled order and, for each, tentatively blank it and count solutions - if the count is still exactly 1 the hole stays, otherwise you put the clue back. What remains is a puzzle that is as sparse as this greedy order allows while still having one and only one solution.
The whole run is deterministic because every random choice - the fill, the removal order - comes from the one seeded generator, advanced in a fixed sequence. Seed 7 yields a specific 26-clue puzzle, and the proof it is well-formed is that solving it returns the very full grid you started from. This is generation and solving closing a loop: you can now make puzzles and immediately solve them back, all from a single reproducible seed.
// dig holes in a shuffled order, restoring any removal that breaks uniquenessfunc Generate(seed uint32) [81]int {r := &RNG{state: seed}full := fillEmpty(r) // same generator, continues advancingorder := shuffledPositions(r) // shuffle 0..80 with the same rpuz := fullfor _, cell := range order {saved := puz[cell]; puz[cell] = 0if !IsUnique(puz) { puz[cell] = saved } // undo if now ambiguous}return puz}