Created
October 20, 2012 01:56
-
-
Save pgrm/3921679 to your computer and use it in GitHub Desktop.
Haskell Sudoku Solver
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Data.List | |
--2) | |
type Row = [Integer] | |
type Sudoku = [Row] | |
data Variant = Basic | Cross | Color deriving (Eq,Show) | |
isRowValid :: Row -> Bool | |
isRowValid [] = True | |
isRowValid (r:rs) = (r == 0 || (notElem r rs)) && isRowValid rs | |
getSmallField :: Sudoku -> (Int, Int) -> Row | |
getSmallField sudoku (x, y) = [(sudoku!!(i + (y * 3))!!(j + (x * 3))) | i <- [0 .. 2], j <- [0 .. 2]] | |
getColorField :: Sudoku -> Int -> Row | |
getColorField sudoku index = [(sudoku!!j)!!i | i <- [mod index 3, (mod index 3) + 3 .. 8], j <- [div index 3, (div index 3) + 3 .. 8]] | |
isValid :: Sudoku -> Variant -> Bool | |
isValid sudoku Basic = | |
let | |
transposedSudoku = transpose sudoku | |
in | |
and [(isRowValid (sudoku!!i)) && (isRowValid (transposedSudoku!!i)) | i <- [0 .. 8]] && | |
and [(isRowValid (getSmallField sudoku (x, y))) | x <- [0 .. 2], y <- [0 .. 2]] | |
isValid sudoku Cross = | |
(isValid sudoku Basic) && | |
(isRowValid [(sudoku!!i)!!i | i <- [0 .. 8]]) && | |
(isRowValid [(sudoku!!(8 - i))!!i | i <- [0 .. 8]]) | |
isValid sudoku Color = | |
(isValid sudoku Basic) && | |
and [(isRowValid (getColorField sudoku i)) | i <- [0 .. 8]] | |
getEmptyCells :: Row -> (Int, Int) -> [(Int, Int)] | |
getEmptyCells (0:xs) (x,y) = ((x,y):(getEmptyCells xs ((x + 1), y))) | |
getEmptyCells (_:xs) (x,y) = getEmptyCells xs ((x + 1), y) | |
getEmptyCells _ _= [] | |
getEmptyRows :: Sudoku -> Int -> [[(Int, Int)]] | |
getEmptyRows (x:xs) i = (getEmptyCells x (0, i)) : (getEmptyRows xs (i + 1)) | |
getEmptyRows _ _ = [] | |
getAllEmptyPlaces :: Sudoku -> [(Int, Int)] | |
getAllEmptyPlaces sudoku = concat (getEmptyRows sudoku 0) | |
checkSimpleIfValid :: Sudoku -> Variant -> (Int, Int) -> Integer -> Bool | |
checkSimpleIfValid sudoku variant xy val = | |
checkIfValid sudoku (transpose sudoku) variant xy val | |
checkIfValid :: Sudoku -> Sudoku -> Variant -> (Int, Int) -> Integer -> Bool | |
checkIfValid sudoku transposedSudoku Basic (x,y) val = | |
(notElem val (sudoku!!y)) && | |
(notElem val (transposedSudoku!!x)) && | |
(notElem val (getSmallField sudoku ((div x 3), (div y 3)))) | |
checkIfValid sudoku transposedSudoku Color (x,y) val = | |
(checkIfValid sudoku transposedSudoku Basic (x,y) val) && | |
(notElem val (getColorField sudoku (((mod x 3) * 3) + (mod y 3)))) | |
checkIfValid sudoku transposedSudoku Cross (x,y) val = | |
(checkIfValid sudoku transposedSudoku Basic (x,y) val) && | |
((x /= y) || (notElem val [(sudoku!!i)!!i | i <- [0 .. 8]])) && | |
(((x + y) /= 8) || (notElem val [(sudoku!!(8 - i))!!i | i <- [0 .. 8]])) | |
getPossibleValues :: Sudoku -> Sudoku -> Variant -> (Int, Int) -> [Integer] | |
getPossibleValues sudoku transposedSudoku variant xy = | |
[i | i <- [1 .. 9], (checkIfValid sudoku transposedSudoku variant xy i)] | |
maskValues :: [(Int,Int,[Integer])] -> [(Int,Int,[Integer])] | |
maskValues ((x,y,[v]):vals) = ((x,y,[v]):(maskValues [(vx,vy, (if (vs == [v]) then ([0,v]) else vs)) | (vx,vy,vs) <- vals])) | |
maskValues (v:vs) = (v:(maskValues vs)) | |
maskValues [] = [] | |
getAllPossibleValues :: Sudoku -> Variant -> [(Int, Int)] -> [(Int, Int, [Integer])] | |
getAllPossibleValues sudoku variant freeCells = | |
let transposedSudoku = transpose sudoku | |
in [(x, y, (getPossibleValues sudoku transposedSudoku variant (x,y))) | (x,y) <- freeCells] | |
updateRow :: Row -> [(Int, Int, [Integer])] -> (Int, Int) -> Row | |
updateRow (r:rs) ((vx, vy, [v]):values) (x,y) | |
| (vx == x && vy == y) = (v:(updateRow rs values ((x+1), y))) | |
| (vy > y) = (r:rs) | |
| otherwise = (r:(updateRow rs ((vx, vy, [v]):values) ((x+1), y))) | |
updateRow (r:rs) ((vx, vy, vals):values) (x,y) | |
| (vy > y) = (r:rs) | |
| otherwise = (r:(updateRow rs values ((x+1), y))) | |
updateRow row _ _ = row | |
updateSudoku :: Sudoku -> [(Int, Int, [Integer])] -> Int -> Sudoku | |
updateSudoku (r:rows) ((vx, vy, vals):values) y | |
| (vy < y) = updateSudoku (r:rows) values y | |
| (vy == y) = | |
let row = (updateRow r ((vx, vy, vals):values) (0,y)) | |
in (row:(updateSudoku rows values (y+1))) | |
| otherwise = (r:(updateSudoku rows ((vx, vy, vals):values) (y+1))) | |
updateSudoku rows _ _ = rows | |
simpleSolvePossible :: [(Int, Int, [Integer])] -> Bool | |
simpleSolvePossible values = or [(length vals) == 1 | (_, _, vals) <- values] | |
eachPlaceFillable :: [(Int, Int, [Integer])] -> Bool | |
eachPlaceFillable values = and [vals /= [] | (_,_,vals) <- values] | |
findHiddenValue :: Sudoku -> Variant -> [(Int, Int, [Integer])] -> [(Int, Int, [Integer])] | |
findHiddenValue sudoku Basic allValues = | |
[(x, y, [v]) | (x, y, vs) <- allValues, v <- vs, | |
(notElem v [v2 | (x2,y2,vs2) <- allValues, v2 <- vs2, x /= x2 && y == y2]) || | |
(notElem v [v2 | (x2,y2,vs2) <- allValues, v2 <- vs2, x == x2 && y /= y2]) || | |
(notElem v [v2 | (x2,y2,vs2) <- allValues, v2 <- vs2, (x /= x2 || y /= y2) && | |
(((div x 3) == (div x2 3)) && ((div y 3) == (div y2 3)))])] | |
findHiddenValue sudoku Color allValues = | |
[(x, y, [v]) | (x, y, vs) <- allValues, v <- vs, | |
(notElem v [v2 | (x2,y2,vs2) <- allValues, v2 <- vs2, x /= x2 && y == y2]) || | |
(notElem v [v2 | (x2,y2,vs2) <- allValues, v2 <- vs2, x == x2 && y /= y2]) || | |
(notElem v [v2 | (x2,y2,vs2) <- allValues, v2 <- vs2, (x /= x2 || y /= y2) && | |
(((div x 3) == (div x2 3)) && ((div y 3) == (div y2 3)))]) || | |
(notElem v [v2 | (x2,y2,vs2) <- allValues, v2 <- vs2, (x /= x2 || y /= y2) && | |
(((mod x 3) == (mod x2 3)) && ((mod y 3) == (mod y2 3)))])] | |
findHiddenValue sudoku Cross allValues = | |
[(x, y, [v]) | (x, y, vs) <- allValues, v <- vs, | |
(notElem v [v2 | (x2,y2,vs2) <- allValues, v2 <- vs2, x /= x2 && y == y2]) || | |
(notElem v [v2 | (x2,y2,vs2) <- allValues, v2 <- vs2, x == x2 && y /= y2]) || | |
(notElem v [v2 | (x2,y2,vs2) <- allValues, v2 <- vs2, (x /= x2 || y /= y2) && | |
(((div x 3) == (div x2 3)) && ((div y 3) == (div y2 3)))]) || | |
((x /= y) || | |
(notElem v [v2 | (x2,y2,vs2) <- allValues, v2 <- vs2, (x /= x2 && x2 == y2)])) || | |
(((x + y) /= 8) || | |
(notElem v [v2 | (x2,y2,vs2) <- allValues, v2 <- vs2, (x /= x2 && (x2 + y2) == 8)]))] | |
startGuessing :: Sudoku -> Variant -> (Int, Int, [Integer]) -> Maybe Sudoku | |
startGuessing sudoku variant (x,y,(v:vs)) = | |
let | |
sudokuNew = updateSudoku sudoku [(x,y,[v])] 0 | |
sudokuNew2 = trySolve sudokuNew variant (getAllEmptyPlaces sudokuNew) | |
in | |
case sudokuNew2 of | |
Nothing -> startGuessing sudoku variant (x,y,vs) | |
_ -> sudokuNew2 | |
startGuessing _ _ _ = Nothing | |
getGessingPossibleValues :: [(Int, Int, [Integer])] -> (Int, Int, [Integer]) -> (Int, Int, [Integer]) | |
getGessingPossibleValues [] val = val | |
getGessingPossibleValues ((x,y,v):vals) (_,_,[]) | |
| ((length v) == 2) = (x,y,v) | |
| otherwise = getGessingPossibleValues vals (x,y,v) | |
getGessingPossibleValues ((x,y,v):vals) (xl, yl, vl) | |
| ((length v) == 2) = (x,y,v) | |
| ((length v) < (length vl)) = getGessingPossibleValues vals (x,y,v) | |
| otherwise = getGessingPossibleValues vals (xl,yl,vl) | |
trySolve :: Sudoku -> Variant -> [(Int, Int)] -> Maybe Sudoku | |
trySolve sudoku variant [] | |
| (isValid sudoku variant) = (Just sudoku) | |
| otherwise = case getAllEmptyPlaces sudoku of | |
[] -> (Just sudoku) | |
_ -> Nothing | |
trySolve sudoku variant emptyPlaces = | |
let possibleVals = getAllPossibleValues sudoku variant emptyPlaces | |
in | |
if (eachPlaceFillable possibleVals) then ( | |
if (simpleSolvePossible possibleVals) then ( | |
let sudokuNew = updateSudoku sudoku possibleVals 0 | |
in trySolve sudokuNew variant (getAllEmptyPlaces sudokuNew) | |
) else ( | |
let | |
vals = findHiddenValue sudoku variant possibleVals | |
in | |
case vals of | |
[] -> ( | |
let | |
y = getGessingPossibleValues possibleVals (-1,-1,[]) | |
sudokuNew = startGuessing sudoku variant y | |
in | |
case sudokuNew of | |
Nothing -> Nothing | |
(Just s) -> (if (isValid s variant) then sudokuNew else Nothing) | |
) | |
_ -> ( | |
let sudokuNew = updateSudoku sudoku vals 0 | |
in trySolve sudokuNew variant (getAllEmptyPlaces sudokuNew) | |
) | |
) | |
) else (Nothing) | |
solve :: Sudoku -> Variant -> Maybe Sudoku | |
solve sudoku variant | |
| isValid sudoku variant = trySolve sudoku variant (getAllEmptyPlaces sudoku) | |
| otherwise = Nothing | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment