Add sudoku.lisp

This commit is contained in:
Owen 2024-02-02 09:47:13 -06:00
commit 8d78a1ba6e

134
sudoku.lisp Normal file
View File

@ -0,0 +1,134 @@
(defvar *map* (make-array '(9 9)))
(defun get-row (map x)
"Returns a list of all values present in row x of map"
(let ((row nil))
(loop for i from 0 to 8 do
(setf row (cons (aref map x i) row)))
(remove-if (lambda (e) (= e 0)) row)))
(defun get-col (map y)
"Returns a list of all values in column y of map"
(let ((col nil))
(loop for i from 0 to 8 do
(setf col (cons (aref map i y) col)))
(remove-if (lambda (e) (= e 0)) col)))
(defun get-zone (map x y)
"Returns a list of all values for the zone which contains cell x,y."
(let ((zone nil)
(zx (* 3 (floor (/ x 3))))
(zy (* 3 (floor (/ y 3)))))
(loop for i from 0 to 2 do
(loop for j from 0 to 2 do
(setf zone (cons (aref map (+ zx i) (+ zy j)) zone))))
(remove-if (lambda (e) (= e 0)) zone)))
(defun get-valid-for-pos (map x y)
"Returns the list of presently-valid remaining values for cell x,y in map"
(if (= 0 (aref map x y))
(let ((row (get-row map x))
(col (get-col map y))
(zone (get-zone map x y))
(valid '(1 2 3 4 5 6 7 8 9)))
(setf valid (set-difference valid row))
(setf valid (set-difference valid col))
(set-difference valid zone))))
(defun get-min-move-count (map)
"Returns the cell coordinates in list form '(x y) for the cell with the lowest number of presently-valid remaining values. It also returns the count of this quantity"
(let ((min-move-count 10)
(min-move-coords nil))
(loop for i from 0 to 8 do
(loop for j from 0 to 8 do
(let ((move-count (length (aref map i j))))
(if (and (not (= move-count 0)) (< move-count min-move-count))
(progn
(setf min-move-count move-count)
(setf min-move-coords (list i j)))))))
(values min-move-coords min-move-count)))
(defun build-move-map (map)
"Returns an array where each cell contains a list of values which are presently-valid for map"
(let ((move-map (make-array '(9 9))))
(loop for i from 0 to 8 do
(loop for j from 0 to 8 do
(setf (aref move-map i j) (get-valid-for-pos map i j))))
move-map))
(defun map-complete (map)
"Checks whether all cells are filled with a non-zero value in map"
(loop for i from 0 to 8 do
(loop for j from 0 to 8 do
(if (= 0 (aref map i j))
(return-from map-complete nil))))
t)
(defun copy-array (arr)
"Creates a value copy of an array, assumes 9 x 9 dimensions"
(let ((arr2 (make-array '(9 9))))
(loop for i from 0 to 8 do
(loop for j from 0 to 8 do
(setf (aref arr2 i j) (aref arr i j))))
arr2))
(defun solve-map-r (map)
"Resursive solver for a sudoku puzzle map, saves result to global *map*"
(let ((move-map (build-move-map map))
(mapc (copy-array map))
(min-move nil))
(setf min-move (get-min-move-count move-map))
(if (equal nil min-move)
(if (map-complete mapc)
(progn
(setf *map* mapc)
(return-from solve-map-r t))
(return-from solve-map-r nil)))
(loop for val in (aref move-map (first min-move) (second min-move)) do
(setf (aref mapc (nth 0 min-move) (nth 1 min-move)) val)
(if (solve-map-r mapc)
(return-from solve-map-r t))))
nil)
(defun solve-map (map)
"Wrapper function for recursive solver. Returns value *map* saved by solver"
(if (solve-map-r map)
*map*
nil))
(defun get-file (filename)
(with-open-file (stream filename)
(loop for line = (read-line stream nil)
while line
collect line)))
(defun build-map (strlst)
"Builds a map array from a list of strings. Expects 9 strings of 9 characters each"
(let ((map (make-array '(9 9)))
(row 0))
(loop for str in strlst do
(loop for i from 0 to 8 do
(setf (aref map row i) (digit-char-p (aref str i))))
(setf row (1+ row)))
map))
(defun build-map-list (strlst)
"Builds a list of map arrays from a list of strings"
(let ((lineno 1)
(maplist nil))
(loop while (< lineno (length strlst)) do
(push (build-map (subseq strlst lineno (+ 9 lineno))) maplist)
(setf lineno (+ lineno 10)))
maplist))
(defun solve ()
(let ((maplist (build-map-list (get-file "p096_sudoku.txt")))
(sum 0))
(loop for map in maplist do
(setf map (solve-map map))
(if (not (equal nil map))
(setf sum (+ sum
(* 100 (aref map 0 0))
(* 10 (aref map 0 1))
(aref map 0 2)))))
sum))