I have written following code to solve Sudoku puzzles in Racket (a Scheme/Lisp derivative programming language). Following relatively simple code appears to work but are there any bugs in it or can it be further optimized? (semi-colon indicates start of comment on that line).
(define (SolveSudoku sentboard)
(define (subgrid brd r c)
(define ll '((0 1 2)(3 4 5)(6 7 8)))
(define-values (rr cc)
(values (flatten (filter (λ (x) (member r x)) ll))
(flatten (filter (λ (x) (member c x)) ll))))
(for*/list ((i rr)(j cc))
(list-ref (list-ref brd i) j)))
(let loop ((bd sentboard) (r 0) (c 0))
(cond [(= 0 (list-ref (list-ref bd r) c) )
(for ((i (range 1 10)))
(when (and (not(member i (list-ref bd r)))
(not(member i (map (λ (x) (list-ref x c)) bd)))
(not(member i (subgrid bd r c))))
(define newbd (list-set bd r
(list-set (list-ref bd r) c i)))
(cond [(< (add1 c) 9)
(loop newbd r (add1 c) )]
[(< (add1 r) 9)
(loop newbd (add1 r) 0 )]
[else (displayln "SOLUTION:")
(for ((rowline newbd)) (println rowline))])))]
[(< (add1 c) 9)
(loop bd r (add1 c))]
[(< (add1 r) 9)
(loop bd (add1 r) 0)]
[else (displayln "Solution:")
(for ((rowline bd)) (println rowline))])))
Following is code with comments added:
(define (SolveSudoku sentboard)
; subfn to get list of numbers in 3x3 subgrid for a given row and column position:
(define (subgrid brd r c)
(define ll '((0 1 2)(3 4 5)(6 7 8)))
; get row/column set to which r and c belong:
(define-values (rr cc)
(values (flatten (filter (λ (x) (member r x)) ll))
(flatten (filter (λ (x) (member c x)) ll))))
; get all numbers from this set:
(for*/list ((i rr)(j cc))
(list-ref (list-ref brd i) j)))
; start with sent board and first row and column:
(let loop ((bd sentboard) (r 0) (c 0))
(cond
; if entry at (r,c) is 0, try to put numbers from 1 to 9:
[(= 0 (list-ref (list-ref bd r) c) )
(for ((i (range 1 10)))
; if i is not present in that row, column & subgrid:
(when
(and (not(member i (list-ref bd r))) ; number not in row
(not(member i (map (λ (x) (list-ref x c)) bd))) ; number not in column
(not(member i (subgrid bd r c)))) ; number not in subgrid
; create a new board with i added:
(define newbd
(list-set bd r
(list-set (list-ref bd r) c i)))
(cond
; go to next column and loop:
[(< (add1 c) 9)
(loop newbd r (add1 c) )]
; if columns over, go to next row and loop:
[(< (add1 r) 9)
(loop newbd (add1 r) 0 )]
; if rows also over, solution found:
[else (displayln "SOLUTION:")
(for ((rowline newbd)) (println rowline))])))]
; if entry is not 0 go to next column or row (as above):
[(< (add1 c) 9)
(loop bd r (add1 c))]
[(< (add1 r) 9)
(loop bd (add1 r) 0)]
[else (displayln "Solution:")
(for ((rowline bd)) (println rowline))])))
Testing:
(define board
'((0 0 3 0 2 0 6 0 0)
(9 0 0 3 0 5 0 0 1)
(0 0 1 8 0 6 4 0 0)
(0 0 8 1 0 2 9 0 0)
(7 0 0 0 0 0 0 0 8)
(0 0 6 7 0 8 2 0 0)
(0 0 2 6 0 9 5 0 0)
(8 0 0 2 0 3 0 0 9)
(0 0 5 0 1 0 3 0 0)))
(define b2
'((3 9 4 0 0 2 6 7 0)
(0 0 0 3 0 0 4 0 0)
(5 0 0 6 9 0 0 2 0)
(0 4 5 0 0 0 9 0 0)
(6 0 0 0 0 0 0 0 7)
(0 0 7 0 0 0 5 8 0)
(0 1 0 0 6 7 0 0 8)
(0 0 9 0 0 8 0 0 0)
(0 2 6 4 0 0 7 3 5)))
(SolveSudoku board)
(SolveSudoku b2)
Output:
SOLUTION:
'(4 8 3 9 2 1 6 5 7)
'(9 6 7 3 4 5 8 2 1)
'(2 5 1 8 7 6 4 9 3)
'(5 4 8 1 3 2 9 7 6)
'(7 2 9 5 6 4 1 3 8)
'(1 3 6 7 9 8 2 4 5)
'(3 7 2 6 8 9 5 1 4)
'(8 1 4 2 5 3 7 6 9)
'(6 9 5 4 1 7 3 8 2)
Solution:
'(3 9 4 8 5 2 6 7 1)
'(2 6 8 3 7 1 4 5 9)
'(5 7 1 6 9 4 8 2 3)
'(1 4 5 7 8 3 9 6 2)
'(6 8 2 9 4 5 3 1 7)
'(9 3 7 1 2 6 5 8 4)
'(4 1 3 5 6 7 2 9 8)
'(7 5 9 2 3 8 1 4 6)
'(8 2 6 4 1 9 7 3 5)