A bit
This commit is contained in:
@ -68,6 +68,9 @@ library
|
|||||||
|
|
||||||
-- Other library packages from which modules are imported.
|
-- Other library packages from which modules are imported.
|
||||||
build-depends: base >=4.19.2.0
|
build-depends: base >=4.19.2.0
|
||||||
|
, matrix
|
||||||
|
, vector
|
||||||
|
, parallel
|
||||||
|
|
||||||
-- Directories containing source files.
|
-- Directories containing source files.
|
||||||
hs-source-dirs: src
|
hs-source-dirs: src
|
||||||
|
|||||||
29
README.md
29
README.md
@ -1,3 +1,30 @@
|
|||||||
# Estinien
|
# Estinien
|
||||||
|
|
||||||
Sudo solver written in haskell
|
Sudoku solver written in haskell
|
||||||
|
|
||||||
|
|
||||||
|
## Format for input files
|
||||||
|
|
||||||
|
```
|
||||||
|
1 2 3 4 5 6 7 8 9
|
||||||
|
2 3 4 5 6 7 8 9 1
|
||||||
|
3 4 5 6 7 8 9 1 2
|
||||||
|
4 5 6 7 8 9 1 2 3
|
||||||
|
5 6 7 8 9 1 2 3 4
|
||||||
|
6 7 8 9 1 2 3 4 5
|
||||||
|
7 8 9 1 2 3 4 5 6
|
||||||
|
8 9 1 2 3 4 5 6 7
|
||||||
|
9 1 2 3 4 5 6 7 8
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
1 2 3 4 5 6 X 8 9
|
||||||
|
2 X 4 5 6 X 8 X 1
|
||||||
|
3 4 5 X 7 8 9 1 2
|
||||||
|
4 5 6 7 8 X 1 2 3
|
||||||
|
5 X X 8 9 1 2 3 4
|
||||||
|
6 7 8 9 X 2 3 X X
|
||||||
|
7 X 9 1 2 3 X X X
|
||||||
|
8 9 1 X 3 4 5 6 X
|
||||||
|
9 1 2 3 4 5 6 7 8
|
||||||
|
```
|
||||||
|
|||||||
10
app/Main.hs
10
app/Main.hs
@ -1,5 +1,11 @@
|
|||||||
|
{-# LANGUAGE LambdaCase #-}
|
||||||
module Main where
|
module Main where
|
||||||
|
|
||||||
|
import Sudoku
|
||||||
|
import System.Environment (getArgs)
|
||||||
|
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = getArgs >>= \case
|
||||||
putStrLn "Hello, Haskell!"
|
[filename] -> readFile filename >>= print . parseStringToGrid
|
||||||
|
_ -> error "Usage: Estinen filename"
|
||||||
|
|||||||
9
invalid.sudoku
Normal file
9
invalid.sudoku
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
1 2 3 4 5 6 X 9 9
|
||||||
|
2 X 4 5 6 X 8 X 1
|
||||||
|
3 4 5 X 7 8 9 1 2
|
||||||
|
4 5 6 7 8 X 1 2 3
|
||||||
|
5 X X 8 9 1 2 3 4
|
||||||
|
6 7 8 9 X 2 3 X X
|
||||||
|
7 X 9 1 2 3 X X X
|
||||||
|
8 9 1 X 3 4 5 6 X
|
||||||
|
9 1 2 3 4 5 6 7 8
|
||||||
132
src/Sudoku.hs
132
src/Sudoku.hs
@ -1 +1,131 @@
|
|||||||
module Sudoku where
|
{-# LANGUAGE OverloadedStrings, LambdaCase, MultiWayIf #-}
|
||||||
|
module Sudoku ( isValid
|
||||||
|
, Cell(..)
|
||||||
|
, Grid(..)
|
||||||
|
, updateCell
|
||||||
|
, emptyGrid
|
||||||
|
, parseStringToGrid
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Matrix hiding (getRow, getCol)
|
||||||
|
import Data.Matrix qualified as M
|
||||||
|
import Data.Vector (Vector)
|
||||||
|
import Data.Vector qualified as V
|
||||||
|
import Data.Coerce
|
||||||
|
import Control.Parallel
|
||||||
|
import Control.Parallel.Strategies
|
||||||
|
|
||||||
|
data Cell where
|
||||||
|
One :: Cell
|
||||||
|
Two :: Cell
|
||||||
|
Three :: Cell
|
||||||
|
Four :: Cell
|
||||||
|
Five :: Cell
|
||||||
|
Six :: Cell
|
||||||
|
Seven :: Cell
|
||||||
|
Eight :: Cell
|
||||||
|
Nine :: Cell
|
||||||
|
Unknown :: Cell
|
||||||
|
deriving (Eq)
|
||||||
|
|
||||||
|
instance Show Cell where
|
||||||
|
show One = "1"
|
||||||
|
show Two = "2"
|
||||||
|
show Three = "3"
|
||||||
|
show Four = "4"
|
||||||
|
show Five = "5"
|
||||||
|
show Six = "6"
|
||||||
|
show Seven = "7"
|
||||||
|
show Eight = "8"
|
||||||
|
show Nine = "9"
|
||||||
|
show Unknown = " "
|
||||||
|
|
||||||
|
cellFromChar :: String -> Cell
|
||||||
|
cellFromChar "1" = One
|
||||||
|
cellFromChar "2" = Two
|
||||||
|
cellFromChar "3" = Three
|
||||||
|
cellFromChar "4" = Four
|
||||||
|
cellFromChar "5" = Five
|
||||||
|
cellFromChar "6" = Six
|
||||||
|
cellFromChar "7" = Seven
|
||||||
|
cellFromChar "8" = Eight
|
||||||
|
cellFromChar "9" = Nine
|
||||||
|
cellFromChar "X" = Unknown
|
||||||
|
cellFromChar _ = error "Unknown char"
|
||||||
|
|
||||||
|
numbers :: Vector Cell
|
||||||
|
numbers = V.fromList [One, Two, Three, Four, Five, Six, Seven, Eight, Nine]
|
||||||
|
|
||||||
|
newtype Column = Column (Vector Cell)
|
||||||
|
newtype Row = Row (Vector Cell)
|
||||||
|
-- Square is 3x3
|
||||||
|
newtype Square = Square (Matrix Cell)
|
||||||
|
-- Grid is 9x9
|
||||||
|
newtype Grid = Grid (Matrix Cell)
|
||||||
|
|
||||||
|
instance Show Grid where
|
||||||
|
show (Grid m) = prettyMatrix m
|
||||||
|
|
||||||
|
emptyGrid :: Grid
|
||||||
|
emptyGrid = coerce $ matrix 9 9 (const Unknown)
|
||||||
|
|
||||||
|
parseStringToGrid :: String -> Grid
|
||||||
|
parseStringToGrid str =
|
||||||
|
let m = M.fromLists . fmap (fmap cellFromChar) . fmap words . lines $ str
|
||||||
|
rows = nrows m
|
||||||
|
cols = nrows m
|
||||||
|
in if
|
||||||
|
| rows == 9 && cols == 9 && isValid (Grid m) -> Grid m
|
||||||
|
| rows == 9 && cols == 9 -> error "Grid was not valid"
|
||||||
|
| otherwise -> error "String did not fit a sudoku grid"
|
||||||
|
|
||||||
|
isValidVector :: Vector Cell -> Bool
|
||||||
|
isValidVector = V.all (<= 1) . (numbers >>=) . (((pure . V.length) .) . (. (==)) . flip V.filter)
|
||||||
|
|
||||||
|
isValidColumn :: Column -> Bool
|
||||||
|
isValidColumn = isValidVector . coerce
|
||||||
|
|
||||||
|
isValidRow :: Row -> Bool
|
||||||
|
isValidRow = isValidVector . coerce
|
||||||
|
|
||||||
|
isValidSquare :: Square -> Bool
|
||||||
|
isValidSquare = isValidVector . getMatrixAsVector . coerce
|
||||||
|
|
||||||
|
getRow :: Int -> Grid -> Row
|
||||||
|
getRow n
|
||||||
|
| n > 0 && n <= 9 = Row . M.getRow n . coerce
|
||||||
|
| otherwise = error "Indexing row outside of grid"
|
||||||
|
|
||||||
|
getColumn :: Int -> Grid -> Column
|
||||||
|
getColumn n
|
||||||
|
| n > 0 && n <= 9 = Column . M.getCol n . coerce
|
||||||
|
| otherwise = error "Indexing column outside of grid"
|
||||||
|
|
||||||
|
getSquare :: Int -> Grid -> Square
|
||||||
|
getSquare n
|
||||||
|
| n > 0 && n <= 9 = let !sr = (n+2) `div` 3
|
||||||
|
!er = sr + 2
|
||||||
|
!sc = (n+2) `mod` 3 + 1
|
||||||
|
!ec = sc + 2
|
||||||
|
in Square . submatrix sr er sc ec . coerce
|
||||||
|
| otherwise = error "Indexing square outside of grid"
|
||||||
|
|
||||||
|
isValid :: Grid -> Bool
|
||||||
|
isValid g = and $ flip (parMap rpar) [1..9]
|
||||||
|
(\i ->
|
||||||
|
let row = isValidRow (getRow i g)
|
||||||
|
col = isValidColumn (getColumn i g)
|
||||||
|
square = isValidSquare (getSquare i g)
|
||||||
|
in col `par` square `par` row && col && square
|
||||||
|
)
|
||||||
|
|
||||||
|
updateCell :: Int -> Int -> Cell -> Grid -> Grid
|
||||||
|
updateCell n m
|
||||||
|
| n > 0 && n <= 9 && m > 0 && m <= 9 = \ c g ->
|
||||||
|
let mx = coerce g in
|
||||||
|
if (mx M.! (n,m) == Unknown) then
|
||||||
|
coerce $ setElem c (n,m) mx else
|
||||||
|
error "Updating non unkown value"
|
||||||
|
| otherwise = error "Updating cell outside of grid"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
9
valid.sudoku
Normal file
9
valid.sudoku
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
1 2 3 4 5 6 X 8 9
|
||||||
|
2 X 4 5 6 X 8 X 1
|
||||||
|
3 4 5 X 7 8 9 1 2
|
||||||
|
4 5 6 7 8 X 1 2 3
|
||||||
|
5 X X 8 9 1 2 3 4
|
||||||
|
6 7 8 9 X 2 3 X X
|
||||||
|
7 X 9 1 2 3 X X X
|
||||||
|
8 9 1 X 3 4 5 6 X
|
||||||
|
9 1 2 3 4 5 6 7 8
|
||||||
Reference in New Issue
Block a user