{-# OPTIONS_GHC -fno-warn-incomplete-uni-patterns #-}
module Problems.Crosswords (Crossword (..), printCrossword, parseCrossword, readCrossword) where
import Data.List (intersperse)
import System.IO
data Crossword = Crossword
{ Crossword -> [String]
word :: [String]
, Crossword -> [[Either Bool Char]]
grid :: [[Either Bool Char]]
}
deriving (Crossword -> Crossword -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Crossword -> Crossword -> Bool
$c/= :: Crossword -> Crossword -> Bool
== :: Crossword -> Crossword -> Bool
$c== :: Crossword -> Crossword -> Bool
Eq, Int -> Crossword -> ShowS
[Crossword] -> ShowS
Crossword -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Crossword] -> ShowS
$cshowList :: [Crossword] -> ShowS
show :: Crossword -> String
$cshow :: Crossword -> String
showsPrec :: Int -> Crossword -> ShowS
$cshowsPrec :: Int -> Crossword -> ShowS
Show)
printCrossword :: Maybe [[Maybe Char]] -> IO ()
printCrossword :: Maybe [[Maybe Char]] -> IO ()
printCrossword Maybe [[Maybe Char]]
Nothing = forall (m :: * -> *) a. Monad m => a -> m a
return ()
printCrossword (Just [[Maybe Char]]
solution) = forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ [Maybe Char] -> IO ()
printRow [[Maybe Char]]
solution
where printRow :: [Maybe Char] -> IO ()
printRow [Maybe Char]
cs = String -> IO ()
putStrLn forall a b. (a -> b) -> a -> b
$ forall a. a -> [a] -> [a]
intersperse Char
' ' forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map Maybe Char -> Char
fromSpot [Maybe Char]
cs
fromSpot :: Maybe Char -> Char
fromSpot Maybe Char
Nothing = Char
'■'
fromSpot (Just Char
c) = Char
c
parseCrossword :: String -> Maybe Crossword
parseCrossword :: String -> Maybe Crossword
parseCrossword String
spec
| forall (t :: * -> *) a. Foldable t => t a -> Bool
null [String]
gridLines' = forall a. Maybe a
Nothing
| Bool
otherwise = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ Crossword { word :: [String]
word = [String]
wordLines, grid :: [[Either Bool Char]]
grid = [String] -> [[Either Bool Char]]
parseGrid [String]
gridLines }
where ([String]
wordLines, [String]
gridLines') = forall a. (a -> Bool) -> [a] -> ([a], [a])
break (String
"" ==) forall a b. (a -> b) -> a -> b
$ String -> [String]
lines String
spec
String
_ : [String]
gridLines = [String]
gridLines'
parseGrid :: [String] -> [[Either Bool Char]]
parseGrid :: [String] -> [[Either Bool Char]]
parseGrid [String]
ls = [[Either Bool Char]] -> [[Either Bool Char]]
padRows forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map String -> [Either Bool Char]
parseLine [String]
ls
where parseLine :: String -> [Either Bool Char]
parseLine = forall a b. (a -> b) -> [a] -> [b]
map Char -> Either Bool Char
parseSpot
parseSpot :: Char -> Either Bool Char
parseSpot Char
' ' = forall a b. a -> Either a b
Left Bool
False
parseSpot Char
'.' = forall a b. a -> Either a b
Left Bool
True
parseSpot Char
c = forall a b. b -> Either a b
Right Char
c
padRows :: [[Either Bool Char]] -> [[Either Bool Char]]
padRows :: [[Either Bool Char]] -> [[Either Bool Char]]
padRows [[Either Bool Char]]
rs = forall a b. (a -> b) -> [a] -> [b]
map forall {b}. [Either Bool b] -> [Either Bool b]
pad [[Either Bool Char]]
rs
where n :: Int
n = forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map forall (t :: * -> *) a. Foldable t => t a -> Int
length [[Either Bool Char]]
rs
pad :: [Either Bool b] -> [Either Bool b]
pad [Either Bool b]
r = [Either Bool b]
r forall a. [a] -> [a] -> [a]
++ forall a. Int -> a -> [a]
replicate (Int
n forall a. Num a => a -> a -> a
- forall (t :: * -> *) a. Foldable t => t a -> Int
length [Either Bool b]
r) (forall a b. a -> Either a b
Left Bool
False)
readCrossword :: FilePath -> IO (Maybe Crossword)
readCrossword :: String -> IO (Maybe Crossword)
readCrossword String
path = do
Handle
h <- String -> IOMode -> IO Handle
openFile String
path IOMode
ReadMode
String
spec <- Handle -> IO String
hGetContents Handle
h
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ String -> Maybe Crossword
parseCrossword String
spec