packages feed

exact-cover-0.1.0.0: examples/sudoku/Sudoku/Grid.hs

-- |

module Sudoku.Grid
  (
    -- * Types
    Grid(..)

    -- * Conversion
  , fromListList
  , toListList

    -- * Printing
  , prettyPrintGrid

    -- * Access
  , gridElem

  )
where

import Data.Functor.Identity (Identity(..))
import Data.List (transpose)
import qualified Data.Vector as VB (Vector)
import qualified Data.Vector.Generic as V (fromList, toList)
import Data.Vector.Generic ((!))
import qualified Text.PrettyPrint.Boxes as Box


newtype Grid a = Grid (VB.Vector (VB.Vector a))

instance (Show a) => Show (Grid a) where
  show = runIdentity . prettyPrintGrid (Identity . show)

fromListList :: [[a]] -> Grid a
fromListList = Grid . V.fromList . map V.fromList

toListList :: Grid a -> [[a]]
toListList (Grid vv) = map V.toList . V.toList $ vv

-- | Prints matrix.
prettyPrintGrid :: (Monad m) => (a -> m String) -> Grid a -> m String
prettyPrintGrid printCell (Grid g) = do
  g2 <- mapM (mapM printCell) . transpose . map V.toList $ V.toList g
  let g3 = map (Box.vcat Box.center1 . map Box.text) g2
      colCount = map Box.cols g3
      gStr = Box.render $ Box.hsep 1 Box.center1 $ intersperse3 verticleSep g3
      gStr2 = unlines . intersperse3 (horizontalSep colCount) . lines $ gStr
  pure gStr2
  where
    verticleSep :: Box.Box
    verticleSep = Box.vcat Box.center1 . replicate 9 $ Box.text "|"
    horizontalSep :: [Int] -> String
    horizontalSep (c1:c2:c3:c4:c5:c6:c7:c8:c9:_) =
      replicate (c1+c2+c3+3) '-'
        ++ '+' : replicate (c4+c5+c6+4) '-'
        ++ '+' : replicate (c7+c8+c9+3) '-'
    horizontalSep _ = undefined

    intersperse3 :: a -> [a] -> [a]
    intersperse3 a (x1:x2:x3:xs@(_:_)) = x1:x2:x3:a:(intersperse3 a xs)
    intersperse3 _ xs = xs

data Coordinate = Coordinate !Int !Int

instance Show Coordinate where
  show (Coordinate r c) = '(' : show r ++ ',' : show c ++ ")"

-- | Get the corresponding element of a grid.
gridElem :: Grid a -> Coordinate -> a
gridElem (Grid g) (Coordinate r c) = g ! r ! c