packages feed

puzzle-draw 0.1.0.4 → 0.2.0.0

raw patch · 39 files changed

+3302/−974 lines, 39 filesdep +hspecdep +lineardep +processdep ~basedep ~diagrams-cairodep ~diagrams-libnew-component:exe:checkpuzzlenew-component:exe:test-comparePVP ok

version bump matches the API change (PVP)

Dependencies added: hspec, linear, process, tasty-golden, tasty-hspec

Dependency ranges changed: base, diagrams-cairo, diagrams-lib, diagrams-svg, optparse-applicative

API changes (from Hackage documentation)

- Data.Puzzles.Compose: drawExample' :: Backend' b => PuzzleHandler b ((Value, Maybe Value) -> Parser (Diagram b R2))
- Data.Puzzles.Compose: drawPuzzle :: PuzzleHandler b (Value -> Parser (Diagram b R2))
- Data.Puzzles.Compose: drawPuzzle' :: PuzzleHandler b ((Value, Maybe Value) -> Parser (Diagram b R2))
- Data.Puzzles.Compose: drawPuzzleSol :: PuzzleHandler b ((Value, Value) -> Parser (Diagram b R2, Diagram b R2))
- Data.Puzzles.Compose: drawSolution' :: PuzzleHandler b ((Value, Maybe Value) -> Parser (Diagram b R2))
- Data.Puzzles.Elements: instance Eq MasyuPearl
- Data.Puzzles.Elements: instance Show CompassC
- Data.Puzzles.Elements: instance Show KropkiDot
- Data.Puzzles.Elements: instance Show MasyuPearl
- Data.Puzzles.Elements: instance Show Shade
- Data.Puzzles.Elements: instance Show SlalomDiag
- Data.Puzzles.Elements: instance Show TapaClue
- Data.Puzzles.Elements: instance Show a => Show (Tightfit a)
- Data.Puzzles.Elements: mwend :: MarkedWord -> Coord
- Data.Puzzles.Elements: mwstart :: MarkedWord -> Coord
- Data.Puzzles.Grid: (!) :: (GridShape s, Ord (Cell s)) => Grid s a -> Cell s -> a
- Data.Puzzles.Grid: Grid :: s -> Map (Cell s) a -> Grid s a
- Data.Puzzles.Grid: OClue :: (Int, Int) -> (Int, Int) -> a -> OutsideClue a
- Data.Puzzles.Grid: bottom :: OutsideClues a -> [a]
- Data.Puzzles.Grid: cells :: GridShape s => Grid s a -> [Cell s]
- Data.Puzzles.Grid: contents :: Grid s a -> Map (Cell s) a
- Data.Puzzles.Grid: data Grid s a
- Data.Puzzles.Grid: data OutsideClue a
- Data.Puzzles.Grid: dualEdgesP :: (a -> a -> Bool) -> Grid Square a -> [Edge]
- Data.Puzzles.Grid: edgesP :: (a -> a -> Bool) -> Grid Square a -> [Edge]
- Data.Puzzles.Grid: filterG :: (a -> Bool) -> Grid s a -> Grid s a
- Data.Puzzles.Grid: fromListList :: [[a]] -> Grid Square a
- Data.Puzzles.Grid: inBounds :: (GridShape s, Eq (Cell s)) => Grid s a -> Cell s -> Bool
- Data.Puzzles.Grid: instance (Eq s, Eq (Cell s), Eq a) => Eq (Grid s a)
- Data.Puzzles.Grid: instance (Show a, Show s, GridShape s) => Show (Grid s a)
- Data.Puzzles.Grid: instance Eq a => Eq (OutsideClues a)
- Data.Puzzles.Grid: instance Foldable (Grid s)
- Data.Puzzles.Grid: instance Functor (Grid s)
- Data.Puzzles.Grid: instance Functor OutsideClue
- Data.Puzzles.Grid: instance Functor OutsideClues
- Data.Puzzles.Grid: instance Show a => Show (OutsideClues a)
- Data.Puzzles.Grid: instance Traversable (Grid s)
- Data.Puzzles.Grid: left :: OutsideClues a -> [a]
- Data.Puzzles.Grid: ocBase :: OutsideClue a -> (Int, Int)
- Data.Puzzles.Grid: ocDir :: OutsideClue a -> (Int, Int)
- Data.Puzzles.Grid: ocValue :: OutsideClue a -> a
- Data.Puzzles.Grid: outsideClueList :: OutsideClues a -> [OutsideClue a]
- Data.Puzzles.Grid: right :: OutsideClues a -> [a]
- Data.Puzzles.Grid: shape :: Grid s a -> s
- Data.Puzzles.Grid: top :: OutsideClues a -> [a]
- Data.Puzzles.Grid: type CharClueGrid = SGrid (Maybe Char)
- Data.Puzzles.Grid: type CharGrid = SGrid Char
- Data.Puzzles.Grid: type IntGrid = SGrid (Clue Int)
- Data.Puzzles.Grid: type SGrid = Grid Square
- Data.Puzzles.Grid: values :: GridShape s => Grid s a -> [(Cell s, a)]
- Data.Puzzles.GridShape: H :: Dir
- Data.Puzzles.GridShape: V :: Dir
- Data.Puzzles.GridShape: cells :: GridShape a => a -> [Cell a]
- Data.Puzzles.GridShape: class Show (Cell a) => GridShape a where type family GridSize a :: * type family Cell a :: * type family Vertex a :: *
- Data.Puzzles.GridShape: instance Eq Dir
- Data.Puzzles.GridShape: instance Eq Dir'
- Data.Puzzles.GridShape: instance Eq Edge
- Data.Puzzles.GridShape: instance Eq Square
- Data.Puzzles.GridShape: instance Eq a => Eq (Edge' a)
- Data.Puzzles.GridShape: instance GridShape Square
- Data.Puzzles.GridShape: instance Ord Dir
- Data.Puzzles.GridShape: instance Ord Dir'
- Data.Puzzles.GridShape: instance Ord Edge
- Data.Puzzles.GridShape: instance Ord a => Ord (Edge' a)
- Data.Puzzles.GridShape: instance Show Dir
- Data.Puzzles.GridShape: instance Show Dir'
- Data.Puzzles.GridShape: instance Show Edge
- Data.Puzzles.GridShape: instance Show Square
- Data.Puzzles.GridShape: instance Show a => Show (Edge' a)
- Data.Puzzles.GridShape: orientedEdge :: Cell Square -> Cell Square -> Edge' (Vertex Square)
- Data.Puzzles.GridShape: size :: GridShape a => a -> GridSize a
- Data.Puzzles.GridShape: squareNeighbours :: [(Int, Int)] -> Square -> Cell Square -> [Cell Square]
- Data.Puzzles.GridShape: vertices :: GridShape a => a -> [Vertex a]
- Data.Puzzles.PuzzleTypes: instance Eq PuzzleType
- Data.Puzzles.PuzzleTypes: instance Show PuzzleType
- Data.Puzzles.Pyramid: dotsk :: KropkiRow -> [KropkiDot]
- Data.Puzzles.Pyramid: entries :: Row -> [Maybe Int]
- Data.Puzzles.Pyramid: entriesk :: KropkiRow -> [Maybe Int]
- Data.Puzzles.Pyramid: instance FromJSON Pyramid
- Data.Puzzles.Pyramid: instance FromJSON PyramidSol
- Data.Puzzles.Pyramid: instance FromJSON RowKropkiPyramid
- Data.Puzzles.Pyramid: instance Show KropkiRow
- Data.Puzzles.Pyramid: instance Show Pyramid
- Data.Puzzles.Pyramid: instance Show PyramidSol
- Data.Puzzles.Pyramid: instance Show Row
- Data.Puzzles.Pyramid: instance Show RowKropkiPyramid
- Data.Puzzles.Pyramid: shaded :: Row -> Bool
- Data.Puzzles.Pyramid: shadedk :: KropkiRow -> Bool
- Data.Puzzles.Pyramid: unKP :: RowKropkiPyramid -> [KropkiRow]
- Data.Puzzles.Pyramid: unPyr :: Pyramid -> [Row]
- Diagrams.Puzzles.CmdLine: _file :: RenderOpts -> FilePath
- Diagrams.Puzzles.CmdLine: _w :: RenderOpts -> Double
- Diagrams.Puzzles.CmdLine: checkType :: Maybe String -> IO PuzzleType
- Diagrams.Puzzles.CmdLine: exitErr :: String -> IO a
- Diagrams.Puzzles.CmdLine: readPuzzle :: FilePath -> IO (Either ParseException TypedPuzzle)
- Diagrams.Puzzles.Draw: instance Show OutputChoice
- Diagrams.Puzzles.Elements: drawShade :: Backend' b => Shade -> Diagram b R2
- Diagrams.Puzzles.Grid: atCentres :: (Transformable a, Monoid a, V a ~ R2) => (t -> a) -> [(Coord, t)] -> a
- Diagrams.Puzzles.Grid: atCentres' :: (Transformable a, V a ~ R2) => SGrid a -> [a]
- Diagrams.Puzzles.Grid: atVertices :: (Transformable a, Monoid a, V a ~ R2) => (t -> a) -> [(Coord, t)] -> a
- Diagrams.Puzzles.Grid: atVertices' :: (Transformable a, V a ~ R2) => SGrid a -> [a]
- Diagrams.Puzzles.Grid: dashedgrid :: Backend' b => Size -> Diagram b R2
- Diagrams.Puzzles.Grid: drawAreaGrid :: (Backend' b, Eq a) => SGrid a -> Diagram b R2
- Diagrams.Puzzles.Grid: drawAreaGridGray :: Backend' b => SGrid Char -> Diagram b R2
- Diagrams.Puzzles.Grid: drawDualEdges :: Backend' b => [Edge] -> Diagram b R2
- Diagrams.Puzzles.Grid: drawShadedGrid :: Backend' b => SGrid Bool -> Diagram b R2
- Diagrams.Puzzles.Grid: drawThinDualEdges :: Backend' b => [Edge] -> Diagram b R2
- Diagrams.Puzzles.Grid: dualEdge :: Edge -> Path R2
- Diagrams.Puzzles.Grid: edgePath :: Edge' (Vertex Square) -> Path R2
- Diagrams.Puzzles.Grid: fence :: [Double] -> Double -> Path R2
- Diagrams.Puzzles.Grid: fullgridlines :: Size -> Path R2
- Diagrams.Puzzles.Grid: grid' :: Backend' b => (Diagram b R2 -> Diagram b R2) -> Size -> Diagram b R2
- Diagrams.Puzzles.Grid: gridlines :: Size -> Path R2
- Diagrams.Puzzles.Grid: irregAreaGridX :: Backend' b => SGrid Char -> Diagram b R2
- Diagrams.Puzzles.Grid: irregularGrid :: Backend' b => SGrid a -> Diagram b R2
- Diagrams.Puzzles.Grid: outframe :: Backend' b => Size -> Diagram b R2
- Diagrams.Puzzles.Grid: outframe' :: Backend' b => Double -> Size -> Diagram b R2
- Diagrams.Puzzles.Grid: outsideCells :: (Transformable c, Monoid c, V c ~ R2) => Double -> [OutsideClue [c]] -> c
- Diagrams.Puzzles.Grid: outsideGen :: (Transformable c, Monoid c, V c ~ R2) => (OutsideClue [c] -> R2) -> Double -> [OutsideClue [c]] -> c
- Diagrams.Puzzles.Grid: outsideVertices :: (Transformable c, Monoid c, V c ~ R2) => Double -> [OutsideClue [c]] -> c
- Diagrams.Puzzles.Grid: plaingrid :: Backend' b => Size -> Diagram b R2
- Diagrams.Puzzles.Grid: slithergrid :: Backend' b => Size -> Diagram b R2
- Diagrams.Puzzles.PuzzleGrids: drawClueGrid :: Backend' b => SGrid (Clue Char) -> Diagram b R2
- Diagrams.Puzzles.PuzzleGrids: drawCompassClues :: Backend' b => SGrid CompassClue -> Diagram b R2
- Diagrams.Puzzles.PuzzleGrids: drawCompassGrid :: Backend' b => SGrid CompassClue -> Diagram b R2
- Diagrams.Puzzles.PuzzleGrids: drawCrosses :: Backend' b => SGrid Bool -> Diagram b R2
- Diagrams.Puzzles.PuzzleGrids: drawIntClues :: Backend' b => SGrid (Clue Int) -> Diagram b R2
- Diagrams.Puzzles.PuzzleGrids: drawInts :: Backend' b => SGrid Int -> Diagram b R2
- Diagrams.Puzzles.PuzzleGrids: drawMasyuGrid :: Backend' b => SGrid MasyuClue -> Diagram b R2
- Diagrams.Puzzles.PuzzleGrids: drawSlalomDiags :: Backend' b => SGrid SlalomDiag -> Diagram b R2
- Diagrams.Puzzles.PuzzleGrids: drawSlalomGrid :: Backend' b => SGrid (Clue Int) -> Diagram b R2
- Diagrams.Puzzles.PuzzleGrids: outsideGrid :: Backend' b => OutsideClues [String] -> Diagram b R2
- Diagrams.Puzzles.Pyramid: kropki :: Backend' b => KropkiDot -> Diagram b R2
- Text.Puzzles.Puzzle: instance FromJSON TypedPuzzle
- Text.Puzzles.Puzzle: instance Show TypedPuzzle
- Text.Puzzles.PuzzleTypes: instance FromJSON Curve
- Text.Puzzles.PuzzleTypes: instance FromJSON GridMarked
- Text.Puzzles.PuzzleTypes: instance FromJSON GridWords
- Text.Puzzles.PuzzleTypes: instance FromJSON LSol
- Text.Puzzles.Util: PlainNode :: PlainNode
- Text.Puzzles.Util: Ref :: Char -> Ref
- Text.Puzzles.Util: data PlainNode
- Text.Puzzles.Util: data Ref
- Text.Puzzles.Util: failChar :: Char -> String -> Parser a
- Text.Puzzles.Util: instance (FromChar a, FromChar b) => FromChar (Either a b)
- Text.Puzzles.Util: instance (FromChar a, FromChar b) => FromChar (Either' a b)
- Text.Puzzles.Util: instance (FromChar a, FromChar b) => FromJSON (BorderedRect a b)
- Text.Puzzles.Util: instance (FromJSON a, FromJSON b) => FromJSON (Either' a b)
- Text.Puzzles.Util: instance (FromString a, FromString b) => FromString (Either a b)
- Text.Puzzles.Util: instance (Show a, Show b) => Show (BorderedRect a b)
- Text.Puzzles.Util: instance Eq Alpha
- Text.Puzzles.Util: instance Foldable Border
- Text.Puzzles.Util: instance FromChar Alpha
- Text.Puzzles.Util: instance FromChar Black
- Text.Puzzles.Util: instance FromChar Blank
- Text.Puzzles.Util: instance FromChar Blank'
- Text.Puzzles.Util: instance FromChar Char
- Text.Puzzles.Util: instance FromChar Crossing
- Text.Puzzles.Util: instance FromChar Empty
- Text.Puzzles.Util: instance FromChar HalfDirs
- Text.Puzzles.Util: instance FromChar Int
- Text.Puzzles.Util: instance FromChar MasyuPearl
- Text.Puzzles.Util: instance FromChar PlainNode
- Text.Puzzles.Util: instance FromChar PrimeDiag
- Text.Puzzles.Util: instance FromChar Ref
- Text.Puzzles.Util: instance FromChar Shaded
- Text.Puzzles.Util: instance FromChar SlalomDiag
- Text.Puzzles.Util: instance FromChar Space
- Text.Puzzles.Util: instance FromChar Tight
- Text.Puzzles.Util: instance FromChar a => FromChar (Maybe a)
- Text.Puzzles.Util: instance FromChar a => FromJSON (Rect a)
- Text.Puzzles.Util: instance FromChar a => FromString (Tightfit a)
- Text.Puzzles.Util: instance FromJSON Blank
- Text.Puzzles.Util: instance FromJSON Blank'
- Text.Puzzles.Util: instance FromJSON IntString
- Text.Puzzles.Util: instance FromJSON PCompassC
- Text.Puzzles.Util: instance FromJSON PMarkedWord
- Text.Puzzles.Util: instance FromJSON ParseTapaClue
- Text.Puzzles.Util: instance FromJSON a => FromJSON (RefGrid a)
- Text.Puzzles.Util: instance FromString Blank
- Text.Puzzles.Util: instance FromString Int
- Text.Puzzles.Util: instance FromString a => FromJSON (SpacedRect a)
- Text.Puzzles.Util: instance Functor Border
- Text.Puzzles.Util: instance Functor Rect
- Text.Puzzles.Util: instance Ord Alpha
- Text.Puzzles.Util: instance Show Alpha
- Text.Puzzles.Util: instance Show Ref
- Text.Puzzles.Util: instance Show a => Show (Border a)
- Text.Puzzles.Util: instance Show a => Show (Rect a)
- Text.Puzzles.Util: instance Traversable Border
- Text.Puzzles.Util: parseTightIntGrid :: Value -> Parser (SGrid (Tightfit Int))
- Text.Puzzles.Util: rectToClueGrid :: Rect (Either Blank a) -> SGrid (Clue a)
- Text.Puzzles.Util: rectToClueGrid' :: Rect (Either Blank' a) -> SGrid (Clue a)
- Text.Puzzles.Util: rectToSGrid :: Rect a -> SGrid a
- Text.Puzzles.Util: unAlpha :: Alpha -> Char
- Text.Puzzles.Util: unEither' :: Either' a b -> Either a b
- Text.Puzzles.Util: unHalfDirs :: HalfDirs -> [Dir]
- Text.Puzzles.Util: unIntString :: IntString -> String
- Text.Puzzles.Util: unPCC :: PCompassC -> CompassC
- Text.Puzzles.Util: unPMW :: PMarkedWord -> MarkedWord
- Text.Puzzles.Util: unParseTapaClue :: ParseTapaClue -> TapaClue
- Text.Puzzles.Util: unRG :: RefGrid a -> SGrid a
- Text.Puzzles.Util: unRef :: Ref -> Char
- Text.Puzzles.Util: unShaded :: Shaded -> Bool
- Text.Puzzles.Util: unSpaced :: SpacedRect a -> Rect a
- Text.Puzzles.Util: unTight :: Tight -> Tightfit ()
+ Data.Puzzles.CmdLine: checkType :: Maybe String -> IO PuzzleType
+ Data.Puzzles.CmdLine: exitErr :: String -> IO a
+ Data.Puzzles.CmdLine: readPuzzle :: FilePath -> IO (Either ParseException TypedPuzzle)
+ Data.Puzzles.Code: Cols :: [Int] -> CodePart
+ Data.Puzzles.Code: ColsN :: [Int] -> CodePart
+ Data.Puzzles.Code: LabelsN :: (Grid N (Maybe Char)) -> CodePart
+ Data.Puzzles.Code: Rows' :: [Int] -> CodePart
+ Data.Puzzles.Code: RowsN' :: [Int] -> CodePart
+ Data.Puzzles.Code: data CodePart
+ Data.Puzzles.Code: type Code = [CodePart]
+ Data.Puzzles.Elements: DigitRange :: !Int -> !Int -> DigitRange
+ Data.Puzzles.Elements: FComp :: String -> String -> String -> Fraction
+ Data.Puzzles.Elements: FFrac :: String -> String -> Fraction
+ Data.Puzzles.Elements: FInt :: String -> Fraction
+ Data.Puzzles.Elements: Fish :: Fish
+ Data.Puzzles.Elements: GalaxyCentre :: GalaxyCentre
+ Data.Puzzles.Elements: MEnd :: MEnd
+ Data.Puzzles.Elements: MarkedLine :: a -> a -> MarkedLine a
+ Data.Puzzles.Elements: PlainNode :: PlainNode
+ Data.Puzzles.Elements: REqual :: Relation
+ Data.Puzzles.Elements: RGreater :: Relation
+ Data.Puzzles.Elements: RLess :: Relation
+ Data.Puzzles.Elements: RUndetermined :: Relation
+ Data.Puzzles.Elements: SlovakClue :: !Int -> !Int -> SlovakClue
+ Data.Puzzles.Elements: Star :: Star
+ Data.Puzzles.Elements: [mwend] :: MarkedWord -> Coord
+ Data.Puzzles.Elements: [mwstart] :: MarkedWord -> Coord
+ Data.Puzzles.Elements: data DigitRange
+ Data.Puzzles.Elements: data Fish
+ Data.Puzzles.Elements: data Fraction
+ Data.Puzzles.Elements: data GalaxyCentre
+ Data.Puzzles.Elements: data MEnd
+ Data.Puzzles.Elements: data MarkedLine a
+ Data.Puzzles.Elements: data PlainNode
+ Data.Puzzles.Elements: data Relation
+ Data.Puzzles.Elements: data SlovakClue
+ Data.Puzzles.Elements: data Star
+ Data.Puzzles.Elements: digitList :: DigitRange -> [Int]
+ Data.Puzzles.Elements: instance GHC.Classes.Eq Data.Puzzles.Elements.DigitRange
+ Data.Puzzles.Elements: instance GHC.Classes.Eq Data.Puzzles.Elements.MasyuPearl
+ Data.Puzzles.Elements: instance GHC.Classes.Eq Data.Puzzles.Elements.Relation
+ Data.Puzzles.Elements: instance GHC.Show.Show Data.Puzzles.Elements.CompassC
+ Data.Puzzles.Elements: instance GHC.Show.Show Data.Puzzles.Elements.DigitRange
+ Data.Puzzles.Elements: instance GHC.Show.Show Data.Puzzles.Elements.KropkiDot
+ Data.Puzzles.Elements: instance GHC.Show.Show Data.Puzzles.Elements.MasyuPearl
+ Data.Puzzles.Elements: instance GHC.Show.Show Data.Puzzles.Elements.Relation
+ Data.Puzzles.Elements: instance GHC.Show.Show Data.Puzzles.Elements.Shade
+ Data.Puzzles.Elements: instance GHC.Show.Show Data.Puzzles.Elements.SlalomDiag
+ Data.Puzzles.Elements: instance GHC.Show.Show Data.Puzzles.Elements.TapaClue
+ Data.Puzzles.Elements: instance GHC.Show.Show a => GHC.Show.Show (Data.Puzzles.Elements.Tightfit a)
+ Data.Puzzles.Elements: type GreaterClue = [Relation]
+ Data.Puzzles.Elements: type Myopia = [Dir']
+ Data.Puzzles.Elements: type VertexLoop = [N]
+ Data.Puzzles.Grid: [bottom] :: OutsideClues k a -> [a]
+ Data.Puzzles.Grid: [left] :: OutsideClues k a -> [a]
+ Data.Puzzles.Grid: [right] :: OutsideClues k a -> [a]
+ Data.Puzzles.Grid: [top] :: OutsideClues k a -> [a]
+ Data.Puzzles.Grid: cellGrid :: Grid N a -> Grid C ()
+ Data.Puzzles.Grid: collectLines :: (Key k, Eq a) => Grid k (Maybe a) -> [Edge k]
+ Data.Puzzles.Grid: colour :: Eq a => Grid C a -> Grid C Int
+ Data.Puzzles.Grid: dominoGrid :: DigitRange -> Grid C (Int, Int)
+ Data.Puzzles.Grid: instance GHC.Base.Functor (Data.Puzzles.Grid.OutsideClues k)
+ Data.Puzzles.Grid: instance GHC.Classes.Eq Data.Puzzles.Grid.OClue
+ Data.Puzzles.Grid: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.Puzzles.Grid.OutsideClues k a)
+ Data.Puzzles.Grid: instance GHC.Classes.Ord Data.Puzzles.Grid.OClue
+ Data.Puzzles.Grid: instance GHC.Show.Show Data.Puzzles.Grid.OClue
+ Data.Puzzles.Grid: instance GHC.Show.Show a => GHC.Show.Show (Data.Puzzles.Grid.OutsideClues k a)
+ Data.Puzzles.Grid: nodeGrid :: Grid C a -> Grid N ()
+ Data.Puzzles.Grid: nodes :: Grid N a -> Set N
+ Data.Puzzles.Grid: outsideGrid :: (Ord k, FromCoord k) => OutsideClues k a -> Grid k ()
+ Data.Puzzles.Grid: sizeGrid :: (Ord k, FromCoord k) => Size -> Grid k ()
+ Data.Puzzles.Grid: type Grid k a = Map k a
+ Data.Puzzles.GridShape: C :: !Int -> !Int -> C
+ Data.Puzzles.GridShape: Horiz :: Dir
+ Data.Puzzles.GridShape: N :: !Int -> !Int -> N
+ Data.Puzzles.GridShape: Vert :: Dir
+ Data.Puzzles.GridShape: class Dual2D a where type Dual a :: * where {
+ Data.Puzzles.GridShape: class FromCoord a
+ Data.Puzzles.GridShape: class ToCoord a
+ Data.Puzzles.GridShape: data C
+ Data.Puzzles.GridShape: data N
+ Data.Puzzles.GridShape: dualE :: Dual' a => Edge a -> Edge (Dual a)
+ Data.Puzzles.GridShape: dualE' :: Dual2D a => Edge' a -> Edge' (Dual a)
+ Data.Puzzles.GridShape: edge :: (AffineSpace a, Diff a ~ (Int, Int)) => a -> a -> Edge a
+ Data.Puzzles.GridShape: edge' :: (AffineSpace a, Diff a ~ (Int, Int)) => a -> a -> Edge' a
+ Data.Puzzles.GridShape: edgeBetween :: Dual' k => k -> k -> Edge (Dual k)
+ Data.Puzzles.GridShape: edgeBetween' :: Dual' k => k -> k -> Edge' (Dual k)
+ Data.Puzzles.GridShape: edgeSize :: [Edge N] -> Size
+ Data.Puzzles.GridShape: edgesM :: Dual' k => Map k a -> ([Edge' (Dual k)], [Edge (Dual k)])
+ Data.Puzzles.GridShape: ends :: (AffineSpace a, Diff a ~ (Int, Int)) => Edge a -> (a, a)
+ Data.Puzzles.GridShape: ends' :: (AffineSpace a, Diff a ~ (Int, Int)) => Edge' a -> (a, a)
+ Data.Puzzles.GridShape: fromCoord :: FromCoord a => Coord -> a
+ Data.Puzzles.GridShape: instance Data.AffineSpace.AffineSpace Data.Puzzles.GridShape.C
+ Data.Puzzles.GridShape: instance Data.AffineSpace.AffineSpace Data.Puzzles.GridShape.N
+ Data.Puzzles.GridShape: instance Data.Puzzles.GridShape.Dual2D Data.Puzzles.GridShape.C
+ Data.Puzzles.GridShape: instance Data.Puzzles.GridShape.Dual2D Data.Puzzles.GridShape.N
+ Data.Puzzles.GridShape: instance Data.Puzzles.GridShape.FromCoord Data.Puzzles.GridShape.C
+ Data.Puzzles.GridShape: instance Data.Puzzles.GridShape.FromCoord Data.Puzzles.GridShape.N
+ Data.Puzzles.GridShape: instance Data.Puzzles.GridShape.ToCoord Data.Puzzles.GridShape.C
+ Data.Puzzles.GridShape: instance Data.Puzzles.GridShape.ToCoord Data.Puzzles.GridShape.N
+ Data.Puzzles.GridShape: instance GHC.Classes.Eq Data.Puzzles.GridShape.C
+ Data.Puzzles.GridShape: instance GHC.Classes.Eq Data.Puzzles.GridShape.Dir
+ Data.Puzzles.GridShape: instance GHC.Classes.Eq Data.Puzzles.GridShape.Dir'
+ Data.Puzzles.GridShape: instance GHC.Classes.Eq Data.Puzzles.GridShape.N
+ Data.Puzzles.GridShape: instance GHC.Classes.Eq Data.Puzzles.GridShape.Square
+ Data.Puzzles.GridShape: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.Puzzles.GridShape.Edge a)
+ Data.Puzzles.GridShape: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.Puzzles.GridShape.Edge' a)
+ Data.Puzzles.GridShape: instance GHC.Classes.Ord Data.Puzzles.GridShape.C
+ Data.Puzzles.GridShape: instance GHC.Classes.Ord Data.Puzzles.GridShape.Dir
+ Data.Puzzles.GridShape: instance GHC.Classes.Ord Data.Puzzles.GridShape.Dir'
+ Data.Puzzles.GridShape: instance GHC.Classes.Ord Data.Puzzles.GridShape.N
+ Data.Puzzles.GridShape: instance GHC.Classes.Ord a => GHC.Classes.Ord (Data.Puzzles.GridShape.Edge a)
+ Data.Puzzles.GridShape: instance GHC.Classes.Ord a => GHC.Classes.Ord (Data.Puzzles.GridShape.Edge' a)
+ Data.Puzzles.GridShape: instance GHC.Show.Show Data.Puzzles.GridShape.C
+ Data.Puzzles.GridShape: instance GHC.Show.Show Data.Puzzles.GridShape.Dir
+ Data.Puzzles.GridShape: instance GHC.Show.Show Data.Puzzles.GridShape.Dir'
+ Data.Puzzles.GridShape: instance GHC.Show.Show Data.Puzzles.GridShape.N
+ Data.Puzzles.GridShape: instance GHC.Show.Show Data.Puzzles.GridShape.Square
+ Data.Puzzles.GridShape: instance GHC.Show.Show a => GHC.Show.Show (Data.Puzzles.GridShape.Edge a)
+ Data.Puzzles.GridShape: instance GHC.Show.Show a => GHC.Show.Show (Data.Puzzles.GridShape.Edge' a)
+ Data.Puzzles.GridShape: orient :: Edge a -> Edge' a
+ Data.Puzzles.GridShape: revEdge :: (AffineSpace a, Diff a ~ (Int, Int)) => Edge' a -> Edge' a
+ Data.Puzzles.GridShape: toCoord :: ToCoord a => a -> Coord
+ Data.Puzzles.GridShape: type Dual' k = (Key k, Dual2D k, Key (Dual k), Dual2D (Dual k), Dual (Dual k) ~ k)
+ Data.Puzzles.GridShape: type Key k = (AffineSpace k, Diff k ~ (Int, Int), Ord k, FromCoord k)
+ Data.Puzzles.GridShape: type family Dual a :: *;
+ Data.Puzzles.GridShape: unorient :: (AffineSpace a, Diff a ~ (Int, Int)) => Edge' a -> Edge a
+ Data.Puzzles.GridShape: }
+ Data.Puzzles.PuzzleTypes: ABCtje :: PuzzleType
+ Data.Puzzles.PuzzleTypes: AngleLoop :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Anglers :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Baca :: PuzzleType
+ Data.Puzzles.PuzzleTypes: BlackoutDominos :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Buchstabensalat :: PuzzleType
+ Data.Puzzles.PuzzleTypes: ColorAkari :: PuzzleType
+ Data.Puzzles.PuzzleTypes: DominoPillen :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Dominos :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Doppelblock :: PuzzleType
+ Data.Puzzles.PuzzleTypes: FillominoCheckered :: PuzzleType
+ Data.Puzzles.PuzzleTypes: FillominoLoop :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Galaxies :: PuzzleType
+ Data.Puzzles.PuzzleTypes: GreaterWall :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Heyawake :: PuzzleType
+ Data.Puzzles.PuzzleTypes: HorseSnake :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Illumination :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Kropki :: PuzzleType
+ Data.Puzzles.PuzzleTypes: LITSSym :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Loopki :: PuzzleType
+ Data.Puzzles.PuzzleTypes: NanroSignpost :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Neighbors :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Numberlink :: PuzzleType
+ Data.Puzzles.PuzzleTypes: PentominoPipes :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Pentominous :: PuzzleType
+ Data.Puzzles.PuzzleTypes: PentominousBorders :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Pentopia :: PuzzleType
+ Data.Puzzles.PuzzleTypes: PersistenceOfMemory :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Scrabble :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Shikaku :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Skyscrapers :: PuzzleType
+ Data.Puzzles.PuzzleTypes: SkyscrapersFrac :: PuzzleType
+ Data.Puzzles.PuzzleTypes: SkyscrapersStars :: PuzzleType
+ Data.Puzzles.PuzzleTypes: SlitherLinkMulti :: PuzzleType
+ Data.Puzzles.PuzzleTypes: SlovakSums :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Starbattle :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Starwars :: PuzzleType
+ Data.Puzzles.PuzzleTypes: StatuePark :: PuzzleType
+ Data.Puzzles.PuzzleTypes: SudokuDoppelblock :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Summon :: PuzzleType
+ Data.Puzzles.PuzzleTypes: TapaCave :: PuzzleType
+ Data.Puzzles.PuzzleTypes: TomTom :: PuzzleType
+ Data.Puzzles.PuzzleTypes: TurningFences :: PuzzleType
+ Data.Puzzles.PuzzleTypes: TwilightTapa :: PuzzleType
+ Data.Puzzles.PuzzleTypes: Wormhole :: PuzzleType
+ Data.Puzzles.PuzzleTypes: instance GHC.Classes.Eq Data.Puzzles.PuzzleTypes.PuzzleType
+ Data.Puzzles.PuzzleTypes: instance GHC.Show.Show Data.Puzzles.PuzzleTypes.PuzzleType
+ Data.Puzzles.Pyramid: [dotsk] :: KropkiRow -> [KropkiDot]
+ Data.Puzzles.Pyramid: [entries] :: Row -> [Maybe Int]
+ Data.Puzzles.Pyramid: [entriesk] :: KropkiRow -> [Maybe Int]
+ Data.Puzzles.Pyramid: [shaded] :: Row -> Bool
+ Data.Puzzles.Pyramid: [shadedk] :: KropkiRow -> Bool
+ Data.Puzzles.Pyramid: [unKP] :: RowKropkiPyramid -> [KropkiRow]
+ Data.Puzzles.Pyramid: [unPyr] :: Pyramid -> [Row]
+ Data.Puzzles.Pyramid: instance Data.Aeson.Types.FromJSON.FromJSON Data.Puzzles.Pyramid.Pyramid
+ Data.Puzzles.Pyramid: instance Data.Aeson.Types.FromJSON.FromJSON Data.Puzzles.Pyramid.PyramidSol
+ Data.Puzzles.Pyramid: instance Data.Aeson.Types.FromJSON.FromJSON Data.Puzzles.Pyramid.RowKropkiPyramid
+ Data.Puzzles.Pyramid: instance GHC.Show.Show Data.Puzzles.Pyramid.KropkiRow
+ Data.Puzzles.Pyramid: instance GHC.Show.Show Data.Puzzles.Pyramid.Pyramid
+ Data.Puzzles.Pyramid: instance GHC.Show.Show Data.Puzzles.Pyramid.PyramidSol
+ Data.Puzzles.Pyramid: instance GHC.Show.Show Data.Puzzles.Pyramid.Row
+ Data.Puzzles.Pyramid: instance GHC.Show.Show Data.Puzzles.Pyramid.RowKropkiPyramid
+ Data.Puzzles.Util: loops :: Eq a => [(a, a)] -> Maybe [[a]]
+ Data.Puzzles.Util: paths :: Eq a => [(a, a)] -> Maybe [[a]]
+ Diagrams.Puzzles.CmdLine: [_file] :: RenderOpts -> FilePath
+ Diagrams.Puzzles.CmdLine: [_w] :: RenderOpts -> Double
+ Diagrams.Puzzles.Code: CodeDiagrams :: a -> a -> a -> CodeDiagrams a
+ Diagrams.Puzzles.Code: [_cdLeft] :: CodeDiagrams a -> a
+ Diagrams.Puzzles.Code: [_cdOver] :: CodeDiagrams a -> a
+ Diagrams.Puzzles.Code: [_cdTop] :: CodeDiagrams a -> a
+ Diagrams.Puzzles.Code: data CodeDiagrams a
+ Diagrams.Puzzles.Code: drawCode :: Backend' b => Code -> CodeDiagrams (Diagram b)
+ Diagrams.Puzzles.Code: instance GHC.Base.Monoid a => GHC.Base.Monoid (Diagrams.Puzzles.Code.CodeDiagrams a)
+ Diagrams.Puzzles.Draw: instance GHC.Show.Show Diagrams.Puzzles.Draw.OutputChoice
+ Diagrams.Puzzles.Elements: DominoC :: C -> DominoC
+ Diagrams.Puzzles.Elements: dominoBG :: Colour Double
+ Diagrams.Puzzles.Elements: drawAnglePoly :: Backend' b => Int -> Diagram b
+ Diagrams.Puzzles.Elements: drawCharFixed :: Backend' b => Char -> Diagram b
+ Diagrams.Puzzles.Elements: drawCharOpaque :: Backend' b => Char -> Diagram b
+ Diagrams.Puzzles.Elements: drawDomino :: Backend' b => (Int, Int) -> Diagram b
+ Diagrams.Puzzles.Elements: drawDominos :: Backend' b => DigitRange -> Diagram b
+ Diagrams.Puzzles.Elements: drawEnd :: Backend' b => MEnd -> Diagram b
+ Diagrams.Puzzles.Elements: drawFish :: Backend' b => Fish -> Diagram b
+ Diagrams.Puzzles.Elements: drawFraction :: Backend' b => Fraction -> Diagram b
+ Diagrams.Puzzles.Elements: drawMarkedLine :: (ToPoint a, Backend' b) => MarkedLine a -> Diagram b
+ Diagrams.Puzzles.Elements: drawMarkedLines :: (ToPoint a, Backend' b) => [MarkedLine a] -> Diagram b
+ Diagrams.Puzzles.Elements: drawMyopia :: Backend' b => Myopia -> Diagram b
+ Diagrams.Puzzles.Elements: drawPill :: Backend' b => Int -> Diagram b
+ Diagrams.Puzzles.Elements: drawPills :: Backend' b => DigitRange -> Diagram b
+ Diagrams.Puzzles.Elements: drawShadow :: Backend' b => Shade -> Diagram b
+ Diagrams.Puzzles.Elements: drawSlalomDiag :: Backend' b => SlalomDiag -> Diagram b
+ Diagrams.Puzzles.Elements: drawSlovakClue :: Backend' b => SlovakClue -> Diagram b
+ Diagrams.Puzzles.Elements: drawStar :: Backend' b => Star -> Diagram b
+ Diagrams.Puzzles.Elements: drawTextFixed :: Backend' b => String -> Diagram b
+ Diagrams.Puzzles.Elements: fish :: Double -> Angle Double -> Trail' Loop V2 Double
+ Diagrams.Puzzles.Elements: greaterClue :: Backend' b => GreaterClue -> [Diagram b]
+ Diagrams.Puzzles.Elements: hintTL :: Backend' b => String -> Diagram b
+ Diagrams.Puzzles.Elements: instance Diagrams.Puzzles.Grid.ToPoint Diagrams.Puzzles.Elements.DominoC
+ Diagrams.Puzzles.Elements: instance GHC.Classes.Eq Diagrams.Puzzles.Elements.DominoC
+ Diagrams.Puzzles.Elements: instance GHC.Classes.Ord Diagrams.Puzzles.Elements.DominoC
+ Diagrams.Puzzles.Elements: kropkiDot :: Backend' b => KropkiDot -> Diagram b
+ Diagrams.Puzzles.Elements: miniloop :: Backend' b => Diagram b
+ Diagrams.Puzzles.Elements: newtype DominoC
+ Diagrams.Puzzles.Elements: note :: Backend' b => Diagram b -> Diagram b
+ Diagrams.Puzzles.Elements: placeNote :: Backend' b => Size -> Diagram b -> Diagram b
+ Diagrams.Puzzles.Elements: placeNoteBR :: Backend' b => Size -> Diagram b -> Diagram b
+ Diagrams.Puzzles.Elements: placeNoteTL :: Backend' b => Size -> Diagram b -> Diagram b
+ Diagrams.Puzzles.Elements: stackWordsLeft :: Backend' b => [String] -> Diagram b
+ Diagrams.Puzzles.Elements: stackWordsRight :: Backend' b => [String] -> Diagram b
+ Diagrams.Puzzles.Elements: vertexLoop :: VertexLoop -> Located (Trail' Loop V2 Double)
+ Diagrams.Puzzles.Grid: (.--.) :: AffineSpace p => p -> p -> Diff p
+ Diagrams.Puzzles.Grid: class ToPoint a
+ Diagrams.Puzzles.Grid: drawAreas :: (Backend' b, Eq a) => Grid C a -> Diagram b
+ Diagrams.Puzzles.Grid: drawAreasGray :: Backend' b => Grid C Char -> Diagram b
+ Diagrams.Puzzles.Grid: drawShade :: Backend' b => Grid C Bool -> Diagram b
+ Diagrams.Puzzles.Grid: drawThinEdges :: (ToPoint k, Backend' b) => [Edge k] -> Diagram b
+ Diagrams.Puzzles.Grid: instance Diagrams.Puzzles.Grid.ToPoint Data.Puzzles.GridShape.C
+ Diagrams.Puzzles.Grid: instance Diagrams.Puzzles.Grid.ToPoint Data.Puzzles.GridShape.N
+ Diagrams.Puzzles.Grid: irregPathToVertices :: (Path V2 Double, Path V2 Double) -> (Set (P2 Double), Set (P2 Double), Set (P2 Double))
+ Diagrams.Puzzles.Grid: midPoint :: (AffineSpace k, Diff k ~ (Int, Int), ToPoint k) => Edge k -> P2 Double
+ Diagrams.Puzzles.Grid: onGrid :: (Transformable a, Monoid a, InSpace V2 Double a) => Double -> Double -> (t -> a) -> [(Coord, t)] -> a
+ Diagrams.Puzzles.Grid: outLine :: Backend' b => Double -> Path V2 Double -> Diagram b
+ Diagrams.Puzzles.Grid: placeGrid :: (ToPoint k, HasOrigin a, Monoid a, InSpace V2 Double a) => Grid k a -> a
+ Diagrams.Puzzles.Grid: placeGrid' :: (HasOrigin a, Monoid a, InSpace V2 Double a) => Grid (P2 Double) a -> a
+ Diagrams.Puzzles.Grid: toPoint :: ToPoint a => a -> P2 Double
+ Diagrams.Puzzles.Lib: aboveT :: Backend' b => Diagram b -> Diagram b -> Diagram b
+ Diagrams.Puzzles.Lib: debugPath :: Backend' b => Path V2 Double -> Diagram b
+ Diagrams.Puzzles.Lib: debugPath' :: Backend' b => Path V2 Double -> Diagram b
+ Diagrams.Puzzles.Lib: fontBit :: Font
+ Diagrams.Puzzles.Lib: fontGenLight :: Font
+ Diagrams.Puzzles.Lib: magnitude :: V2 Double -> Double
+ Diagrams.Puzzles.Lib: text'' :: Backend' b => Font -> String -> Diagram b
+ Diagrams.Puzzles.Lib: textFixed :: Backend' b => String -> Diagram b
+ Diagrams.Puzzles.Lib: type Font = PreparedFont Double
+ Diagrams.Puzzles.PuzzleGrids: drawCharGrid :: Backend' b => Grid C (Maybe Char) -> Diagram b
+ Diagrams.Puzzles.PuzzleGrids: drawMultiOutsideGrid :: Backend' b => OutsideClues C [String] -> Diagram b
+ Diagrams.Puzzles.PuzzleGrids: drawMultiOutsideGridN :: Backend' b => OutsideClues N [String] -> Diagram b
+ Diagrams.Puzzles.PuzzleGrids: drawOutsideGrid :: Backend' b => OutsideClues C (Maybe String) -> Diagram b
+ Diagrams.Puzzles.PuzzleGrids: drawOutsideGridN :: Backend' b => OutsideClues N (Maybe String) -> Diagram b
+ Diagrams.Puzzles.PuzzleGrids: placeMultiOutside :: (Ord k, FromCoord k, ToPoint k, HasOrigin a, Monoid a, InSpace V2 Double a) => OutsideClues k [a] -> a
+ Diagrams.Puzzles.PuzzleTypes: abctje :: Backend' b => RenderPuzzle b (DigitRange, [(String, Int)]) [(Int, Char)]
+ Diagrams.Puzzles.PuzzleTypes: angleLoop :: Backend' b => RenderPuzzle b (Grid N (Clue Int)) VertexLoop
+ Diagrams.Puzzles.PuzzleTypes: anglers :: Backend' b => RenderPuzzle b (OutsideClues C (Clue Int), Grid C (Maybe Fish)) [Edge C]
+ Diagrams.Puzzles.PuzzleTypes: baca :: Backend' b => RenderPuzzle b (Grid C (Maybe Char), OutsideClues C [Int], OutsideClues C (Maybe Char)) (Grid C (Either Black Char))
+ Diagrams.Puzzles.PuzzleTypes: blackoutDominos :: Backend' b => RenderPuzzle b (Grid C (Clue Int), DigitRange) (Grid C (Clue Int), AreaGrid)
+ Diagrams.Puzzles.PuzzleTypes: buchstabensalat :: Backend' b => RenderPuzzle b (OutsideClues C (Maybe Char), String) (Grid C (Maybe Char))
+ Diagrams.Puzzles.PuzzleTypes: colorakari :: Backend' b => RenderPuzzle b (Grid C (Maybe Char)) (Grid C (Maybe Char))
+ Diagrams.Puzzles.PuzzleTypes: dominoPills :: Backend' b => RenderPuzzle b (Grid C (Clue Int), DigitRange, DigitRange) AreaGrid
+ Diagrams.Puzzles.PuzzleTypes: dominos :: Backend' b => RenderPuzzle b (Grid C (Clue Int), DigitRange) AreaGrid
+ Diagrams.Puzzles.PuzzleTypes: doppelblock :: Backend' b => RenderPuzzle b (OutsideClues C (Maybe Int)) (Grid C (Either Black Int))
+ Diagrams.Puzzles.PuzzleTypes: fillominoCheckered :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) (Grid C Int)
+ Diagrams.Puzzles.PuzzleTypes: fillominoLoop :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) (Grid C Int, Loop C)
+ Diagrams.Puzzles.PuzzleTypes: galaxies :: Backend' b => RenderPuzzle b (Grid C (), Grid N (), Grid C (), Map (Edge N) ()) AreaGrid
+ Diagrams.Puzzles.PuzzleTypes: greaterWall :: Backend' b => RenderPuzzle b ([GreaterClue], [GreaterClue]) (Grid C Bool)
+ Diagrams.Puzzles.PuzzleTypes: heyawake :: Backend' b => RenderPuzzle b (AreaGrid, Grid C (Maybe Int)) (Grid C Bool)
+ Diagrams.Puzzles.PuzzleTypes: horseSnake :: Backend' b => RenderPuzzle b (Grid C (Maybe (Either MEnd Int))) [Edge C]
+ Diagrams.Puzzles.PuzzleTypes: illumination :: Backend' b => RenderPuzzle b (OutsideClues C (Maybe Fraction)) (Grid N (Maybe PlainNode), [Edge N])
+ Diagrams.Puzzles.PuzzleTypes: kropki :: Backend' b => RenderPuzzle b (Map (Edge N) KropkiDot) (Grid C Int)
+ Diagrams.Puzzles.PuzzleTypes: litssym :: Backend' b => RenderPuzzle b AreaGrid ShadedGrid
+ Diagrams.Puzzles.PuzzleTypes: loopki :: Backend' b => RenderPuzzle b (Grid C (Maybe MasyuPearl)) (Loop N)
+ Diagrams.Puzzles.PuzzleTypes: nanroSignpost :: Backend' b => RenderPuzzle b (AreaGrid, Grid C (Maybe Int)) (Grid C Int)
+ Diagrams.Puzzles.PuzzleTypes: neighbors :: Backend' b => RenderPuzzle b (Grid C Bool, Grid C (Maybe Int)) (Grid C Int)
+ Diagrams.Puzzles.PuzzleTypes: numberlink :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) [Edge C]
+ Diagrams.Puzzles.PuzzleTypes: pentominoPipes :: Backend' b => RenderPuzzle b (Grid N Char) (Grid N KropkiDot, [Edge N])
+ Diagrams.Puzzles.PuzzleTypes: pentominous :: Backend' b => RenderPuzzle b (Grid C (Maybe Char)) (Grid C Char)
+ Diagrams.Puzzles.PuzzleTypes: pentominousBorders :: Backend' b => RenderPuzzle b (Grid C (), [Edge N]) (Grid C Char)
+ Diagrams.Puzzles.PuzzleTypes: pentopia :: Backend' b => RenderPuzzle b (Grid C (Maybe Myopia)) (Grid C Bool)
+ Diagrams.Puzzles.PuzzleTypes: persistenceOfMemory :: Backend' b => RenderPuzzle b (AreaGrid, Grid C (Maybe MEnd)) (Loop C)
+ Diagrams.Puzzles.PuzzleTypes: scrabble :: Backend' b => RenderPuzzle b (Grid C Bool, [String]) (Grid C (Maybe Char))
+ Diagrams.Puzzles.PuzzleTypes: shikaku :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) AreaGrid
+ Diagrams.Puzzles.PuzzleTypes: skyscrapers :: Backend' b => RenderPuzzle b (OutsideClues C (Maybe Int), String) (Grid C (Maybe Int))
+ Diagrams.Puzzles.PuzzleTypes: skyscrapersStars :: Backend' b => RenderPuzzle b (OutsideClues C (Maybe Int), Int) (Grid C (Either Int Star))
+ Diagrams.Puzzles.PuzzleTypes: slithermulti :: Backend' b => RenderPuzzle b (Grid C (Maybe Int), Int) [Edge N]
+ Diagrams.Puzzles.PuzzleTypes: slovaksums :: Backend' b => RenderPuzzle b (Grid C (Maybe SlovakClue), String) (Grid C (Maybe Int))
+ Diagrams.Puzzles.PuzzleTypes: starbattle :: Backend' b => RenderPuzzle b (AreaGrid, Int) (Grid C (Maybe Star))
+ Diagrams.Puzzles.PuzzleTypes: starwars :: Backend' b => RenderPuzzle b (AreaGrid, [MarkedLine C]) (Grid C (Maybe Star))
+ Diagrams.Puzzles.PuzzleTypes: statuepark :: Backend' b => RenderPuzzle b (Grid C (Maybe MasyuPearl)) (Grid C Bool)
+ Diagrams.Puzzles.PuzzleTypes: sudokuDoppelblock :: Backend' b => RenderPuzzle b (AreaGrid, OutsideClues C (Maybe Int)) (Grid C (Either Black Int))
+ Diagrams.Puzzles.PuzzleTypes: summon :: Backend' b => RenderPuzzle b (AreaGrid, OutsideClues C (Maybe Int), String) (Grid C (Maybe Int))
+ Diagrams.Puzzles.PuzzleTypes: tomTom :: Backend' b => RenderPuzzle b (AreaGrid, Grid C (Maybe String)) (Grid C Int)
+ Diagrams.Puzzles.PuzzleTypes: wormhole :: Backend' b => RenderPuzzle b (Grid C (Maybe (Either Int Char))) ()
+ Diagrams.Puzzles.Style: FrameStyle :: Double -> Colour Double -> FrameStyle
+ Diagrams.Puzzles.Style: GridStyle :: LineStyle -> LineStyle -> Maybe FrameStyle -> VertexStyle -> GridStyle
+ Diagrams.Puzzles.Style: LineDashed :: LineStyle
+ Diagrams.Puzzles.Style: LineNone :: LineStyle
+ Diagrams.Puzzles.Style: LineThick :: LineStyle
+ Diagrams.Puzzles.Style: LineThin :: LineStyle
+ Diagrams.Puzzles.Style: VertexDot :: VertexStyle
+ Diagrams.Puzzles.Style: VertexNone :: VertexStyle
+ Diagrams.Puzzles.Style: [_border] :: GridStyle -> LineStyle
+ Diagrams.Puzzles.Style: [_fColour] :: FrameStyle -> Colour Double
+ Diagrams.Puzzles.Style: [_fWidthFactor] :: FrameStyle -> Double
+ Diagrams.Puzzles.Style: [_frame] :: GridStyle -> Maybe FrameStyle
+ Diagrams.Puzzles.Style: [_line] :: GridStyle -> LineStyle
+ Diagrams.Puzzles.Style: [_vertex] :: GridStyle -> VertexStyle
+ Diagrams.Puzzles.Style: data FrameStyle
+ Diagrams.Puzzles.Style: data GridStyle
+ Diagrams.Puzzles.Style: data LineStyle
+ Diagrams.Puzzles.Style: data VertexStyle
+ Diagrams.Puzzles.Style: gDashed :: GridStyle
+ Diagrams.Puzzles.Style: gDashedThick :: GridStyle
+ Diagrams.Puzzles.Style: gDefault :: GridStyle
+ Diagrams.Puzzles.Style: gPlain :: GridStyle
+ Diagrams.Puzzles.Style: gPlainDashed :: GridStyle
+ Diagrams.Puzzles.Style: gSlither :: GridStyle
+ Text.Puzzles.Code: parseCode :: Value -> Parser [CodePart]
+ Text.Puzzles.Parsec: fraction :: GenParser Char st Fraction
+ Text.Puzzles.Parsec: toParser :: GenParser a () b -> [a] -> Parser b
+ Text.Puzzles.Parsec: toStringParser :: GenParser Char () b -> Value -> Parser b
+ Text.Puzzles.Puzzle: instance Data.Aeson.Types.FromJSON.FromJSON Text.Puzzles.Puzzle.TypedPuzzle
+ Text.Puzzles.Puzzle: instance GHC.Show.Show Text.Puzzles.Puzzle.TypedPuzzle
+ Text.Puzzles.PuzzleTypes: abctje :: ParsePuzzle (DigitRange, [(String, Int)]) [(Int, Char)]
+ Text.Puzzles.PuzzleTypes: angleLoop :: ParsePuzzle (Grid N (Clue Int)) VertexLoop
+ Text.Puzzles.PuzzleTypes: anglers :: ParsePuzzle (OutsideClues C (Maybe Int), Grid C (Maybe Fish)) [Edge C]
+ Text.Puzzles.PuzzleTypes: baca :: ParsePuzzle (Grid C (Maybe Char), OutsideClues C [Int], OutsideClues C (Maybe Char)) (Grid C (Either Black Char))
+ Text.Puzzles.PuzzleTypes: blackoutDominos :: ParsePuzzle (Grid C (Clue Int), DigitRange) (Grid C (Clue Int), AreaGrid)
+ Text.Puzzles.PuzzleTypes: buchstabensalat :: ParsePuzzle (OutsideClues C (Maybe Char), String) (Grid C (Maybe Char))
+ Text.Puzzles.PuzzleTypes: colorakari :: ParsePuzzle (Grid C (Maybe Char)) (Grid C (Maybe Char))
+ Text.Puzzles.PuzzleTypes: dominoPills :: ParsePuzzle (Grid C (Maybe Int), DigitRange, DigitRange) AreaGrid
+ Text.Puzzles.PuzzleTypes: dominos :: ParsePuzzle (Grid C (Maybe Int), DigitRange) AreaGrid
+ Text.Puzzles.PuzzleTypes: doppelblock :: ParsePuzzle (OutsideClues C (Maybe Int)) (Grid C (Either Black Int))
+ Text.Puzzles.PuzzleTypes: fillominoLoop :: ParsePuzzle (Grid C (Maybe Int)) (Grid C Int, Loop C)
+ Text.Puzzles.PuzzleTypes: galaxies :: ParsePuzzle (Grid C (), Grid N (), Grid C (), Map (Edge N) ()) AreaGrid
+ Text.Puzzles.PuzzleTypes: greaterWall :: ParsePuzzle ([GreaterClue], [GreaterClue]) (Grid C Bool)
+ Text.Puzzles.PuzzleTypes: heyawake :: ParsePuzzle (AreaGrid, Grid C (Maybe Int)) (Grid C Bool)
+ Text.Puzzles.PuzzleTypes: horseSnake :: ParsePuzzle (Grid C (Maybe (Either MEnd Int))) [Edge C]
+ Text.Puzzles.PuzzleTypes: illumination :: ParsePuzzle (OutsideClues C (Maybe Fraction)) (Grid N (Maybe PlainNode), [Edge N])
+ Text.Puzzles.PuzzleTypes: instance Data.Aeson.Types.FromJSON.FromJSON Text.Puzzles.PuzzleTypes.Curve
+ Text.Puzzles.PuzzleTypes: instance Data.Aeson.Types.FromJSON.FromJSON Text.Puzzles.PuzzleTypes.GridMarked
+ Text.Puzzles.PuzzleTypes: instance Data.Aeson.Types.FromJSON.FromJSON Text.Puzzles.PuzzleTypes.GridWords
+ Text.Puzzles.PuzzleTypes: instance Data.Aeson.Types.FromJSON.FromJSON Text.Puzzles.PuzzleTypes.LSol
+ Text.Puzzles.PuzzleTypes: instance Data.Aeson.Types.FromJSON.FromJSON Text.Puzzles.PuzzleTypes.Myo
+ Text.Puzzles.PuzzleTypes: kropki :: ParsePuzzle (Map (Edge N) KropkiDot) (Grid C Int)
+ Text.Puzzles.PuzzleTypes: loopki :: ParsePuzzle (Grid C (Maybe MasyuPearl)) (Loop N)
+ Text.Puzzles.PuzzleTypes: nanroSignpost :: ParsePuzzle (AreaGrid, Grid C (Maybe Int)) (Grid C Int)
+ Text.Puzzles.PuzzleTypes: neighbors :: ParsePuzzle (Grid C Bool, Grid C (Maybe Int)) (Grid C Int)
+ Text.Puzzles.PuzzleTypes: numberlink :: ParsePuzzle (Grid C (Maybe Int)) [Edge C]
+ Text.Puzzles.PuzzleTypes: pentominoPipes :: ParsePuzzle (Grid N Char) (Grid N KropkiDot, [Edge N])
+ Text.Puzzles.PuzzleTypes: pentominous :: ParsePuzzle (Grid C (Maybe Char)) (Grid C Char)
+ Text.Puzzles.PuzzleTypes: pentominousBorders :: ParsePuzzle (Grid C (), [Edge N]) (Grid C Char)
+ Text.Puzzles.PuzzleTypes: pentopia :: ParsePuzzle (Grid C (Maybe Myopia)) (Grid C Bool)
+ Text.Puzzles.PuzzleTypes: persistenceOfMemory :: ParsePuzzle (AreaGrid, Grid C (Maybe MEnd)) (Loop C)
+ Text.Puzzles.PuzzleTypes: scrabble :: ParsePuzzle (Grid C Bool, [String]) (Grid C (Maybe Char))
+ Text.Puzzles.PuzzleTypes: shikaku :: ParsePuzzle (Grid C (Maybe Int)) AreaGrid
+ Text.Puzzles.PuzzleTypes: skyscrapers :: ParsePuzzle (OutsideClues C (Maybe Int), String) (Grid C (Maybe Int))
+ Text.Puzzles.PuzzleTypes: skyscrapersStars :: ParsePuzzle (OutsideClues C (Maybe Int), Int) (Grid C (Either Int Star))
+ Text.Puzzles.PuzzleTypes: slithermulti :: ParsePuzzle (Grid C (Clue Int), Int) [Edge N]
+ Text.Puzzles.PuzzleTypes: slovaksums :: ParsePuzzle (Grid C (Maybe SlovakClue), String) (Grid C (Maybe Int))
+ Text.Puzzles.PuzzleTypes: starbattle :: ParsePuzzle (AreaGrid, Int) (Grid C (Maybe Star))
+ Text.Puzzles.PuzzleTypes: starwars :: ParsePuzzle (AreaGrid, [MarkedLine C]) (Grid C (Maybe Star))
+ Text.Puzzles.PuzzleTypes: statuepark :: ParsePuzzle (Grid C (Maybe MasyuPearl)) (Grid C Bool)
+ Text.Puzzles.PuzzleTypes: sudokuDoppelblock :: ParsePuzzle (AreaGrid, OutsideClues C (Maybe Int)) (Grid C (Either Black Int))
+ Text.Puzzles.PuzzleTypes: summon :: ParsePuzzle (AreaGrid, OutsideClues C (Maybe Int), String) (Grid C (Maybe Int))
+ Text.Puzzles.PuzzleTypes: tomTom :: ParsePuzzle (AreaGrid, Grid C (Maybe String)) (Grid C Int)
+ Text.Puzzles.PuzzleTypes: wormhole :: ParsePuzzle (Grid C (Maybe (Either Int Char))) ()
+ Text.Puzzles.Util: Blank'' :: Blank''
+ Text.Puzzles.Util: Dirs' :: [Dir'] -> Dirs'
+ Text.Puzzles.Util: MM :: Map k (Maybe a) -> MaybeMap k a
+ Text.Puzzles.Util: PFraction :: Fraction -> PFraction
+ Text.Puzzles.Util: PML :: MarkedLine a -> PMarkedLine a
+ Text.Puzzles.Util: PSlovakClue :: SlovakClue -> PSlovakClue
+ Text.Puzzles.Util: [unAlpha] :: Alpha -> Char
+ Text.Puzzles.Util: [unDirs'] :: Dirs' -> [Dir']
+ Text.Puzzles.Util: [unEither'] :: Either' a b -> Either a b
+ Text.Puzzles.Util: [unHalfDirs] :: HalfDirs -> [Dir]
+ Text.Puzzles.Util: [unIntString] :: IntString -> String
+ Text.Puzzles.Util: [unMaybeMap] :: MaybeMap k a -> Map k (Maybe a)
+ Text.Puzzles.Util: [unPCC] :: PCompassC -> CompassC
+ Text.Puzzles.Util: [unPFraction] :: PFraction -> Fraction
+ Text.Puzzles.Util: [unPML] :: PMarkedLine a -> MarkedLine a
+ Text.Puzzles.Util: [unPMW] :: PMarkedWord -> MarkedWord
+ Text.Puzzles.Util: [unPSlovakClue] :: PSlovakClue -> SlovakClue
+ Text.Puzzles.Util: [unParseTapaClue] :: ParseTapaClue -> TapaClue
+ Text.Puzzles.Util: [unRG] :: RefGrid k a -> Grid k (Maybe a)
+ Text.Puzzles.Util: [unShaded] :: Shaded -> Bool
+ Text.Puzzles.Util: [unSpaced] :: SpacedRect a -> Rect a
+ Text.Puzzles.Util: [unTight] :: Tight -> Tightfit ()
+ Text.Puzzles.Util: blankToMaybe'' :: Either Blank'' a -> Maybe a
+ Text.Puzzles.Util: char :: Char -> Char -> Parser Char
+ Text.Puzzles.Util: chars :: [Char] -> Char -> Parser Char
+ Text.Puzzles.Util: compose' :: Ord b => Map a (Maybe b) -> Map b c -> Maybe (Map a (Maybe c))
+ Text.Puzzles.Util: data Blank''
+ Text.Puzzles.Util: filterPlainEdges :: Map (Edge k) Char -> [Edge k]
+ Text.Puzzles.Util: fromCoordEdge :: Key k => Edge Coord -> Edge k
+ Text.Puzzles.Util: fromCoordEdges :: Key k => [Edge Coord] -> [Edge k]
+ Text.Puzzles.Util: fromCoordGrid :: Key k => Grid Coord a -> Grid k a
+ Text.Puzzles.Util: impossible :: a
+ Text.Puzzles.Util: instance (Data.Aeson.Types.FromJSON.FromJSON a, Data.Aeson.Types.FromJSON.FromJSON b) => Data.Aeson.Types.FromJSON.FromJSON (Text.Puzzles.Util.Either' a b)
+ Text.Puzzles.Util: instance (Data.Puzzles.GridShape.Key k, Data.Aeson.Types.FromJSON.FromJSON a) => Data.Aeson.Types.FromJSON.FromJSON (Text.Puzzles.Util.RefGrid k a)
+ Text.Puzzles.Util: instance (GHC.Show.Show b, GHC.Show.Show a) => GHC.Show.Show (Text.Puzzles.Util.BorderedRect a b)
+ Text.Puzzles.Util: instance (Text.Puzzles.Util.FromChar a, Text.Puzzles.Util.FromChar b) => Data.Aeson.Types.FromJSON.FromJSON (Text.Puzzles.Util.BorderedRect a b)
+ Text.Puzzles.Util: instance (Text.Puzzles.Util.FromChar a, Text.Puzzles.Util.FromChar b) => Text.Puzzles.Util.FromChar (Data.Either.Either a b)
+ Text.Puzzles.Util: instance (Text.Puzzles.Util.FromChar a, Text.Puzzles.Util.FromChar b) => Text.Puzzles.Util.FromChar (Text.Puzzles.Util.Either' a b)
+ Text.Puzzles.Util: instance (Text.Puzzles.Util.FromString a, Text.Puzzles.Util.FromString b) => Text.Puzzles.Util.FromString (Data.Either.Either a b)
+ Text.Puzzles.Util: instance Data.Aeson.Types.FromJSON.FromJSON Text.Puzzles.Util.Blank
+ Text.Puzzles.Util: instance Data.Aeson.Types.FromJSON.FromJSON Text.Puzzles.Util.Blank'
+ Text.Puzzles.Util: instance Data.Aeson.Types.FromJSON.FromJSON Text.Puzzles.Util.IntString
+ Text.Puzzles.Util: instance Data.Aeson.Types.FromJSON.FromJSON Text.Puzzles.Util.PCompassC
+ Text.Puzzles.Util: instance Data.Aeson.Types.FromJSON.FromJSON Text.Puzzles.Util.PFraction
+ Text.Puzzles.Util: instance Data.Aeson.Types.FromJSON.FromJSON Text.Puzzles.Util.PMarkedWord
+ Text.Puzzles.Util: instance Data.Aeson.Types.FromJSON.FromJSON Text.Puzzles.Util.PSlovakClue
+ Text.Puzzles.Util: instance Data.Aeson.Types.FromJSON.FromJSON Text.Puzzles.Util.ParseTapaClue
+ Text.Puzzles.Util: instance Data.Foldable.Foldable (Text.Puzzles.Util.MaybeMap k)
+ Text.Puzzles.Util: instance Data.Foldable.Foldable Text.Puzzles.Util.Border
+ Text.Puzzles.Util: instance Data.Puzzles.GridShape.FromCoord a => Data.Aeson.Types.FromJSON.FromJSON (Text.Puzzles.Util.PMarkedLine a)
+ Text.Puzzles.Util: instance Data.Traversable.Traversable (Text.Puzzles.Util.MaybeMap k)
+ Text.Puzzles.Util: instance Data.Traversable.Traversable Text.Puzzles.Util.Border
+ Text.Puzzles.Util: instance GHC.Base.Functor (Text.Puzzles.Util.MaybeMap k)
+ Text.Puzzles.Util: instance GHC.Base.Functor Text.Puzzles.Util.Border
+ Text.Puzzles.Util: instance GHC.Base.Functor Text.Puzzles.Util.Rect
+ Text.Puzzles.Util: instance GHC.Classes.Eq Text.Puzzles.Util.Alpha
+ Text.Puzzles.Util: instance GHC.Classes.Ord Text.Puzzles.Util.Alpha
+ Text.Puzzles.Util: instance GHC.Show.Show Text.Puzzles.Util.Alpha
+ Text.Puzzles.Util: instance GHC.Show.Show a => GHC.Show.Show (Text.Puzzles.Util.Border a)
+ Text.Puzzles.Util: instance GHC.Show.Show a => GHC.Show.Show (Text.Puzzles.Util.Rect a)
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar ()
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Data.Puzzles.Elements.Black
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Data.Puzzles.Elements.Crossing
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Data.Puzzles.Elements.Fish
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Data.Puzzles.Elements.GalaxyCentre
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Data.Puzzles.Elements.KropkiDot
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Data.Puzzles.Elements.MEnd
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Data.Puzzles.Elements.MasyuPearl
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Data.Puzzles.Elements.PlainNode
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Data.Puzzles.Elements.PrimeDiag
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Data.Puzzles.Elements.Relation
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Data.Puzzles.Elements.SlalomDiag
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Data.Puzzles.Elements.Star
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Data.Puzzles.GridShape.Dir'
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar GHC.Types.Char
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar GHC.Types.Int
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Text.Puzzles.Util.Alpha
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Text.Puzzles.Util.Blank
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Text.Puzzles.Util.Blank'
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Text.Puzzles.Util.Blank''
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Text.Puzzles.Util.Dirs'
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Text.Puzzles.Util.Empty
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Text.Puzzles.Util.HalfDirs
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Text.Puzzles.Util.Shaded
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Text.Puzzles.Util.Space
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar Text.Puzzles.Util.Tight
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar a => Data.Aeson.Types.FromJSON.FromJSON (Text.Puzzles.Util.Rect a)
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar a => Text.Puzzles.Util.FromChar (GHC.Base.Maybe a)
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromChar a => Text.Puzzles.Util.FromString (Data.Puzzles.Elements.Tightfit a)
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromString Data.Puzzles.Elements.DigitRange
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromString GHC.Types.Int
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromString Text.Puzzles.Util.Blank
+ Text.Puzzles.Util: instance Text.Puzzles.Util.FromString a => Data.Aeson.Types.FromJSON.FromJSON (Text.Puzzles.Util.SpacedRect a)
+ Text.Puzzles.Util: newtype Dirs'
+ Text.Puzzles.Util: newtype MaybeMap k a
+ Text.Puzzles.Util: newtype PFraction
+ Text.Puzzles.Util: newtype PMarkedLine a
+ Text.Puzzles.Util: newtype PSlovakClue
+ Text.Puzzles.Util: parseAnnotatedEdges :: (Key k, FromChar a) => Value -> Parser (Map (Edge k) a)
+ Text.Puzzles.Util: parseBorderedRect :: (Char -> Parser a) -> (Char -> Parser b) -> Value -> Parser (BorderedRect a b)
+ Text.Puzzles.Util: parseCoordGrid :: (FromChar a) => Value -> Parser (Grid Coord a)
+ Text.Puzzles.Util: parseCoordLoop :: Value -> Parser VertexLoop
+ Text.Puzzles.Util: parseDoublePair :: FromString a => Value -> Parser ((a, a), (a, a))
+ Text.Puzzles.Util: parseEdgesFull :: Key k => Value -> Parser [Edge k]
+ Text.Puzzles.Util: parseExtClueGrid :: (Key k, FromChar a, FromJSON a) => Value -> Parser (Grid k (Maybe a))
+ Text.Puzzles.Util: parseExtGrid' :: (Key k, FromJSON a, FromChar b) => (a -> b) -> Value -> Parser (Grid k b)
+ Text.Puzzles.Util: parseGreaterClue :: [Char] -> Parser GreaterClue
+ Text.Puzzles.Util: parseGreaterClues :: Value -> Parser [GreaterClue]
+ Text.Puzzles.Util: parseMarkedLine :: FromCoord a => Value -> Parser (MarkedLine a)
+ Text.Puzzles.Util: parseOutsideGrid :: Key k => (Char -> Parser a) -> (Char -> Parser b) -> Value -> Parser (OutsideClues k b, Grid k a)
+ Text.Puzzles.Util: parseOutsideGridMap :: (Key k, FromChar a, FromChar b) => (a -> c) -> (b -> d) -> Value -> Parser (OutsideClues k d, Grid k c)
+ Text.Puzzles.Util: parsePlainEdgeGrid :: (FromChar a, FromChar b) => Value -> Parser (Grid N a, Grid C b, [Edge N])
+ Text.Puzzles.Util: parseSpacedGrid :: (Key k, FromString a) => Value -> Parser (Grid k a)
+ Text.Puzzles.Util: parseStringJSON :: FromString a => Value -> Parser a
+ Text.Puzzles.Util: rectToCoordGrid :: Rect a -> Grid Coord a
- Data.Puzzles.Compose: drawPuzzleMaybeSol :: PuzzleHandler b ((Value, Maybe Value) -> Parser (Diagram b R2, Maybe (Diagram b R2)))
+ Data.Puzzles.Compose: drawPuzzleMaybeSol :: PuzzleHandler b ((Value, Maybe Value) -> Parser (Diagram b, Maybe (Diagram b)))
- Data.Puzzles.Elements: type Loop = [Edge]
+ Data.Puzzles.Elements: type Loop a = [Edge a]
- Data.Puzzles.Elements: type Thermometer = [Coord]
+ Data.Puzzles.Elements: type Thermometer = [C]
- Data.Puzzles.Grid: OC :: [a] -> [a] -> [a] -> [a] -> OutsideClues a
+ Data.Puzzles.Grid: OC :: [a] -> [a] -> [a] -> [a] -> OutsideClues k a
- Data.Puzzles.Grid: borders :: Eq a => Grid Square a -> [Edge]
+ Data.Puzzles.Grid: borders :: Eq a => Grid C a -> [Edge N]
- Data.Puzzles.Grid: clues :: GridShape s => Grid s (Maybe a) -> [(Cell s, a)]
+ Data.Puzzles.Grid: clues :: Grid k (Maybe a) -> Grid k a
- Data.Puzzles.Grid: data OutsideClues a
+ Data.Puzzles.Grid: data OutsideClues k a
- Data.Puzzles.Grid: edgesGen :: (a -> a -> Bool) -> (a -> Bool) -> Grid Square a -> [Edge]
+ Data.Puzzles.Grid: edgesGen :: Dual' k => (a -> a -> Bool) -> (a -> Bool) -> Map k a -> [Edge (Dual k)]
- Data.Puzzles.Grid: multiOutsideClues :: OutsideClues [a] -> [((Int, Int), a)]
+ Data.Puzzles.Grid: multiOutsideClues :: (Ord k, FromCoord k) => OutsideClues k [a] -> Map k a
- Data.Puzzles.Grid: outsideClues :: OutsideClues (Maybe a) -> [((Int, Int), a)]
+ Data.Puzzles.Grid: outsideClues :: (Ord k, FromCoord k) => OutsideClues k a -> Map k a
- Data.Puzzles.Grid: outsideSize :: OutsideClues a -> (Int, Int)
+ Data.Puzzles.Grid: outsideSize :: OutsideClues k a -> Size
- Data.Puzzles.Grid: size :: GridShape s => Grid s a -> GridSize s
+ Data.Puzzles.Grid: size :: Grid Coord a -> Size
- Data.Puzzles.Grid: type AreaGrid = CharGrid
+ Data.Puzzles.Grid: type AreaGrid = Grid C Char
- Data.Puzzles.Grid: type ShadedGrid = SGrid Bool
+ Data.Puzzles.Grid: type ShadedGrid = Grid C Bool
- Data.Puzzles.GridShape: E :: (Cell Square) -> Dir -> Edge
+ Data.Puzzles.GridShape: E :: a -> Dir -> Edge a
- Data.Puzzles.GridShape: Square :: !Int -> !Int -> Square
+ Data.Puzzles.GridShape: Square :: Square
- Data.Puzzles.GridShape: data Edge
+ Data.Puzzles.GridShape: data Edge a
- Data.Puzzles.GridShape: edgeNeighbours :: GridShape a => a -> Cell a -> [Cell a]
+ Data.Puzzles.GridShape: edgeNeighbours :: C -> [C]
- Data.Puzzles.GridShape: edges :: Foldable f => f (Cell Square) -> (Cell Square -> Bool) -> ([Edge' (Vertex Square)], [Edge' (Vertex Square)])
+ Data.Puzzles.GridShape: edges :: (Dual' k, Foldable f) => f k -> (k -> Bool) -> ([Edge' (Dual k)], [Edge (Dual k)])
- Data.Puzzles.GridShape: type Coord = Cell Square
+ Data.Puzzles.GridShape: type Coord = (Int, Int)
- Data.Puzzles.GridShape: type Size = GridSize Square
+ Data.Puzzles.GridShape: type Size = (Int, Int)
- Data.Puzzles.GridShape: vertexNeighbours :: GridShape a => a -> Cell a -> [Cell a]
+ Data.Puzzles.GridShape: vertexNeighbours :: C -> [C]
- Data.Puzzles.Sudoku: sudokuborders :: Int -> [Edge]
+ Data.Puzzles.Sudoku: sudokuborders :: Int -> [Edge N]
- Data.Puzzles.Sudoku: sudokubordersg :: SGrid a -> [Edge]
+ Data.Puzzles.Sudoku: sudokubordersg :: Grid C a -> [Edge N]
- Diagrams.Puzzles.CmdLine: renderToFile :: RenderOpts -> Diagram B R2 -> IO ()
+ Diagrams.Puzzles.CmdLine: renderToFile :: RenderOpts -> Diagram B -> IO ()
- Diagrams.Puzzles.Draw: diagramWidth :: Diagram b R2 -> Double
+ Diagrams.Puzzles.Draw: diagramWidth :: Backend' b => Diagram b -> Double
- Diagrams.Puzzles.Draw: draw :: Backend' b => PuzzleSol b -> OutputChoice -> Maybe (Diagram b R2)
+ Diagrams.Puzzles.Draw: draw :: Backend' b => Maybe (CodeDiagrams (Diagram b)) -> PuzzleSol b -> OutputChoice -> Maybe (Diagram b)
- Diagrams.Puzzles.Draw: type PuzzleSol b = (Diagram b R2, Maybe (Diagram b R2))
+ Diagrams.Puzzles.Draw: type PuzzleSol b = (Diagram b, Maybe (Diagram b))
- Diagrams.Puzzles.Draw: type RenderPuzzle b p s = (p -> Diagram b R2, (p, s) -> Diagram b R2)
+ Diagrams.Puzzles.Draw: type RenderPuzzle b p s = (p -> Diagram b, (p, s) -> Diagram b)
- Diagrams.Puzzles.Elements: cross :: Path R2
+ Diagrams.Puzzles.Elements: cross :: Path V2 Double
- Diagrams.Puzzles.Elements: dr :: Path R2
+ Diagrams.Puzzles.Elements: dr :: Path V2 Double
- Diagrams.Puzzles.Elements: drawBahnhofClue :: Backend' b => BahnhofClue -> Diagram b R2
+ Diagrams.Puzzles.Elements: drawBahnhofClue :: Backend' b => BahnhofClue -> Diagram b
- Diagrams.Puzzles.Elements: drawChar :: Backend' b => Char -> Diagram b R2
+ Diagrams.Puzzles.Elements: drawChar :: Backend' b => Char -> Diagram b
- Diagrams.Puzzles.Elements: drawCompassClue :: Backend' b => CompassC -> Diagram b R2
+ Diagrams.Puzzles.Elements: drawCompassClue :: Backend' b => CompassC -> Diagram b
- Diagrams.Puzzles.Elements: drawCross :: Backend' b => Diagram b R2
+ Diagrams.Puzzles.Elements: drawCross :: Backend' b => Bool -> Diagram b
- Diagrams.Puzzles.Elements: drawCrossing :: Backend' b => Crossing -> Diagram b R2
+ Diagrams.Puzzles.Elements: drawCrossing :: Backend' b => Crossing -> Diagram b
- Diagrams.Puzzles.Elements: drawCurve :: Backend' b => [Edge] -> Diagram b R2
+ Diagrams.Puzzles.Elements: drawCurve :: Backend' b => [Edge N] -> Diagram b
- Diagrams.Puzzles.Elements: drawInt :: Backend' b => Int -> Diagram b R2
+ Diagrams.Puzzles.Elements: drawInt :: Backend' b => Int -> Diagram b
- Diagrams.Puzzles.Elements: drawMarkedWord :: Backend' b => MarkedWord -> QDiagram b R2 Any
+ Diagrams.Puzzles.Elements: drawMarkedWord :: Backend' b => MarkedWord -> Diagram b
- Diagrams.Puzzles.Elements: drawMarkedWords :: Backend' b => [MarkedWord] -> QDiagram b R2 Any
+ Diagrams.Puzzles.Elements: drawMarkedWords :: Backend' b => [MarkedWord] -> Diagram b
- Diagrams.Puzzles.Elements: drawPrimeDiag :: Backend' b => PrimeDiag -> Diagram b R2
+ Diagrams.Puzzles.Elements: drawPrimeDiag :: Backend' b => PrimeDiag -> Diagram b
- Diagrams.Puzzles.Elements: drawSlalomClue :: (Show a, Backend' b) => a -> Diagram b R2
+ Diagrams.Puzzles.Elements: drawSlalomClue :: (Show a, Backend' b) => a -> Diagram b
- Diagrams.Puzzles.Elements: drawTapaClue :: Backend' b => TapaClue -> Diagram b R2
+ Diagrams.Puzzles.Elements: drawTapaClue :: Backend' b => TapaClue -> Diagram b
- Diagrams.Puzzles.Elements: drawText :: Backend' b => String -> QDiagram b R2 Any
+ Diagrams.Puzzles.Elements: drawText :: Backend' b => String -> Diagram b
- Diagrams.Puzzles.Elements: drawThermos :: Backend' b => [Thermometer] -> QDiagram b R2 Any
+ Diagrams.Puzzles.Elements: drawThermos :: Backend' b => [Thermometer] -> Diagram b
- Diagrams.Puzzles.Elements: drawTight :: Backend' b => (a -> Diagram b R2) -> Tightfit a -> Diagram b R2
+ Diagrams.Puzzles.Elements: drawTight :: Backend' b => (a -> Diagram b) -> Tightfit a -> Diagram b
- Diagrams.Puzzles.Elements: drawWords :: Backend' b => [String] -> Diagram b R2
+ Diagrams.Puzzles.Elements: drawWords :: Backend' b => [String] -> Diagram b
- Diagrams.Puzzles.Elements: pearl :: Backend' b => MasyuPearl -> Diagram b R2
+ Diagrams.Puzzles.Elements: pearl :: Backend' b => MasyuPearl -> Diagram b
- Diagrams.Puzzles.Elements: smallPearl :: Backend' b => MasyuPearl -> Diagram b R2
+ Diagrams.Puzzles.Elements: smallPearl :: Backend' b => MasyuPearl -> Diagram b
- Diagrams.Puzzles.Elements: stackWords :: Backend' b => [String] -> QDiagram b R2 Any
+ Diagrams.Puzzles.Elements: stackWords :: Backend' b => [String] -> Diagram b
- Diagrams.Puzzles.Elements: thermo :: Backend' b => [P2] -> QDiagram b R2 Any
+ Diagrams.Puzzles.Elements: thermo :: Backend' b => [P2 Double] -> Diagram b
- Diagrams.Puzzles.Elements: ur :: Path R2
+ Diagrams.Puzzles.Elements: ur :: Path V2 Double
- Diagrams.Puzzles.Grid: bgdashingG :: (Semigroup a, HasStyle a, V a ~ R2) => [Double] -> Double -> Colour Double -> a -> a
+ Diagrams.Puzzles.Grid: bgdashingG :: (Semigroup a, HasStyle a, InSpace V2 Double a) => [Double] -> Double -> AlphaColour Double -> a -> a
- Diagrams.Puzzles.Grid: distrib :: (Transformable c, Monoid c, V c ~ R2) => R2 -> (Int, Int) -> Double -> [c] -> c
+ Diagrams.Puzzles.Grid: distrib :: (Transformable c, Monoid c, InSpace V2 Double c) => V2 Double -> (Int, Int) -> Double -> [c] -> c
- Diagrams.Puzzles.Grid: dot :: Backend' b => Diagram b R2
+ Diagrams.Puzzles.Grid: dot :: Backend' b => Diagram b
- Diagrams.Puzzles.Grid: drawEdges :: Backend' b => [Edge] -> Diagram b R2
+ Diagrams.Puzzles.Grid: drawEdges :: (ToPoint k, Backend' b) => [Edge k] -> Diagram b
- Diagrams.Puzzles.Grid: edge :: Edge -> Path R2
+ Diagrams.Puzzles.Grid: edge :: (ToPoint k) => Edge k -> Path V2 Double
- Diagrams.Puzzles.Grid: edgeStyle :: (HasStyle a, V a ~ R2) => a -> a
+ Diagrams.Puzzles.Grid: edgeStyle :: (HasStyle a, InSpace V2 Double a) => a -> a
- Diagrams.Puzzles.Grid: fillBG :: Backend' b => Colour Double -> Diagram b R2
+ Diagrams.Puzzles.Grid: fillBG :: Backend' b => Colour Double -> Diagram b
- Diagrams.Puzzles.Grid: grid :: Backend' b => Size -> Diagram b R2
+ Diagrams.Puzzles.Grid: grid :: Backend' b => GridStyle -> Grid C a -> Diagram b
- Diagrams.Puzzles.Grid: gridDashing :: (Semigroup a, HasStyle a, V a ~ R2) => a -> a
+ Diagrams.Puzzles.Grid: gridDashing :: (Semigroup a, HasStyle a, InSpace V2 Double a) => a -> a
- Diagrams.Puzzles.Grid: irregularGridPaths :: SGrid a -> (Path R2, Path R2)
+ Diagrams.Puzzles.Grid: irregularGridPaths :: Grid C a -> (Path V2 Double, Path V2 Double)
- Diagrams.Puzzles.Grid: shadeGrid :: Backend' b => SGrid (Maybe (Colour Double)) -> Diagram b R2
+ Diagrams.Puzzles.Grid: shadeGrid :: Backend' b => Grid C (Maybe (Colour Double)) -> Diagram b
- Diagrams.Puzzles.Grid: thinEdgeStyle :: (HasStyle a, V a ~ R2) => a -> a
+ Diagrams.Puzzles.Grid: thinEdgeStyle :: (HasStyle a, InSpace V2 Double a) => a -> a
- Diagrams.Puzzles.Lib: besidesL :: (Backend' b, Semigroup m, Monoid m) => QDiagram b R2 m -> QDiagram b R2 m -> QDiagram b R2 m
+ Diagrams.Puzzles.Lib: besidesL :: Backend' b => Diagram b -> Diagram b -> Diagram b
- Diagrams.Puzzles.Lib: besidesR :: (Backend' b, Semigroup m, Monoid m) => QDiagram b R2 m -> QDiagram b R2 m -> QDiagram b R2 m
+ Diagrams.Puzzles.Lib: besidesR :: Backend' b => Diagram b -> Diagram b -> Diagram b
- Diagrams.Puzzles.Lib: dmid :: (Enveloped a, V a ~ R2) => a -> Double
+ Diagrams.Puzzles.Lib: dmid :: (InSpace V2 Double a, Enveloped a) => V2 Double -> a -> Double
- Diagrams.Puzzles.Lib: fit :: (Transformable t, Enveloped t, V t ~ R2) => Double -> t -> t
+ Diagrams.Puzzles.Lib: fit :: (Transformable t, Enveloped t, InSpace V2 Double t) => Double -> t -> t
- Diagrams.Puzzles.Lib: hcatsep :: (Juxtaposable a, HasOrigin a, Monoid' a, V a ~ R2) => [a] -> a
+ Diagrams.Puzzles.Lib: hcatsep :: (InSpace V2 Double a, Juxtaposable a, HasOrigin a, Monoid' a) => [a] -> a
- Diagrams.Puzzles.Lib: hline :: Backend' b => Double -> Diagram b R2
+ Diagrams.Puzzles.Lib: hline :: Backend' b => Double -> Diagram b
- Diagrams.Puzzles.Lib: mirror :: (Transformable t, V t ~ R2) => t -> t
+ Diagrams.Puzzles.Lib: mirror :: (InSpace V2 Double t, Transformable t) => t -> t
- Diagrams.Puzzles.Lib: p2i :: (Int, Int) -> P2
+ Diagrams.Puzzles.Lib: p2i :: (Int, Int) -> P2 Double
- Diagrams.Puzzles.Lib: phantom' :: Backend' b => Diagram b R2 -> Diagram b R2
+ Diagrams.Puzzles.Lib: phantom' :: Backend' b => Diagram b -> Diagram b
- Diagrams.Puzzles.Lib: r2i :: (Int, Int) -> R2
+ Diagrams.Puzzles.Lib: r2i :: (Int, Int) -> V2 Double
- Diagrams.Puzzles.Lib: smash :: Backend' b => QDiagram b R2 Any -> QDiagram b R2 Any
+ Diagrams.Puzzles.Lib: smash :: Backend' b => Diagram b -> Diagram b
- Diagrams.Puzzles.Lib: spread :: Backend' b => R2 -> [Diagram b R2] -> Diagram b R2
+ Diagrams.Puzzles.Lib: spread :: Backend' b => V2 Double -> [Diagram b] -> Diagram b
- Diagrams.Puzzles.Lib: text' :: Backend' b => String -> Diagram b R2
+ Diagrams.Puzzles.Lib: text' :: Backend' b => String -> Diagram b
- Diagrams.Puzzles.Lib: translatep :: (Transformable t, V t ~ R2) => (Int, Int) -> t -> t
+ Diagrams.Puzzles.Lib: translatep :: (InSpace V2 Double t, Transformable t) => (Int, Int) -> t -> t
- Diagrams.Puzzles.Lib: type Backend' b = (Backend b R2, Renderable (Path R2) b)
+ Diagrams.Puzzles.Lib: type Backend' b = (V b ~ V2, N b ~ Double, Renderable (Path V2 Double) b, Backend b V2 Double)
- Diagrams.Puzzles.Lib: vcatsep :: (Juxtaposable a, HasOrigin a, Monoid' a, V a ~ R2) => [a] -> a
+ Diagrams.Puzzles.Lib: vcatsep :: (InSpace V2 Double a, Juxtaposable a, HasOrigin a, Monoid' a) => [a] -> a
- Diagrams.Puzzles.Lib: vline :: Backend' b => Double -> Diagram b R2
+ Diagrams.Puzzles.Lib: vline :: Backend' b => Double -> Diagram b
- Diagrams.Puzzles.PuzzleGrids: drawIntGrid :: Backend' b => SGrid (Clue Int) -> Diagram b R2
+ Diagrams.Puzzles.PuzzleGrids: drawIntGrid :: Backend' b => Grid C (Maybe Int) -> Diagram b
- Diagrams.Puzzles.PuzzleGrids: drawSlitherGrid :: Backend' b => SGrid (Clue Int) -> Diagram b R2
+ Diagrams.Puzzles.PuzzleGrids: drawSlitherGrid :: Backend' b => Grid C (Maybe Int) -> Diagram b
- Diagrams.Puzzles.PuzzleGrids: drawTightGrid :: Backend' b => (t -> Diagram b R2) -> SGrid (Tightfit t) -> Diagram b R2
+ Diagrams.Puzzles.PuzzleGrids: drawTightGrid :: Backend' b => (t -> Diagram b) -> Grid C (Tightfit t) -> Diagram b
- Diagrams.Puzzles.PuzzleGrids: drawWordsClues :: Backend' b => SGrid (Clue [String]) -> Diagram b R2
+ Diagrams.Puzzles.PuzzleGrids: drawWordsClues :: Backend' b => Grid C (Maybe [String]) -> Diagram b
- Diagrams.Puzzles.PuzzleGrids: outsideIntGrid :: Backend' b => OutsideClues [Int] -> Diagram b R2
+ Diagrams.Puzzles.PuzzleGrids: outsideIntGrid :: Backend' b => OutsideClues C [Int] -> Diagram b
- Diagrams.Puzzles.PuzzleGrids: sudokugrid :: Backend' b => SGrid a -> Diagram b R2
+ Diagrams.Puzzles.PuzzleGrids: sudokugrid :: Backend' b => Grid C a -> Diagram b
- Diagrams.Puzzles.PuzzleTypes: afternoonskyscrapers :: Backend' b => RenderPuzzle b (SGrid Shade) IntGrid
+ Diagrams.Puzzles.PuzzleTypes: afternoonskyscrapers :: Backend' b => RenderPuzzle b (Grid C Shade) (Grid C (Maybe Int))
- Diagrams.Puzzles.PuzzleTypes: bahnhof :: Backend' b => RenderPuzzle b (SGrid (Maybe BahnhofClue)) [Edge]
+ Diagrams.Puzzles.PuzzleTypes: bahnhof :: Backend' b => RenderPuzzle b (Grid C (Maybe BahnhofClue)) [Edge C]
- Diagrams.Puzzles.PuzzleTypes: boxof2or3 :: Backend' b => RenderPuzzle b (SGrid MasyuPearl, [Edge]) ()
+ Diagrams.Puzzles.PuzzleTypes: boxof2or3 :: Backend' b => RenderPuzzle b (Grid N MasyuPearl, [Edge N]) ()
- Diagrams.Puzzles.PuzzleTypes: cave :: (Backend b R2, Renderable (Path R2) b) => RenderPuzzle b (SGrid (Clue Int)) ShadedGrid
+ Diagrams.Puzzles.PuzzleTypes: cave :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) ShadedGrid
- Diagrams.Puzzles.PuzzleTypes: compass :: Backend' b => RenderPuzzle b (SGrid (Clue CompassC)) AreaGrid
+ Diagrams.Puzzles.PuzzleTypes: compass :: Backend' b => RenderPuzzle b (Grid C (Maybe CompassC)) AreaGrid
- Diagrams.Puzzles.PuzzleTypes: coral :: Backend' b => RenderPuzzle b (OutsideClues [String]) ShadedGrid
+ Diagrams.Puzzles.PuzzleTypes: coral :: Backend' b => RenderPuzzle b (OutsideClues C [String]) ShadedGrid
- Diagrams.Puzzles.PuzzleTypes: curvedata :: Backend' b => RenderPuzzle b (SGrid (Clue [Edge])) [Edge]
+ Diagrams.Puzzles.PuzzleTypes: curvedata :: Backend' b => RenderPuzzle b (Grid C (Maybe [Edge N])) [Edge C]
- Diagrams.Puzzles.PuzzleTypes: doubleback :: Backend' b => RenderPuzzle b AreaGrid Loop
+ Diagrams.Puzzles.PuzzleTypes: doubleback :: Backend' b => RenderPuzzle b AreaGrid (Loop C)
- Diagrams.Puzzles.PuzzleTypes: fillomino :: Backend' b => RenderPuzzle b IntGrid (SGrid Int)
+ Diagrams.Puzzles.PuzzleTypes: fillomino :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) (Grid C Int)
- Diagrams.Puzzles.PuzzleTypes: geradeweg :: Backend' b => RenderPuzzle b IntGrid Loop
+ Diagrams.Puzzles.PuzzleTypes: geradeweg :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) (Loop C)
- Diagrams.Puzzles.PuzzleTypes: japanesesums :: Backend' b => RenderPuzzle b (OutsideClues [Int]) (SGrid (Either Black Int))
+ Diagrams.Puzzles.PuzzleTypes: japanesesums :: Backend' b => RenderPuzzle b (OutsideClues C [Int], String) (Grid C (Either Black Int))
- Diagrams.Puzzles.PuzzleTypes: labyrinth :: Backend' b => RenderPuzzle b (SGrid (Clue Int), [Edge]) (SGrid (Clue Int))
+ Diagrams.Puzzles.PuzzleTypes: labyrinth :: Backend' b => RenderPuzzle b (Grid C (Maybe Int), [Edge N]) (Grid C (Maybe Int))
- Diagrams.Puzzles.PuzzleTypes: latintapa :: Backend' b => RenderPuzzle b (SGrid (Clue [String])) CharClueGrid
+ Diagrams.Puzzles.PuzzleTypes: latintapa :: Backend' b => RenderPuzzle b (Grid C (Maybe [String])) (Grid C (Maybe Char))
- Diagrams.Puzzles.PuzzleTypes: liarslither :: Backend' b => RenderPuzzle b IntGrid (Loop, SGrid Bool)
+ Diagrams.Puzzles.PuzzleTypes: liarslither :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) (Loop N, Grid C Bool)
- Diagrams.Puzzles.PuzzleTypes: masyu :: Backend' b => RenderPuzzle b (SGrid (Clue MasyuPearl)) Loop
+ Diagrams.Puzzles.PuzzleTypes: masyu :: Backend' b => RenderPuzzle b (Grid C (Maybe MasyuPearl)) (Loop C)
- Diagrams.Puzzles.PuzzleTypes: maximallengths :: Backend' b => RenderPuzzle b (OutsideClues (Maybe Int)) Loop
+ Diagrams.Puzzles.PuzzleTypes: maximallengths :: Backend' b => RenderPuzzle b (OutsideClues C (Maybe Int)) (Loop C)
- Diagrams.Puzzles.PuzzleTypes: meanderingnumbers :: Backend' b => RenderPuzzle b AreaGrid IntGrid
+ Diagrams.Puzzles.PuzzleTypes: meanderingnumbers :: Backend' b => RenderPuzzle b AreaGrid (Grid C (Maybe Int))
- Diagrams.Puzzles.PuzzleTypes: nurikabe :: Backend' b => RenderPuzzle b IntGrid ShadedGrid
+ Diagrams.Puzzles.PuzzleTypes: nurikabe :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) ShadedGrid
- Diagrams.Puzzles.PuzzleTypes: primeplace :: Backend' b => RenderPuzzle b (SGrid PrimeDiag) (SGrid Int)
+ Diagrams.Puzzles.PuzzleTypes: primeplace :: Backend' b => RenderPuzzle b (Grid C PrimeDiag) (Grid C Int)
- Diagrams.Puzzles.PuzzleTypes: slalom :: Backend' b => RenderPuzzle b IntGrid (SGrid SlalomDiag)
+ Diagrams.Puzzles.PuzzleTypes: slalom :: Backend' b => RenderPuzzle b (Grid N (Maybe Int)) (Grid C SlalomDiag)
- Diagrams.Puzzles.PuzzleTypes: slither :: Backend' b => RenderPuzzle b IntGrid Loop
+ Diagrams.Puzzles.PuzzleTypes: slither :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) (Loop N)
- Diagrams.Puzzles.PuzzleTypes: sudoku :: Backend' b => RenderPuzzle b IntGrid IntGrid
+ Diagrams.Puzzles.PuzzleTypes: sudoku :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) (Grid C (Maybe Int))
- Diagrams.Puzzles.PuzzleTypes: tapa :: Backend' b => RenderPuzzle b (SGrid TapaClue) ShadedGrid
+ Diagrams.Puzzles.PuzzleTypes: tapa :: Backend' b => RenderPuzzle b (Grid C (Maybe TapaClue)) ShadedGrid
- Diagrams.Puzzles.PuzzleTypes: thermosudoku :: Backend' b => RenderPuzzle b (SGrid Int, [Thermometer]) IntGrid
+ Diagrams.Puzzles.PuzzleTypes: thermosudoku :: Backend' b => RenderPuzzle b (Grid C (Maybe Int), [Thermometer]) (Grid C (Maybe Int))
- Diagrams.Puzzles.PuzzleTypes: tightfitskyscrapers :: Backend' b => RenderPuzzle b (OutsideClues (Maybe Int), SGrid (Tightfit ())) (SGrid (Tightfit Int))
+ Diagrams.Puzzles.PuzzleTypes: tightfitskyscrapers :: Backend' b => RenderPuzzle b (OutsideClues C (Maybe Int), Grid C (Tightfit ())) (Grid C (Tightfit Int))
- Diagrams.Puzzles.PuzzleTypes: wordloop :: Backend' b => RenderPuzzle b (CharClueGrid, [String]) CharClueGrid
+ Diagrams.Puzzles.PuzzleTypes: wordloop :: Backend' b => RenderPuzzle b (Grid C (Maybe Char), [String]) (Grid C (Maybe Char))
- Diagrams.Puzzles.PuzzleTypes: wordsearch :: Backend' b => RenderPuzzle b (CharClueGrid, [String]) (CharClueGrid, [MarkedWord])
+ Diagrams.Puzzles.PuzzleTypes: wordsearch :: Backend' b => RenderPuzzle b (Grid C (Maybe Char), [String]) (Grid C (Maybe Char), [MarkedWord])
- Diagrams.Puzzles.Pyramid: cell :: Backend' b => Bool -> Diagram b R2
+ Diagrams.Puzzles.Pyramid: cell :: Backend' b => Bool -> Diagram b
- Diagrams.Puzzles.Pyramid: cellc :: Backend' b => Bool -> Maybe Int -> Diagram b R2
+ Diagrams.Puzzles.Pyramid: cellc :: Backend' b => Bool -> Maybe Int -> Diagram b
- Diagrams.Puzzles.Pyramid: clue :: Backend' b => Maybe Int -> Diagram b R2
+ Diagrams.Puzzles.Pyramid: clue :: Backend' b => Maybe Int -> Diagram b
- Diagrams.Puzzles.Pyramid: kpyramid :: Backend' b => RowKropkiPyramid -> Diagram b R2
+ Diagrams.Puzzles.Pyramid: kpyramid :: Backend' b => RowKropkiPyramid -> Diagram b
- Diagrams.Puzzles.Pyramid: krow :: Backend' b => KropkiRow -> Diagram b R2
+ Diagrams.Puzzles.Pyramid: krow :: Backend' b => KropkiRow -> Diagram b
- Diagrams.Puzzles.Pyramid: pyramid :: Backend' b => Pyramid -> Diagram b R2
+ Diagrams.Puzzles.Pyramid: pyramid :: Backend' b => Pyramid -> Diagram b
- Diagrams.Puzzles.Pyramid: row :: Backend' b => Row -> Diagram b R2
+ Diagrams.Puzzles.Pyramid: row :: Backend' b => Row -> Diagram b
- Text.Puzzles.Puzzle: TP :: (Maybe String) -> Value -> (Maybe Value) -> TypedPuzzle
+ Text.Puzzles.Puzzle: TP :: (Maybe String) -> Value -> (Maybe Value) -> (Maybe Value) -> TypedPuzzle
- Text.Puzzles.PuzzleTypes: afternoonskyscrapers :: ParsePuzzle (SGrid Shade) IntGrid
+ Text.Puzzles.PuzzleTypes: afternoonskyscrapers :: ParsePuzzle (Grid C Shade) (Grid C (Maybe Int))
- Text.Puzzles.PuzzleTypes: bahnhof :: ParsePuzzle (SGrid (Maybe BahnhofClue)) [Edge]
+ Text.Puzzles.PuzzleTypes: bahnhof :: ParsePuzzle (Grid C (Maybe BahnhofClue)) [Edge C]
- Text.Puzzles.PuzzleTypes: boxof2or3 :: ParsePuzzle (SGrid MasyuPearl, [Edge]) ()
+ Text.Puzzles.PuzzleTypes: boxof2or3 :: ParsePuzzle (Grid N MasyuPearl, [Edge N]) ()
- Text.Puzzles.PuzzleTypes: cave :: ParsePuzzle (SGrid (Clue Int)) ShadedGrid
+ Text.Puzzles.PuzzleTypes: cave :: ParsePuzzle (Grid C (Maybe Int)) (Grid C Bool)
- Text.Puzzles.PuzzleTypes: compass :: ParsePuzzle (SGrid (Clue CompassC)) CharGrid
+ Text.Puzzles.PuzzleTypes: compass :: ParsePuzzle (Grid C (Maybe CompassC)) AreaGrid
- Text.Puzzles.PuzzleTypes: coral :: ParsePuzzle (OutsideClues [String]) ShadedGrid
+ Text.Puzzles.PuzzleTypes: coral :: ParsePuzzle (OutsideClues C [String]) (Grid C Bool)
- Text.Puzzles.PuzzleTypes: curvedata :: ParsePuzzle (SGrid (Clue [Edge])) [Edge]
+ Text.Puzzles.PuzzleTypes: curvedata :: ParsePuzzle (Grid C (Maybe [Edge N])) [Edge C]
- Text.Puzzles.PuzzleTypes: doubleback :: ParsePuzzle AreaGrid Loop
+ Text.Puzzles.PuzzleTypes: doubleback :: ParsePuzzle AreaGrid (Loop C)
- Text.Puzzles.PuzzleTypes: fillomino :: ParsePuzzle IntGrid (SGrid Int)
+ Text.Puzzles.PuzzleTypes: fillomino :: ParsePuzzle (Grid C (Maybe Int)) (Grid C Int)
- Text.Puzzles.PuzzleTypes: geradeweg :: ParsePuzzle (SGrid (Clue Int)) Loop
+ Text.Puzzles.PuzzleTypes: geradeweg :: ParsePuzzle (Grid C (Maybe Int)) (Loop C)
- Text.Puzzles.PuzzleTypes: japanesesums :: ParsePuzzle (OutsideClues [Int]) (SGrid (Either Black Int))
+ Text.Puzzles.PuzzleTypes: japanesesums :: ParsePuzzle (OutsideClues C [Int], String) (Grid C (Either Black Int))
- Text.Puzzles.PuzzleTypes: labyrinth :: ParsePuzzle (SGrid (Clue Int), [Edge]) (SGrid (Clue Int))
+ Text.Puzzles.PuzzleTypes: labyrinth :: ParsePuzzle (Grid C (Maybe Int), [Edge N]) (Grid C (Maybe Int))
- Text.Puzzles.PuzzleTypes: latintapa :: ParsePuzzle (SGrid (Clue [String])) (SGrid (Maybe Char))
+ Text.Puzzles.PuzzleTypes: latintapa :: ParsePuzzle (Grid C (Maybe [String])) (Grid C (Maybe Char))
- Text.Puzzles.PuzzleTypes: liarslither :: ParsePuzzle (SGrid (Clue Int)) (Loop, SGrid Bool)
+ Text.Puzzles.PuzzleTypes: liarslither :: ParsePuzzle (Grid C (Maybe Int)) (Loop N, Grid C Bool)
- Text.Puzzles.PuzzleTypes: lits :: ParsePuzzle AreaGrid ShadedGrid
+ Text.Puzzles.PuzzleTypes: lits :: ParsePuzzle AreaGrid (Grid C Bool)
- Text.Puzzles.PuzzleTypes: litsplus :: ParsePuzzle AreaGrid ShadedGrid
+ Text.Puzzles.PuzzleTypes: litsplus :: ParsePuzzle AreaGrid (Grid C Bool)
- Text.Puzzles.PuzzleTypes: masyu :: ParsePuzzle (SGrid (Clue MasyuPearl)) Loop
+ Text.Puzzles.PuzzleTypes: masyu :: ParsePuzzle (Grid C (Maybe MasyuPearl)) (Loop C)
- Text.Puzzles.PuzzleTypes: maximallengths :: ParsePuzzle (OutsideClues (Maybe Int)) Loop
+ Text.Puzzles.PuzzleTypes: maximallengths :: ParsePuzzle (OutsideClues C (Maybe Int)) (Loop C)
- Text.Puzzles.PuzzleTypes: meanderingnumbers :: ParsePuzzle AreaGrid IntGrid
+ Text.Puzzles.PuzzleTypes: meanderingnumbers :: ParsePuzzle AreaGrid (Grid C (Maybe Int))
- Text.Puzzles.PuzzleTypes: nurikabe :: ParsePuzzle IntGrid ShadedGrid
+ Text.Puzzles.PuzzleTypes: nurikabe :: ParsePuzzle (Grid C (Maybe Int)) (Grid C Bool)
- Text.Puzzles.PuzzleTypes: primeplace :: ParsePuzzle (SGrid PrimeDiag) (SGrid Int)
+ Text.Puzzles.PuzzleTypes: primeplace :: ParsePuzzle (Grid C PrimeDiag) (Grid C Int)
- Text.Puzzles.PuzzleTypes: slalom :: ParsePuzzle (SGrid (Clue Int)) (SGrid SlalomDiag)
+ Text.Puzzles.PuzzleTypes: slalom :: ParsePuzzle (Grid N (Maybe Int)) (Grid C SlalomDiag)
- Text.Puzzles.PuzzleTypes: slither :: ParsePuzzle (SGrid (Clue Int)) Loop
+ Text.Puzzles.PuzzleTypes: slither :: ParsePuzzle (Grid C (Clue Int)) (Loop N)
- Text.Puzzles.PuzzleTypes: sudoku :: ParsePuzzle IntGrid IntGrid
+ Text.Puzzles.PuzzleTypes: sudoku :: ParsePuzzle (Grid C (Maybe Int)) (Grid C (Maybe Int))
- Text.Puzzles.PuzzleTypes: tapa :: ParsePuzzle (SGrid TapaClue) ShadedGrid
+ Text.Puzzles.PuzzleTypes: tapa :: ParsePuzzle (Grid C (Maybe TapaClue)) (Grid C Bool)
- Text.Puzzles.PuzzleTypes: thermosudoku :: ParsePuzzle (SGrid Int, [Thermometer]) IntGrid
+ Text.Puzzles.PuzzleTypes: thermosudoku :: ParsePuzzle (Grid C (Maybe Int), [Thermometer]) (Grid C (Maybe Int))
- Text.Puzzles.PuzzleTypes: tightfitskyscrapers :: ParsePuzzle (OutsideClues (Maybe Int), SGrid (Tightfit ())) (SGrid (Tightfit Int))
+ Text.Puzzles.PuzzleTypes: tightfitskyscrapers :: ParsePuzzle (OutsideClues C (Maybe Int), Grid C (Tightfit ())) (Grid C (Tightfit Int))
- Text.Puzzles.PuzzleTypes: wordloop :: ParsePuzzle (CharClueGrid, [String]) CharClueGrid
+ Text.Puzzles.PuzzleTypes: wordloop :: ParsePuzzle (Grid C (Maybe Char), [String]) (Grid C (Maybe Char))
- Text.Puzzles.PuzzleTypes: wordsearch :: ParsePuzzle (CharClueGrid, [String]) (CharClueGrid, [MarkedWord])
+ Text.Puzzles.PuzzleTypes: wordsearch :: ParsePuzzle (Grid C (Maybe Char), [String]) (Grid C (Maybe Char), [MarkedWord])
- Text.Puzzles.Util: RefGrid :: SGrid a -> RefGrid a
+ Text.Puzzles.Util: RefGrid :: Grid k (Maybe a) -> RefGrid k a
- Text.Puzzles.Util: compose :: (Ord a, Ord b) => Map a b -> Map b c -> Maybe (Map a c)
+ Text.Puzzles.Util: compose :: Ord b => Map a b -> Map b c -> Maybe (Map a c)
- Text.Puzzles.Util: hashmaptomap :: (Eq a, Hashable a, Ord a) => HashMap a b -> Map a b
+ Text.Puzzles.Util: hashmaptomap :: Ord a => HashMap a b -> Map a b
- Text.Puzzles.Util: listListToMap :: [[a]] -> Map (Cell Square) a
+ Text.Puzzles.Util: listListToMap :: [[a]] -> Grid Coord a
- Text.Puzzles.Util: newtype RefGrid a
+ Text.Puzzles.Util: newtype RefGrid k a
- Text.Puzzles.Util: parseAfternoonGrid :: Value -> Parser (SGrid Shade)
+ Text.Puzzles.Util: parseAfternoonGrid :: Value -> Parser (Grid C Shade)
- Text.Puzzles.Util: parseCellEdges :: FromChar a => Value -> Parser (SGrid a, [Edge])
+ Text.Puzzles.Util: parseCellEdges :: FromChar a => Value -> Parser (Grid C a, [Edge N])
- Text.Puzzles.Util: parseCharOutside :: FromChar a => Value -> Parser (OutsideClues a)
+ Text.Puzzles.Util: parseCharOutside :: FromChar a => Value -> Parser (OutsideClues k a)
- Text.Puzzles.Util: parseClueGrid :: FromChar a => Value -> Parser (SGrid (Clue a))
+ Text.Puzzles.Util: parseClueGrid :: (FromChar a, Key k) => Value -> Parser (Grid k (Maybe a))
- Text.Puzzles.Util: parseClueGrid' :: FromChar a => Value -> Parser (SGrid (Clue a))
+ Text.Puzzles.Util: parseClueGrid' :: (FromChar a, Key k) => Value -> Parser (Grid k (Maybe a))
- Text.Puzzles.Util: parseEdgeGrid :: (FromChar a, FromChar b) => Value -> Parser (SGrid a, SGrid b, [Edge])
+ Text.Puzzles.Util: parseEdgeGrid :: (FromChar a, FromChar b, FromChar c) => Value -> Parser (Grid N a, Grid C b, Map (Edge N) c)
- Text.Puzzles.Util: parseEdges :: Value -> Parser [Edge]
+ Text.Puzzles.Util: parseEdges :: Key k => Value -> Parser [Edge k]
- Text.Puzzles.Util: parseExtGrid :: (FromChar a, FromJSON a) => Value -> Parser (SGrid a)
+ Text.Puzzles.Util: parseExtGrid :: (Key k, FromChar a, FromJSON a) => Value -> Parser (Grid k a)
- Text.Puzzles.Util: parseFrom :: Path -> (Value -> Parser b) -> (Value -> Parser b)
+ Text.Puzzles.Util: parseFrom :: Path -> (Value -> Parser b) -> Value -> Parser b
- Text.Puzzles.Util: parseGrid :: FromChar a => Value -> Parser (SGrid a)
+ Text.Puzzles.Util: parseGrid :: (Key k, FromChar a) => Value -> Parser (Grid k a)
- Text.Puzzles.Util: parseGridChars :: FromChar a => SGrid Char -> Parser (SGrid a)
+ Text.Puzzles.Util: parseGridChars :: FromChar a => Grid k Char -> Parser (Grid k a)
- Text.Puzzles.Util: parseGridWith :: (Char -> Parser a) -> Value -> Parser (SGrid a)
+ Text.Puzzles.Util: parseGridWith :: Key k => (Char -> Parser a) -> Value -> Parser (Grid k a)
- Text.Puzzles.Util: parseIrregGrid :: FromChar a => Value -> Parser (SGrid a)
+ Text.Puzzles.Util: parseIrregGrid :: (Key k, FromChar a) => Value -> Parser (Grid k a)
- Text.Puzzles.Util: parseMultiOutsideClues :: FromJSON a => Value -> Parser (OutsideClues [a])
+ Text.Puzzles.Util: parseMultiOutsideClues :: FromJSON a => Value -> Parser (OutsideClues k [a])
- Text.Puzzles.Util: parseNodeEdges :: FromChar a => Value -> Parser (SGrid a, [Edge])
+ Text.Puzzles.Util: parseNodeEdges :: FromChar a => Value -> Parser (Grid N a, [Edge N])
- Text.Puzzles.Util: parseOutside :: FromJSON a => Value -> Parser (OutsideClues a)
+ Text.Puzzles.Util: parseOutside :: FromJSON a => Value -> Parser (OutsideClues k a)
- Text.Puzzles.Util: parsePlainEdges :: Value -> Parser [Edge]
+ Text.Puzzles.Util: parsePlainEdges :: Key k => Value -> Parser [Edge k]
- Text.Puzzles.Util: parseShadedGrid :: Value -> Parser (SGrid Bool)
+ Text.Puzzles.Util: parseShadedGrid :: Key k => Value -> Parser (Grid k Bool)
- Text.Puzzles.Util: parseSpacedClueGrid :: FromString a => Value -> Parser (SGrid (Clue a))
+ Text.Puzzles.Util: parseSpacedClueGrid :: (Key k, FromString a) => Value -> Parser (Grid k (Maybe a))
- Text.Puzzles.Util: parseThermoGrid :: ThermoRect -> Parser (SGrid Int, [Thermometer])
+ Text.Puzzles.Util: parseThermoGrid :: ThermoRect -> Parser (Grid C (Maybe Int), [Thermometer])
- Text.Puzzles.Util: parseThermos :: SGrid Alpha -> Parser [Thermometer]
+ Text.Puzzles.Util: parseThermos :: Grid C Alpha -> Parser [Thermometer]
- Text.Puzzles.Util: parseTightOutside :: Value -> Parser (OutsideClues (Maybe Int), SGrid (Tightfit ()))
+ Text.Puzzles.Util: parseTightOutside :: Value -> Parser (OutsideClues C (Maybe Int), Grid C (Tightfit ()))
- Text.Puzzles.Util: readEdges :: SGrid Char -> [Edge]
+ Text.Puzzles.Util: readEdges :: Grid Coord Char -> Map (Edge Coord) Char
- Text.Puzzles.Util: rectToIrregGrid :: Rect (Either Empty a) -> SGrid a
+ Text.Puzzles.Util: rectToIrregGrid :: Rect (Either Empty a) -> Grid Coord a
- Text.Puzzles.Util: reorientOutside :: OutsideClues a -> OutsideClues a
+ Text.Puzzles.Util: reorientOutside :: OutsideClues k a -> OutsideClues k a

Files

CHANGES.md view
@@ -1,3 +1,10 @@+0.2.0.0: 20161211+-----------------++* Upgrade diagrams version.+* Add various new puzzle types.+* Add solution code markers.+ 0.1.0.4: 20141115 ----------------- 
README.md view
@@ -45,7 +45,8 @@     ..X... ``` -There is a demo web application at [demo] that provides some more+There is a demo web application at+[puzzle-draw-demo.herokuapp.com][demo] that provides some more examples and that can be used to generate SVG images from such puzzle descriptions. 
puzzle-draw.cabal view
@@ -1,5 +1,5 @@ name:                puzzle-draw-version:             0.1.0.4+version:             0.2.0.0 synopsis:            Creating graphics for pencil puzzles. description:         puzzle-draw is a library and tool for drawing pencil                      puzzles using Diagrams. It aims to provide a utility@@ -28,14 +28,20 @@ library   exposed-modules:     Data.Puzzles.Grid                        Data.Puzzles.GridShape+                       Data.Puzzles.Util                        Data.Puzzles.Elements                        Data.Puzzles.Pyramid                        Data.Puzzles.Sudoku                        Data.Puzzles.Compose                        Data.Puzzles.PuzzleTypes+                       Data.Puzzles.Code+                       Data.Puzzles.CmdLine                        Text.Puzzles.Util+                       Text.Puzzles.Parsec                        Text.Puzzles.Puzzle                        Text.Puzzles.PuzzleTypes+                       Text.Puzzles.Code+                       Diagrams.Puzzles.Style                        Diagrams.Puzzles.Grid                        Diagrams.Puzzles.Lib                        Diagrams.Puzzles.Elements@@ -44,11 +50,12 @@                        Diagrams.Puzzles.PuzzleGrids                        Diagrams.Puzzles.PuzzleTypes                        Diagrams.Puzzles.Draw+                       Diagrams.Puzzles.Code                        Diagrams.Puzzles.CmdLine   -- For access to the font files in the data directory.   other-modules:       Paths_puzzle_draw-  build-depends:       base >= 4.2 && < 5,-                       diagrams-lib >= 1.2,+  build-depends:       base >= 4.8 && < 5,+                       diagrams-lib >= 1.3.1,                        parsec >= 3.1,                        yaml >= 0.8.4,                        aeson >= 0.7,@@ -59,47 +66,80 @@                        SVGFonts >= 1.4,                        vector-space >= 0.8,                        mtl >= 2.1,-                       optparse-applicative >= 0.7,-                       filepath >= 1.3+                       optparse-applicative >= 0.12 && < 0.13,+                       filepath >= 1.3,+                       linear   if flag(cairo)     cpp-options: "-DCAIRO"-    build-depends:     diagrams-cairo >= 1.1+    build-depends:     diagrams-cairo >= 1.3   else-    build-depends:     diagrams-svg >= 1.1+    build-depends:     diagrams-svg >= 1.3   hs-source-dirs:      src   ghc-options:         -Wall  executable drawpuzzle   main-is:          drawpuzzle.hs   hs-source-dirs:   src/tools-  build-depends:       base >= 4.2,+  build-depends:       base >= 4.8 && < 5,                        puzzle-draw,-                       diagrams-lib >= 1.2,+                       diagrams-lib >= 1.3,                        yaml >= 0.8.4,-                       optparse-applicative >= 0.7,+                       optparse-applicative >= 0.12,                        aeson >= 0.7,                        filepath >= 1.3   if flag(cairo)     cpp-options: "-DCAIRO"-    build-depends:     diagrams-cairo >= 1.1+    build-depends:     diagrams-cairo >= 1.3   else-    build-depends:     diagrams-svg >= 1.1+    build-depends:     diagrams-svg >= 1.3   ghc-options:         -Wall +executable checkpuzzle+  main-is:          checkpuzzle.hs+  hs-source-dirs:   src/tools+  build-depends:       base >= 4.8 && < 5,+                       puzzle-draw,+                       yaml >= 0.8.4,+                       optparse-applicative >= 0.12,+                       aeson >= 0.7,+                       filepath >= 1.3+  ghc-options:         -Wall+ test-suite test   type:                exitcode-stdio-1.0   hs-source-dirs:      tests   main-is:             tests.hs-  build-depends:       base >= 4.2,+  other-modules:       Data,+                       Util,+                       Data.Puzzles.GridSpec,+                       Data.Puzzles.GridShapeSpec,+                       Diagrams.Puzzles.GridSpec,+                       Text.Puzzles.PuzzleTypesSpec,+                       Text.Puzzles.UtilSpec+  build-depends:       base >= 4.8 && < 5,                        tasty >= 0.8,+                       tasty-hspec,                        tasty-hunit >= 0.8,+                       hspec,                        yaml >= 0.8.4,                        text >= 1.1,                        deepseq >= 1.3,                        containers >= 0.5,                        blaze-svg >= 0.3,-                       diagrams-lib >= 1.2,-                       diagrams-svg >= 1.1,+                       diagrams-lib >= 1.3,+                       diagrams-svg >= 1.3,                        bytestring >= 0.10,                        puzzle-draw+  ghc-options:         -Wall++-- This should depend on the executable `drawpuzzle`+-- and not the library.+executable test-compare+  hs-source-dirs:      tests+  main-is:             compare.hs+  build-depends:       base >= 4.8 && < 5,+                       tasty >= 0.8,+                       tasty-golden >= 2.0,+                       filepath >= 1.3,+                       process >= 1.2   ghc-options:         -Wall
+ src/Data/Puzzles/CmdLine.hs view
@@ -0,0 +1,27 @@+module Data.Puzzles.CmdLine+    ( checkType+    , exitErr+    , readPuzzle+    ) where++import Text.Puzzles.Puzzle+import Data.Puzzles.PuzzleTypes++import qualified Data.Yaml as Y+import System.Exit++checkType :: Maybe String -> IO PuzzleType+checkType mt = do+    t <- maybe errno return mt+    maybe (errunk t) return (lookupType t)+  where+    errno    = exitErr "no puzzle type given"+    errunk t = exitErr $ "unknown puzzle type: " ++ t++readPuzzle :: FilePath -> IO (Either Y.ParseException TypedPuzzle)+readPuzzle = Y.decodeFileEither++exitErr :: String -> IO a+exitErr e = putStrLn e >> exitFailure++
+ src/Data/Puzzles/Code.hs view
@@ -0,0 +1,13 @@+module Data.Puzzles.Code where++import Data.Puzzles.Grid+import Data.Puzzles.GridShape++type Code = [CodePart]++data CodePart =+      Rows'  [Int] -- ^ Rows of cells, counted from the bottom.+    | Cols   [Int] -- ^ Cols of cells, counted from the left.+    | RowsN' [Int] -- ^ Rows of nodes, counted from the bottom.+    | ColsN  [Int] -- ^ Cols of nodes, counted from the left.+    | LabelsN (Grid N (Maybe Char)) -- ^ Nodes, labeld by letters.
src/Data/Puzzles/Compose.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, RankNTypes #-} {-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE TypeFamilies #-}  -- | -- Helpers to string together parser and renderer by puzzle type.@@ -8,18 +9,11 @@     PuzzleHandler,     handle,     -- * Handlers-    drawPuzzle,-    drawPuzzleSol,     drawPuzzleMaybeSol,-    drawPuzzle',-    drawSolution',-    drawExample'   ) where -import Data.Maybe import Diagrams.Prelude import Data.Yaml (Parser, Value)-import Data.Traversable (traverse)  import Text.Puzzles.Puzzle import Diagrams.Puzzles.Draw@@ -41,6 +35,7 @@         PuzzleHandler b a -> PuzzleType -> a handle f LITS                 = f R.lits                D.lits handle f LITSPlus             = f R.litsplus            D.litsplus+handle f LITSSym              = f R.lits                D.litssym handle f Geradeweg            = f R.geradeweg           D.geradeweg handle f Fillomino            = f R.fillomino           D.fillomino handle f Masyu                = f R.masyu               D.masyu@@ -69,55 +64,60 @@ handle f PrimePlace           = f R.primeplace          D.primeplace handle f Labyrinth            = f R.labyrinth           D.labyrinth handle f Bahnhof              = f R.bahnhof             D.bahnhof+handle f BlackoutDominos      = f R.blackoutDominos     D.blackoutDominos+handle f TwilightTapa         = f R.tapa                D.tapa+handle f TapaCave             = f R.tapa                D.tapa+handle f DominoPillen         = f R.dominoPills         D.dominoPills+handle f SlitherLinkMulti     = f R.slithermulti        D.slithermulti+handle f AngleLoop            = f R.angleLoop           D.angleLoop+handle f Shikaku              = f R.shikaku             D.shikaku+handle f SlovakSums           = f R.slovaksums          D.slovaksums+handle f Anglers              = f R.anglers             D.anglers+handle f Dominos              = f R.dominos             D.dominos+handle f FillominoCheckered   = f R.fillomino           D.fillominoCheckered+handle f FillominoLoop        = f R.fillominoLoop       D.fillominoLoop handle f Cave                 = f R.cave                D.cave---- | Handler that parses a puzzle from a YAML value, and renders.-drawPuzzle :: PuzzleHandler b (Value -> Parser (Diagram b R2))-drawPuzzle (pp, _) (dp, _) p = do-    p' <- pp p-    return $ dp p'---- | Handler that parses puzzle and solution from a pair of corresponding---   YAML values, and renders both individually.-drawPuzzleSol :: PuzzleHandler b ((Value, Value)-                 -> Parser (Diagram b R2, Diagram b R2))-drawPuzzleSol (pp, ps) (dp, ds) (p, s) = do-    p' <- pp p-    s' <- ps s-    return (dp p', ds (p', s'))+handle f Numberlink           = f R.numberlink          D.numberlink+handle f Skyscrapers          = f R.skyscrapers         D.skyscrapers+handle f SkyscrapersStars     = f R.skyscrapersStars    D.skyscrapersStars+handle f SkyscrapersFrac      = f R.tightfitskyscrapers D.tightfitskyscrapers+handle f TurningFences        = f R.slither             D.slither+handle f Summon               = f R.summon              D.summon+handle f Baca                 = f R.baca                D.baca+handle f Buchstabensalat      = f R.buchstabensalat     D.buchstabensalat+handle f Doppelblock          = f R.doppelblock         D.doppelblock+handle f SudokuDoppelblock    = f R.sudokuDoppelblock   D.sudokuDoppelblock+handle f Loopki               = f R.loopki              D.loopki+handle f Scrabble             = f R.scrabble            D.scrabble+handle f Neighbors            = f R.neighbors           D.neighbors+handle f Starwars             = f R.starwars            D.starwars+handle f Starbattle           = f R.starbattle          D.starbattle+handle f Heyawake             = f R.heyawake            D.heyawake+handle f Wormhole             = f R.wormhole            D.wormhole+handle f Pentominous          = f R.pentominous         D.pentominous+handle f ColorAkari           = f R.colorakari          D.colorakari+handle f PersistenceOfMemory  = f R.persistenceOfMemory D.persistenceOfMemory+handle f ABCtje               = f R.abctje              D.abctje+handle f Kropki               = f R.kropki              D.kropki+handle f StatuePark           = f R.statuepark          D.statuepark+handle f PentominousBorders   = f R.pentominousBorders  D.pentominousBorders+handle f NanroSignpost        = f R.nanroSignpost       D.nanroSignpost+handle f TomTom               = f R.tomTom              D.tomTom+handle f HorseSnake           = f R.horseSnake          D.horseSnake+handle f Illumination         = f R.illumination        D.illumination+handle f Pentopia             = f R.pentopia            D.pentopia+handle f PentominoPipes       = f R.pentominoPipes      D.pentominoPipes+handle f GreaterWall          = f R.greaterWall         D.greaterWall+handle f Galaxies             = f R.galaxies            D.galaxies  -- | Handler that parses puzzle and an optional solution from a pair of --   corresponding YAML values, and renders both individually, optionally --   for the solution. drawPuzzleMaybeSol :: PuzzleHandler b ((Value, Maybe Value)-                      -> Parser (Diagram b R2, Maybe (Diagram b R2)))+                      -> Parser (Diagram b, Maybe (Diagram b))) drawPuzzleMaybeSol (pp, ps) (dp, ds) (p, s) = do     p' <- pp p     s' <- traverse ps s     let mps = case s' of Nothing  -> Nothing                          Just s'' -> Just (p', s'')     return (dp p', ds <$> mps)---- | Variant of 'drawPuzzle' that accepts a pair of puzzle YAML value and---   optional solution YAML value.-drawPuzzle' :: PuzzleHandler b ((Value, Maybe Value) -> Parser (Diagram b R2))-drawPuzzle' (pp, _) (dp, _) (p, _) = do-    p' <- pp p-    return $ dp p'---- | Handler that accepts a pair of puzzle YAML value and optional solution---   YAML value, and renders the solution, failing if the solution is not---   provided.-drawSolution' :: PuzzleHandler b ((Value, Maybe Value) -> Parser (Diagram b R2))-drawSolution' (pp, ps) (_, ds) (p, ms) = do-    p' <- pp p-    s' <- maybe (fail "no solution provided") ps ms-    return $ ds (p', s')---- | Like 'drawSolution'', but renders puzzle and solution in example layout.-drawExample' :: Backend' b =>-                PuzzleHandler b ((Value, Maybe Value) -> Parser (Diagram b R2))-drawExample' (pp, ps) (dp, ds) (p, ms) = do-    p' <- pp p-    s' <- maybe (fail "no solution provided") ps ms-    return . fromJust $ draw (dp p', Just $ ds (p', s')) DrawExample
src/Data/Puzzles/Elements.hs view
@@ -1,7 +1,7 @@ -- | Types for a variety of puzzle elements. module Data.Puzzles.Elements where -import Data.Puzzles.GridShape (Coord, Edge)+import Data.Puzzles.GridShape  type Clue a = Maybe a @@ -19,6 +19,8 @@  type CompassClue = Clue CompassC +data SlovakClue = SlovakClue !Int !Int+ -- | A cell that is optionally bisected by a diagonal --   (up-right or down-right). data Tightfit a = Single a | UR a a | DR a a@@ -29,18 +31,25 @@               show' (UR x y)   = show x ++ "/" ++ show y               show' (DR x y)   = show x ++ "\\" ++ show y +-- | A marked line in a grid, given by start and end points.+data MarkedLine a = MarkedLine a a+ -- | A marked word in a letter grid, by its start and end --   coordinates. data MarkedWord = MW { mwstart :: Coord, mwend :: Coord }  -- | A loop of edges.-type Loop = [Edge]+type Loop a = [Edge a] +-- | A loop consisting of straight segments of arbitrary+-- angles between vertices.+type VertexLoop = [N]+ -- | A thermometer, as a list of coordinates from bulb to end. --   There should be at least two entries, entries should be distinct, --   and successive entries should be neighbours (diagonal neighbours --   are fine).-type Thermometer = [Coord]+type Thermometer = [C]  -- | A forward or backward diagonal as occurring in the solution --   of a slalom puzzle.@@ -62,7 +71,32 @@ newtype PrimeDiag = PrimeDiag (Bool, Bool)  data Black = Black-+data Fish = Fish+data Star = Star data Crossing = Crossing  type BahnhofClue = Either Int Crossing++data DigitRange = DigitRange !Int !Int+    deriving (Show, Eq)++digitList :: DigitRange -> [Int]+digitList (DigitRange a b) = [a..b]++data MEnd = MEnd++data Fraction =+    FComp String String String  -- a b/c+  | FFrac String String         -- a/b+  | FInt String                 -- a++data PlainNode = PlainNode++type Myopia = [Dir']++data Relation = RGreater | RLess | REqual | RUndetermined+    deriving (Show, Eq)++type GreaterClue = [Relation]++data GalaxyCentre = GalaxyCentre
src/Data/Puzzles/Grid.hs view
@@ -1,166 +1,207 @@-{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts,-             GADTs, StandaloneDeriving #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ConstraintKinds #-}  -- | Puzzle grids.-module Data.Puzzles.Grid where+module Data.Puzzles.Grid+    (+      Grid+    , AreaGrid+    , ShadedGrid+    , nodes+    , size+    , sizeGrid+    , clues+    , nodeGrid+    , cellGrid -import Data.Maybe+    , dominoGrid++    , borders+    , edgesGen+    , colour+    , collectLines++    , OutsideClues(..)+    , outsideSize+    , outsideClues+    , multiOutsideClues+    , outsideGrid+    ) where+ import qualified Data.Map as Map-import Data.Foldable (Foldable, foldMap)-import Data.Traversable (Traversable, traverse)-import Control.Applicative ((<$>))+import qualified Data.Set as Set+import Data.AffineSpace import Data.VectorSpace+import Control.Monad.State -import Data.Puzzles.GridShape hiding (size, cells)-import qualified Data.Puzzles.GridShape as GS import Data.Puzzles.Elements+import Data.Puzzles.GridShape --- | A generic grid, with the given shape and contents.-data Grid s a where-    Grid :: { shape :: s-            , contents :: Map.Map (Cell s) a} -> Grid s a+type Grid k a = Map.Map k a -deriving instance (Show a, Show s, GridShape s) => Show (Grid s a)-deriving instance (Eq s, Eq (Cell s), Eq a) => Eq (Grid s a)+type AreaGrid = Grid C Char+type ShadedGrid = Grid C Bool --- | Standard square grid.-type SGrid = Grid Square+-- | For a grid with value type @Maybe a@, return an association+--   list of cells and @Just@ values.+clues :: Grid k (Maybe a) -> Grid k a+clues = Map.mapMaybe id -type CharGrid = SGrid Char-type AreaGrid = CharGrid-type ShadedGrid = SGrid Bool-type CharClueGrid = SGrid (Maybe Char)-type IntGrid = SGrid (Clue Int)+edgesGen :: Dual' k+         => (a -> a -> Bool) -> (a -> Bool) -> Map.Map k a -> [Edge (Dual k)]+edgesGen p n m = filter (uncurry p' . ends . dualE) es+  where+    (outer, inner) = edgesM m+    es = map unorient outer ++ inner+    p' c d = p'' (Map.lookup c m)+                 (Map.lookup d m)+    p'' (Just e) (Just f) = p e f+    p'' (Just e) Nothing  = n e+    p'' Nothing (Just e)  = n e+    p'' _        _        = False --- | Lookup a grid value at a given cell. Unsafe.-(!) :: (GridShape s, Ord (Cell s)) => Grid s a -> Cell s -> a-(!) (Grid _ m) = (m Map.!) -instance Functor (Grid s) where-    fmap f (Grid s m) = Grid s (fmap f m)+nodes :: Grid N a -> Set.Set N+nodes = Map.keysSet -instance Foldable (Grid s) where-    foldMap f (Grid _ m) = foldMap f m+-- | The inner edges of a grid that separate unequal cells.+borders :: Eq a => Grid C a -> [Edge N]+borders = edgesGen (/=) (const False) -instance Traversable (Grid s) where-    traverse f (Grid s m) = Grid s <$> traverse f m+corners :: C -> [N]+corners c = map (.+^ (c .-. C 0 0)) [N 0 0, N 1 0, N 0 1, N 1 1] -filterG :: (a -> Bool) -> Grid s a -> Grid s a-filterG p (Grid s m) = Grid s (Map.filter p m)+-- | A grid of empty nodes with all nodes of the cells of the+-- first grid.+nodeGrid :: Grid C a -> Grid N ()+nodeGrid = Map.unions . map cornersM . Map.keys+  where+    cornersM = Map.fromList . map (flip (,) ()) . corners --- | Initialize a square grid from a list of lists. The grid---   might be incomplete if some rows are shorter.-fromListList :: [[a]] -> Grid Square a-fromListList g = Grid s m+cellGrid :: Grid N a -> Grid C ()+cellGrid m = Map.fromList+           . map (flip (,) ())+           . filter (all (`Map.member` m) . corners)+           . map cellUpRight+           . Map.keys+           $ m   where-    w = maximum . map length $ g-    h = length g-    s = Square w h-    m = Map.fromList . concat-      . zipWith (\y -> zipWith (\x -> (,) (x, y)) [0..]) [h-1,h-2..]-      $ g+    cellUpRight :: N -> C+    cellUpRight = fromCoord . toCoord -size :: GridShape s => Grid s a -> GridSize s-size = GS.size . shape+-- | Colour a graph.+colourM :: (Ord k, Eq a) => (k -> [k]) -> Map.Map k a -> Map.Map k Int+colourM nbrs m = fmap fromRight . execState colour' $ start+  where+    fromRight (Right r) = r+    fromRight (Left _)  = error "expected Right" -cells :: GridShape s => Grid s a -> [Cell s]-cells = Map.keys . contents+    start = fmap (const $ Left [1..]) m+    colour' = mapM_ pickAndFill (Map.keys m) -inBounds :: (GridShape s, Eq (Cell s)) => Grid s a -> Cell s -> Bool-inBounds g c = c `elem` cells g+    -- choose a colour for the given node, and spread it to+    -- equal neighbours, removing it from unequal neighbours+    pickAndFill x = do+        v <- (Map.! x) <$> get+        case v of+            Left (c:_) -> fill (m Map.! x) c x+            Left _     -> error "empty set of candidates"+            Right _    -> return () --- | For a grid with value type @Maybe a@, return an association---   list of cells and @Just@ values.-clues :: GridShape s => Grid s (Maybe a) -> [(Cell s, a)]-clues g = [ (k, v) | (k, Just v) <- values g ]+    fill a c x = do+        v <- (Map.! x) <$> get+        case v of+            Left _     -> if m Map.! x == a+                    then do modify (Map.insert x (Right c))+                            mapM_ (fill a c) (nbrs x)+                    else modify (del x c)+            Right _    -> return () --- | Association list of cells and values.-values :: GridShape s => Grid s a -> [(Cell s, a)]-values (Grid _ m) = Map.toList m+    -- remove the given colour from the list of candidates+    del x c = Map.adjust f x+      where+        f (Left cs) = Left $ filter (/= c) cs+        f (Right c') = Right c' -edgesGen :: (a -> a -> Bool) -> (a -> Bool) -> Grid Square a -> [Edge]-edgesGen p n g = [ E pt V | pt <- vedges ] ++ [ E pt H | pt <- hedges ]+colour :: Eq a => Grid C a -> Grid C Int+colour m = colourM edgeNeighbours' m   where-    edges' f (sx, sy) = [ (x + 1, y) | x <- [-1 .. sx - 1]-                                     , y <- [-1 .. sy]-                                     , p' (f (x, y)) (f (x + 1, y)) ]+    edgeNeighbours' p = [ q | q <- edgeNeighbours p+                            , q `Map.member` m ] -    vedges = edges' id (size g)-    hedges = map swap $ edges' swap (swap . size $ g)-    swap (x, y) = (y, x)-    p' c d = p'' (Map.lookup c (contents g))-                 (Map.lookup d (contents g))-    p'' (Just e) (Just f) = p e f-    p'' (Just e) Nothing  = n e-    p'' Nothing (Just e)  = n e-    p'' _        _        = False+-- | Clues along the outside of a square grid.+-- Ordered such that coordinates increase.+data OutsideClues k a = OC { left :: [a], right :: [a], bottom :: [a], top :: [a] }+    deriving (Show, Eq) -edgesP :: (a -> a -> Bool) -> Grid Square a -> [Edge]-edgesP p g = edgesGen p (const False) g+instance Functor (OutsideClues k) where+    fmap f (OC l r b t) = OC (fmap f l) (fmap f r) (fmap f b) (fmap f t) -dualEdgesP :: (a -> a -> Bool) -> Grid Square a -> [Edge]-dualEdgesP p g = [ E pt H | pt <- hedges ] ++-                 [ E pt V | pt <- vedges ]+outsideSize :: OutsideClues k a -> Size+outsideSize (OC l r b t) = (w, h)   where-    edges' f (sx, sy) = [ (x, y) | x <- [0 .. sx - 2]-                                 , y <- [0 .. sy - 1]-                                 , p' (f (x, y)) (f (x + 1, y)) ]+    w = max (length t) (length b)+    h = max (length l) (length r) -    hedges = edges' id (size g)-    vedges = map swap $ edges' swap (swap . size $ g)-    swap (x, y) = (y, x)-    p' c d = p'' (Map.lookup c (contents g))-                 (Map.lookup d (contents g))-    p'' (Just e) (Just f) = p e f-    p'' _        _        = False+-- | Create a dummy grid matching the given outside clues in size.+outsideGrid :: (Ord k, FromCoord k) => OutsideClues k a -> Grid k ()+outsideGrid = sizeGrid . outsideSize --- | The inner edges of a grid that separate unequal cells.-borders :: Eq a => Grid Square a -> [Edge]-borders = edgesP (/=)+-- | Create a dummy grid of the given size.+sizeGrid :: (Ord k, FromCoord k) => Size -> Grid k ()+sizeGrid (w, h) = Map.mapKeys fromCoord+                . Map.fromList+                $ [ ((x, y), ()) | x <- [0..w-1], y <- [0..h-1] ] --- | Clues along the outside of a square grid.-data OutsideClues a = OC { left :: [a], right :: [a], bottom :: [a], top :: [a] }-    deriving (Show, Eq)+data OClue = OClue+    { ocBase :: (Int, Int)+    , _ocDir :: (Int, Int)+    }+  deriving (Show, Eq, Ord) -instance Functor OutsideClues where-    fmap f (OC l r b t) = OC (fmap f l) (fmap f r) (fmap f b) (fmap f t)+oClues :: OutsideClues k a -> Map.Map OClue a+oClues ocs@(OC l r b t) = Map.fromList . concat $+    [ zipWith (\y c -> (OClue (-1, y) (-1, 0), c)) [0..h-1] l+    , zipWith (\y c -> (OClue ( w, y) ( 1, 0), c)) [0..h-1] r+    , zipWith (\x c -> (OClue ( x,-1) ( 0,-1), c)) [0..w-1] b+    , zipWith (\x c -> (OClue ( x, h) ( 0, 1), c)) [0..w-1] t+    ]+  where+    (w, h) = outsideSize ocs -outsideSize :: OutsideClues a -> (Int, Int)-outsideSize (OC l r b t) = ( max (length t) (length b)-                           , max (length l) (length r)-                           )+outsideClues :: (Ord k, FromCoord k) => OutsideClues k a -> Map.Map k a+outsideClues = Map.mapKeys (fromCoord . ocBase) . oClues -data OutsideClue a = OClue-    { ocBase  :: (Int, Int)-    , ocDir   :: (Int, Int)-    , ocValue :: a-    }+multiOutsideClues :: (Ord k, FromCoord k) => OutsideClues k [a] -> Map.Map k a+multiOutsideClues = Map.mapKeys fromCoord+                  . Map.fromList . concatMap distrib . Map.toList+                  . oClues+  where+    distrib (OClue o d, xs) = zip [o ^+^ i *^ d | i <- [0..]] xs -instance Functor OutsideClue where-    fmap f (OClue b d x) = OClue b d (f x)+dualEdgesP :: Key k+           => (a -> a -> Bool) -> Grid k a -> [Edge k]+dualEdgesP p m = concatMap f (Map.keys m)+  where+    f c = [ edge c d | d <- map (c .+^) [(0,1), (1,0)]+                     , d `Map.member` m && p (m Map.! c) (m Map.! d) ] -outsideClueList :: OutsideClues a -> [OutsideClue a]-outsideClueList o@(OC l r b t) =-    concat-       [ zipWith (\ y c -> OClue (0,y)   (-1, 0) c) [0..h-1] l-       , zipWith (\ y c -> OClue (w-1,y) ( 1, 0) c) [0..h-1] r-       , zipWith (\ x c -> OClue (x,0)   ( 0,-1) c) [0..w-1] b-       , zipWith (\ x c -> OClue (x,h-1) ( 0, 1) c) [0..w-1] t-       ]+collectLines :: (Key k, Eq a) => Grid k (Maybe a) -> [Edge k]+collectLines = dualEdgesP eq   where-    (w, h) = outsideSize o+    eq (Just x) (Just y) = x == y+    eq _        _        = False --- | Convert outside clues to association list mapping coordinate to value.-outsideClues :: OutsideClues (Maybe a) -> [((Int, Int), a)]-outsideClues = mapMaybe (liftMaybe . toCell) . outsideClueList+dominoGrid :: DigitRange -> Grid C (Int, Int)+dominoGrid (DigitRange x y) =+    Map.mapKeys fromCoord . Map.fromList $+        [ ((a, s - b), (b + x, a + x))+        | a <- [0..s], b <- [0..s], b <= a ]   where-    toCell (OClue (bx, by) (dx, dy) v) = ((bx + dx, by + dy), v)-    liftMaybe (p, Just x)  = Just (p, x)-    liftMaybe (_, Nothing) = Nothing+    s = y - x -multiOutsideClues :: OutsideClues [a] -> [((Int, Int), a)]-multiOutsideClues = concatMap distrib . outsideClues . fmap Just . dired+size :: Grid Coord a -> Size+size m = foldr (both max) (0, 0) (Map.keys m) ^+^ (1, 1)   where-    dired (OC l r b t) = OC (z (-1,0) l) (z (1,0) r) (z (0,-1) b) (z (0,1) t)-    z x ys = zip (repeat x) ys-    distrib (o, (d, xs)) = zip [o ^+^ i *^ d | i <- [0..]] xs+    both f (x, y) (x', y') = (f x x', f y y')
src/Data/Puzzles/GridShape.hs view
@@ -1,100 +1,219 @@-{-# LANGUAGE TypeFamilies, FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ConstraintKinds #-}  -- | Grid shapes.-module Data.Puzzles.GridShape where+module Data.Puzzles.GridShape+    (+      Coord+    , Size+    , Square(..)+    , Dir(..)+    , Edge(..)+    , Dir'(..)+    , Edge'(..)+    , Dual2D(..)+    , Key+    , Dual'+    , C(..)+    , N(..)+    , FromCoord(..)+    , ToCoord(..) -import Data.Foldable (Foldable)+    , edge+    , edge'+    , edgeBetween+    , edgeBetween'+    , orient+    , ends'+    , revEdge+    , edges+    , edgesM+    , ends+    , edgeSize+    , unorient+    , dualE+    , vertexNeighbours+    , edgeNeighbours+    ) where+ import qualified Data.Foldable as F import Data.List (partition)-import Data.VectorSpace ((^+^))+import qualified Data.Map as Map+import Data.AffineSpace --- | The geometry of a grid.-class Show (Cell a) => GridShape a where-    type GridSize a :: *-    type Cell     a :: *-    type Vertex   a :: *+type Coord = (Int, Int) -    size :: a -> GridSize a-    cells :: a -> [Cell a]-    vertices :: a -> [Vertex a]-    vertexNeighbours :: a -> Cell a -> [Cell a]-    edgeNeighbours :: a -> Cell a -> [Cell a]+class FromCoord a where+    fromCoord :: Coord -> a +class ToCoord a where+    toCoord :: a -> Coord++data C = C !Int !Int+    deriving (Show, Eq, Ord)++instance FromCoord C where+    fromCoord = uncurry C++instance ToCoord C where+    toCoord (C x y) = (x, y)++instance AffineSpace C where+    type Diff C = (Int, Int)++    (C x y) .-. (C x' y') = (x - x', y - y')+    (C x y) .+^ (x',y') = C (x + x') (y + y')++data N = N !Int !Int+    deriving (Show, Eq, Ord)++instance FromCoord N where+    fromCoord = uncurry N++instance ToCoord N where+    toCoord (N x y) = (x, y)++instance AffineSpace N where+    type Diff N = (Int, Int)++    (N x y) .-. (N x' y') = (x - x', y - y')+    (N x y) .+^ (x',y') = N (x + x') (y + y')+ -- | A standard square grid, with cells and vertices --   indexed by pairs of integers in mathematical coordinates. --   The bottom-left corner is vertex (0, 0), the bottom-left --   cell is cell (0, 0).-data Square = Square !Int !Int+data Square = Square     deriving (Show, Eq) -squareNeighbours :: [(Int, Int)] -> Square -> Cell Square -> [Cell Square]-squareNeighbours deltas (Square w h) c = filter inBounds . map (add c) $ deltas-  where-    inBounds (x, y) = x >= 0 && x < w && y >= 0 && y < h-    add (x, y) (x', y') = (x + x', y + y')+squareNeighbours :: [(Int,Int)] -> C -> [C]+squareNeighbours deltas c = map (c .+^) deltas -instance GridShape Square where-    type GridSize Square = (Int, Int)-    type Cell Square     = (Int, Int)-    type Vertex Square   = (Int, Int)+vertexNeighbours :: C -> [C]+vertexNeighbours = squareNeighbours [ (dx, dy)+                                    | dx <- [-1..1], dy <- [-1..1]+                                    , dx /= 0 || dy /= 0+                                    ] -    size (Square w h)       = (w, h)-    cells (Square w h)      = [(x, y) | x <- [0..w-1], y <- [0..h-1]]-    vertices (Square w h)   = [(x, y) | x <- [0..w], y <- [0..h]]-    vertexNeighbours = squareNeighbours [ (dx, dy)-                                        | dx <- [-1..1], dy <- [-1..1]-                                        , dx /= 0 || dy /= 0-                                        ]-    edgeNeighbours = squareNeighbours [ (dx, dy)-                                      | dx <- [-1..1], dy <- [-1..1]-                                      , dx /= 0 || dy /= 0-                                      , dx == 0 || dy == 0-                                      ]+edgeNeighbours :: C -> [C]+edgeNeighbours = squareNeighbours [ (1, 0), (-1,0), (0,1), (0,-1) ]  -- | Edge direction in a square grid, vertical or horizontal.-data Dir = V | H+data Dir = Vert | Horiz     deriving (Eq, Ord, Show)  -- | An edge in a square grid, going up or right from the given cell --   centre.-data Edge = E (Cell Square) Dir+data Edge a = E a Dir     deriving (Show, Eq, Ord) -type Coord = Cell Square-type Size = GridSize Square+type Size = (Int, Int)  -- | Oriented edge direction in a square grid. data Dir' = U | D | L | R     deriving (Eq, Ord, Show) +toDir' :: (Int, Int) -> Dir'+toDir' ( 1, 0) = R+toDir' ( 0, 1) = U+toDir' (-1, 0) = L+toDir' ( 0,-1) = D+toDir' _       = error "non-primitive vector"+ -- | An oriented edge in a square grid.---   @a@ should be @Cell Square@ or @Vertex Square@. data Edge' a = E' a Dir'     deriving (Eq, Ord, Show) --- | The edge between two neighbouring cells, with the first cell---   on the left.-orientedEdge :: Cell Square -> Cell Square -> Edge' (Vertex Square)-orientedEdge (x,y) (x',y')-    | x' == x && y' == y+1  = E' (x,y+1) R-    | x' == x && y' == y-1  = E' (x+1,y) L-    | x' == x+1 && y' == y  = E' (x+1,y+1) D-    | x' == x-1 && y' == y  = E' (x,y) U-    | otherwise             = error $ "not neighbours: " ++-                                      show (x,y) ++ " " ++  show (x',y')+edge' :: (AffineSpace a, Diff a ~ (Int, Int)) => a -> a -> Edge' a+edge' p q = E' p (toDir' (q .-. p)) +edge :: (AffineSpace a, Diff a ~ (Int, Int)) => a -> a -> Edge a+edge p q = unorient $ edge' p q++class Dual2D a where+    type Dual a :: *++    dualE' :: Edge' a -> Edge' (Dual a)++dualE :: Dual' a => Edge a -> Edge (Dual a)+dualE = unorient . dualE' . orient++type Key k = (AffineSpace k, Diff k ~ (Int, Int), Ord k, FromCoord k)+type Dual' k = (Key k, Dual2D k, Key (Dual k), Dual2D (Dual k),+                Dual (Dual k) ~ k)++instance Dual2D N where+    type Dual N = C++    dualE' (E' (N x y) R) = E' (C x (y-1)) U+    dualE' (E' (N x y) U) = E' (C x y) L+    dualE' (E' (N x y) L) = E' (C (x-1) y) D+    dualE' (E' (N x y) D) = E' (C (x-1) (y-1)) R++instance Dual2D C where+    type Dual C = N++    dualE' (E' (C x y) R) = E' (N (x+1) y) U+    dualE' (E' (C x y) U) = E' (N (x+1) (y+1)) L+    dualE' (E' (C x y) L) = E' (N x (y+1)) D+    dualE' (E' (C x y) D) = E' (N x y) R+++ends :: (AffineSpace a, Diff a ~ (Int, Int)) => Edge a -> (a, a)+ends (E x Vert) = (x, x .+^ (0, 1))+ends (E x Horiz) = (x, x .+^ (1, 0))++ends' :: (AffineSpace a, Diff a ~ (Int, Int)) => Edge' a -> (a, a)+ends' (E' x U) = (x, x .+^ (0, 1))+ends' (E' x R) = (x, x .+^ (1, 0))+ends' (E' x D) = (x, x .+^ (0, -1))+ends' (E' x L) = (x, x .+^ (-1, 0))++edgeSize :: [Edge N] -> Size+edgeSize es = (maximum (map fst ps), maximum (map snd ps))+  where+      ps = map toCoord+         . concatMap ((\(x, y) -> [x, y]) . ends)+         $ es++revEdge :: (AffineSpace a, Diff a ~ (Int, Int)) => Edge' a -> Edge' a+revEdge = uncurry edge' . swap . ends'+  where+    swap (x, y) = (y, x)++unorient :: (AffineSpace a, Diff a ~ (Int, Int)) => Edge' a -> Edge a+unorient (E' x U) = E x Vert+unorient (E' x R) = E x Horiz+unorient (E' x D) = E (x .-^ (0,1)) Vert+unorient (E' x L) = E (x .-^ (1,0)) Horiz++orient :: Edge a -> Edge' a+orient (E x Vert)  = E' x U+orient (E x Horiz) = E' x R++edgeBetween' :: Dual' k => k -> k -> Edge' (Dual k)+edgeBetween' p q = dualE' (edge' p q)++edgeBetween :: Dual' k => k -> k -> Edge (Dual k)+edgeBetween p q = unorient $ edgeBetween' p q+ -- | @edges@ computes the outer and inner edges of a set of cells. --   The set is given via fold and membership predicate, the result --   is a pair @(outer, inner)@ of lists of edges, where the outer --   edges are oriented such that the outside is to the left.-edges :: Foldable f =>-           f (Cell Square) -> (Cell Square -> Bool) ->-           ([Edge' (Vertex Square)], [Edge' (Vertex Square)])+edges :: (Dual' k, Foldable f) =>+         f k -> (k -> Bool) ->+         ([Edge' (Dual k)], [Edge (Dual k)]) edges cs isc = F.foldr f ([], []) cs   where     f c (outer, inner) = (newout ++ outer, newin ++ inner)       where-        nbrs = [ c ^+^ d | d <- [(-1,0), (0,1), (1,0), (0,-1)] ]+        nbrs = [ c .+^ d | d <- [(-1,0), (0,1), (1,0), (0,-1)] ]         (ni, no) = partition isc nbrs-        newout = map (orientedEdge c) no-        newin = map (orientedEdge c) . filter (c >=) $ ni+        newout = [ edgeBetween' q c | q <- no ]+        newin  = [ edgeBetween q c | q <- ni, c >= q ]++edgesM :: Dual' k+       => Map.Map k a -> ([Edge' (Dual k)], [Edge (Dual k)])+edgesM m = edges (Map.keysSet m) (`Map.member` m)
src/Data/Puzzles/PuzzleTypes.hs view
@@ -14,6 +14,7 @@ -- | The list of specific puzzle types we can handle. data PuzzleType = LITS                 | LITSPlus+                | LITSSym                 | Geradeweg                 | Fillomino                 | Masyu@@ -42,12 +43,57 @@                 | PrimePlace                 | Labyrinth                 | Bahnhof+                | BlackoutDominos+                | TwilightTapa+                | TapaCave+                | DominoPillen+                | SlitherLinkMulti+                | AngleLoop+                | Shikaku+                | SlovakSums+                | Anglers+                | Dominos+                | FillominoCheckered+                | FillominoLoop                 | Cave+                | Numberlink+                | Skyscrapers+                | SkyscrapersStars+                | SkyscrapersFrac+                | TurningFences+                | Summon+                | Baca+                | Buchstabensalat+                | Doppelblock+                | SudokuDoppelblock+                | Loopki+                | Scrabble+                | Neighbors+                | Starwars+                | Starbattle+                | Heyawake+                | Wormhole+                | Pentominous+                | ColorAkari+                | PersistenceOfMemory+                | ABCtje+                | Kropki+                | StatuePark+                | PentominousBorders+                | NanroSignpost+                | TomTom+                | HorseSnake+                | Illumination+                | Pentopia+                | PentominoPipes+                | GreaterWall+                | Galaxies     deriving (Show, Eq)  typeNames :: [(PuzzleType, String)] typeNames = [ (LITS, "lits")             , (LITSPlus, "litsplus")+            , (LITSSym, "lits-symmetry")             , (Geradeweg, "geradeweg")             , (Fillomino, "fillomino")             , (Masyu, "masyu")@@ -76,7 +122,51 @@             , (PrimePlace, "primeplace")             , (Labyrinth, "magiclabyrinth")             , (Bahnhof, "bahnhof")+            , (BlackoutDominos, "blackout-dominos")+            , (TwilightTapa, "twilight-tapa")+            , (TapaCave, "tapa-cave")+            , (DominoPillen, "domino-pillen")+            , (SlitherLinkMulti, "slitherlink-multi")+            , (AngleLoop, "angleloop")+            , (Shikaku, "shikaku")+            , (SlovakSums, "slovaksums")+            , (Anglers, "anglers")+            , (Dominos, "dominos")+            , (FillominoCheckered, "fillomino-checkered")+            , (FillominoLoop, "fillomino-loop")             , (Cave, "cave")+            , (Numberlink, "numberlink")+            , (Skyscrapers, "skyscrapers")+            , (SkyscrapersStars, "skyscrapers-doppelstern")+            , (SkyscrapersFrac, "skyscrapers-fractional")+            , (TurningFences, "turning-fences")+            , (Summon, "summon")+            , (Baca, "baca")+            , (Buchstabensalat, "buchstabensalat")+            , (Doppelblock, "doppelblock")+            , (SudokuDoppelblock, "sudoku-doppelblock")+            , (Loopki, "loopki")+            , (Scrabble, "scrabble")+            , (Neighbors, "neighbors")+            , (Starwars, "starwars")+            , (Starbattle, "starbattle")+            , (Heyawake, "heyawake")+            , (Wormhole, "wormhole")+            , (Pentominous, "pentominous")+            , (ColorAkari, "color-akari")+            , (PersistenceOfMemory, "persistenceofmemory")+            , (ABCtje, "abctje")+            , (Kropki, "kropki")+            , (StatuePark, "statuepark")+            , (PentominousBorders, "pentominous-borders")+            , (NanroSignpost, "nanro-signpost")+            , (TomTom, "tomtom")+            , (HorseSnake, "horsesnake")+            , (Illumination, "illumination")+            , (Pentopia, "pentopia")+            , (PentominoPipes, "pentomino-pipes")+            , (GreaterWall, "greaterwall")+            , (Galaxies, "galaxies")             ]  -- | Look up a puzzle type by name.
src/Data/Puzzles/Sudoku.hs view
@@ -3,8 +3,10 @@     sudokubordersg   ) where +import qualified Data.Map as Map+ import Data.Puzzles.Grid-import Data.Puzzles.GridShape hiding (size)+import Data.Puzzles.GridShape  msqrt :: Integral a => a -> Maybe a msqrt x = if r ^ (2 :: Int) == x then Just r else Nothing@@ -15,22 +17,20 @@  -- | Determine the internal borders of a standard sudoku of the --   given size.-sudokuborders :: Int -> [Edge]+sudokuborders :: Int -> [Edge N] sudokuborders s =     case msqrt s of         Just r  -> squareborders r         Nothing -> case mhalf s of                        Just h  -> rectborders h                        Nothing -> error "no sudoku layout of this size"-    where squareborders r = [ E (r*x, y) V | x <- [1..r-1], y <- [0..r*r-1] ]-                            ++ [ E (x, r*y) H | x <- [0..r*r-1], y <- [1..r-1] ]-          rectborders h = [ E (h, y) V | y <- [0..2*h-1] ]-                          ++ [ E (x, 2*y) H | x <- [0..2*h-1], y <- [1..h-1] ]+    where squareborders r = [ E (N (r*x) y) Vert  | x <- [1..r-1], y <- [0..r*r-1] ]+                         ++ [ E (N x (r*y)) Horiz | x <- [0..r*r-1], y <- [1..r-1] ]+          rectborders h = [ E (N h y) Vert | y <- [0..2*h-1] ]+                       ++ [ E (N x (2*y)) Horiz | x <- [0..2*h-1], y <- [1..h-1] ]  -- | Determine the internal borders of a standard sudoku of the --   on the given grid.-sudokubordersg :: SGrid a -> [Edge]-sudokubordersg g = sudokuborders s-   where (w, h) = size g-         s | w == h    = w-           | otherwise = error "non-square sudoku grid?"+sudokubordersg :: Grid C a -> [Edge N]+sudokubordersg = sudokuborders+               . fst . size . Map.mapKeys toCoord
+ src/Data/Puzzles/Util.hs view
@@ -0,0 +1,32 @@+module Data.Puzzles.Util+    ( paths+    , loops+    ) where++import Data.List (partition)+import Control.Monad (guard)++paths :: Eq a => [(a, a)] -> Maybe [[a]]+paths [] = Just []+paths ((a,b):segs) = do+    (p, segs') <- collect b segs+    (p', segs'') <- collect a segs'+    ps <- paths segs''+    return ((reverse p' ++ p) : ps)+  where+    collect x sgs = case withx of+        []      -> Just ([x], withoutx)+        [(y,z)] -> do+            (xs, sgs') <- collect (if x == y then z else y) withoutx+            Just (x:xs, sgs')+        _       -> Nothing+      where+        (withx, withoutx) = partition (\s -> fst s == x || snd s == x) sgs++loops :: Eq a => [(a, a)] -> Maybe [[a]]+loops segs = do+    ps <- paths segs+    mapM_ (guard . isLoop) ps+    return ps+  where+    isLoop p = head p == last p
src/Diagrams/Puzzles/CmdLine.hs view
@@ -6,13 +6,13 @@     , RenderOpts(..)     , formats     , checkFormat-    , checkType-    , exitErr-    , readPuzzle     )   where +import Data.Puzzles.CmdLine (exitErr)+ import Diagrams.Prelude hiding (value, option, (<>), Result)+import Control.Monad (unless)  #ifdef CAIRO import Diagrams.Backend.Cairo (B, renderCairo)@@ -20,17 +20,9 @@ import Diagrams.Backend.SVG (B, renderSVG) #endif -import Text.Puzzles.Puzzle-import Data.Puzzles.PuzzleTypes--import System.Exit--import Control.Monad (unless)-import qualified Data.Yaml as Y- data RenderOpts = RenderOpts { _file :: FilePath, _w :: Double } -renderB :: FilePath -> SizeSpec2D -> Diagram B R2 -> IO ()+renderB :: FilePath -> SizeSpec V2 Double -> Diagram B -> IO () renderB = #ifdef CAIRO     renderCairo@@ -38,8 +30,8 @@     renderSVG #endif -renderToFile :: RenderOpts -> Diagram B R2 -> IO ()-renderToFile ropts = renderB (_file ropts) (Width $ _w ropts)+renderToFile :: RenderOpts -> Diagram B -> IO ()+renderToFile ropts = renderB (_file ropts) (mkWidth $ _w ropts)  formats :: [String] #ifdef CAIRO@@ -51,17 +43,3 @@ checkFormat :: String -> IO () checkFormat f = unless (f `elem` formats) $                     exitErr $ "unknown format: " ++ f--checkType :: Maybe String -> IO PuzzleType-checkType mt = do-    t <- maybe errno return mt-    maybe (errunk t) return (lookupType t)-  where-    errno    = exitErr "no puzzle type given"-    errunk t = exitErr $ "unknown puzzle type: " ++ t--readPuzzle :: FilePath -> IO (Either Y.ParseException TypedPuzzle)-readPuzzle = Y.decodeFileEither--exitErr :: String -> IO a-exitErr e = putStrLn e >> exitFailure
+ src/Diagrams/Puzzles/Code.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}++module Diagrams.Puzzles.Code+    (+      CodeDiagrams (..)+    , drawCode+    ) where++import Data.Puzzles.Code+import Data.Puzzles.GridShape+import Data.Puzzles.Grid+import Diagrams.Puzzles.Lib+import Diagrams.Puzzles.Grid+import Diagrams.Puzzles.Elements++import Diagrams.Prelude++import qualified Data.Map as Map++data CodeDiagrams a = CodeDiagrams { _cdLeft :: a, _cdTop :: a, _cdOver :: a }++instance Monoid a => Monoid (CodeDiagrams a) where+    mempty = CodeDiagrams mempty mempty mempty+    (CodeDiagrams x y z) `mappend` (CodeDiagrams x' y' z') =+        CodeDiagrams (x `mappend` x') (y `mappend` y') (z `mappend` z')++drawCode :: Backend' b => Code -> CodeDiagrams (Diagram b)+drawCode cs = mconcat (map drawCodePart cs)++drawCodePart :: Backend' b => CodePart -> CodeDiagrams (Diagram b)+drawCodePart (Rows'  rs) = CodeDiagrams (placeGrid g # centerX) mempty mempty+  where+    g = Map.fromList [ (C 0 r, arrowRight) | r <- rs ]+drawCodePart (Cols   cs) = CodeDiagrams mempty (placeGrid g # centerY) mempty+  where+    g = Map.fromList [ (C c 0, arrowDown)  | c <- cs ]+drawCodePart (RowsN' rs) = CodeDiagrams (placeGrid g # centerX) mempty mempty+  where+    g = Map.fromList [ (N 0 r, arrowRight) | r <- rs ]+drawCodePart (ColsN  cs) = CodeDiagrams mempty (placeGrid g # centerY) mempty+  where+    g = Map.fromList [ (N c 0, arrowDown)  | c <- cs ]+drawCodePart (LabelsN g) = CodeDiagrams mempty mempty (placeGrid . fmap label . clues $ g)+  where+    label c = drawChar c # scale 0.5 # fc gray # translate (r2 (1/3, -1/3))++arrowDown :: Backend' b => Diagram b+arrowDown = triangle 0.7 # lwG 0 # fc (blend 0.7 white black) # rotateBy (1/2)++arrowRight :: Backend' b => Diagram b+arrowRight = arrowDown # rotateBy (1/4)
src/Diagrams/Puzzles/Draw.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ConstraintKinds #-}  module Diagrams.Puzzles.Draw (@@ -13,35 +14,46 @@   ) where  import Diagrams.Prelude-import Diagrams.BoundingBox  import Diagrams.Puzzles.Lib import Diagrams.Puzzles.Widths+import Diagrams.Puzzles.Code -type RenderPuzzle b p s = (p -> Diagram b R2, (p, s) -> Diagram b R2)+type RenderPuzzle b p s = (p -> Diagram b, (p, s) -> Diagram b) -type PuzzleSol b = (Diagram b R2, Maybe (Diagram b R2))+type PuzzleSol b = (Diagram b, Maybe (Diagram b))  data OutputChoice = DrawPuzzle | DrawSolution | DrawExample     deriving Show  -- | Optionally render the puzzle, its solution, or a side-by-side --   example with puzzle and solution.-draw :: Backend' b =>-        PuzzleSol b -> OutputChoice -> Maybe (Diagram b R2)-draw (p, ms) = fmap (bg white) . d+draw :: Backend' b+     => Maybe (CodeDiagrams (Diagram b))+     -> PuzzleSol b -> OutputChoice -> Maybe (Diagram b)+draw mc (p, ms) = fmap (bg white) . d   where     fixup = alignPixel . border borderwidth-    d DrawPuzzle   = Just (fixup p)-    d DrawSolution = fixup <$> ms-    d DrawExample  = ms >>= \s -> return $ fixup p ||| strutX 2.0 ||| fixup s+    addCode x = case mc of+        Nothing                              -> x+        Just (CodeDiagrams cleft ctop cover) ->+            ((cover <> x) =!= top ctop) |!| lft cleft+    (=!=) = beside unitY+    (|!|) = beside (negated unitX)+    top c = if isEmpty c then mempty else strutY 0.5 =!= c+    lft c = if isEmpty c then mempty else strutX 0.5 |!| c+    isEmpty c = diameter unitX c == 0+    d DrawPuzzle   = fixup . addCode <$> Just p+    d DrawSolution = fixup . addCode <$> ms+    d DrawExample  = sideBySide <$> d DrawPuzzle <*> d DrawSolution+    sideBySide x y = x ||| strutX 2.0 ||| y  data Unit = Pixels | Points  cmtopoint :: Double -> Double cmtopoint = (* 28.3464567) -diagramWidth :: Diagram b R2 -> Double+diagramWidth :: Backend' b => Diagram b -> Double diagramWidth = fst . unr2 . boxExtents . boundingBox  toOutputWidth :: Unit -> Double -> Double@@ -49,9 +61,9 @@                               Points -> wpt   where     wpix = round (gridresd * w) :: Int  -- grid square size 40px-    wpt = cmtopoint (0.8 * w)     -- grid square size 0.8cm+    wpt = cmtopoint w     -- grid square size 1.0cm -alignPixel :: Backend' b => Diagram b R2 -> Diagram b R2+alignPixel :: Backend' b => Diagram b -> Diagram b alignPixel = scale (1/gridresd) . align' . scale gridresd   where     align' d = maybe id grow (getCorners $ boundingBox d) d@@ -62,6 +74,6 @@     phantoml p q = phantom' $ p ~~ q  -- | Add a phantom border of the given width around a diagram.-border :: Backend' b => Double -> Diagram b R2 -> Diagram b R2+border :: Backend' b => Double -> Diagram b -> Diagram b border w = extrudeEnvelope (w *^ unitX) . extrudeEnvelope (-w *^ unitX)          . extrudeEnvelope (w *^ unitY) . extrudeEnvelope (-w *^ unitY)
src/Diagrams/Puzzles/Elements.hs view
@@ -10,46 +10,55 @@  module Diagrams.Puzzles.Elements where -import Diagrams.Prelude+import Diagrams.Prelude hiding (N) import Diagrams.TwoD.Offset -import Data.Puzzles.Elements-import Data.Puzzles.GridShape+import qualified Data.Map as Map +import Data.Puzzles.Grid+import Data.Puzzles.Elements hiding (Loop)+import Data.Puzzles.GridShape hiding (edge)+ import Diagrams.Puzzles.Lib+import Diagrams.Puzzles.Style import Diagrams.Puzzles.Widths import Diagrams.Puzzles.Grid  pearl :: Backend' b =>-         MasyuPearl -> Diagram b R2+         MasyuPearl -> Diagram b pearl m = circle 0.35 # lwG 0.05 # fc (c m)   where     c MWhite = white     c MBlack = black  smallPearl :: Backend' b =>-              MasyuPearl -> Diagram b R2+              MasyuPearl -> Diagram b smallPearl = scale 0.4 . pearl +drawEnd :: Backend' b =>+       MEnd -> Diagram b+drawEnd MEnd = smallPearl MBlack+ -- | The up-right diagonal of a centered unit square.-ur :: Path R2+ur :: Path V2 Double ur = fromVertices [p2 (-1/2,-1/2), p2 (1/2,1/2)]  -- | The down-right diagonal of a centered unit square.-dr :: Path R2+dr :: Path V2 Double dr = fromVertices [p2 (1/2,-1/2), p2 (-1/2,1/2)]  -- | Both diagonals of a centered unit square.-cross :: Path R2+cross :: Path V2 Double cross = ur <> dr  -- | Draw a cross.-drawCross :: Backend' b => Diagram b R2-drawCross = stroke cross # scale 0.8 # lwG edgewidth+drawCross :: Backend' b => Bool -> Diagram b+drawCross True = stroke cross # scale 0.8 # lwG edgewidth+drawCross False = mempty  -- | Draw a Compass clue. drawCompassClue :: Backend' b =>-                   CompassC -> Diagram b R2+                   CompassC -> Diagram b drawCompassClue (CC n e s w) = texts <> stroke cross # lwG onepix     where tx Nothing _ = mempty           tx (Just x) v = text' (show x) # scale 0.5 # translate (r2 v)@@ -57,10 +66,17 @@                   [(0,f), (f,0), (0,-f), (-f,0)]           f = 3/10 --- | Draw a thermometer, given by a list of bottom-left corners--- of square cells.-thermo :: Backend' b => [P2] -> QDiagram b R2 Any-thermo vs@(v:_) = (bulb `atop` line) # col # translate (r2 (0.5, 0.5))+drawSlovakClue :: Backend' b =>+                  SlovakClue -> Diagram b+drawSlovakClue (SlovakClue s c) =+    centerY (drawInt s === strutY 0.1 === dots c) <> fillBG gray+  where+    dots n = centerX $ hcat' with {_sep = 0.04} (replicate n $ d)+    d = circle 0.1 # lwG 0.02 # fc white++-- | Draw a thermometer.+thermo :: Backend' b => [P2 Double] -> Diagram b+thermo vs@(v:_) = (bulb `atop` line) # col     where bulb = circle 0.4 # moveTo v           line = strokeLocLine (fromVertices vs)                  # lwG 0.55 # lineCap LineCapSquare@@ -70,13 +86,13 @@  -- | Draw a list of thermometers, given as lists of @(Int, Int)@ cell -- coordinates.-drawThermos :: Backend' b => [Thermometer] -> QDiagram b R2 Any-drawThermos = mconcat . map (thermo . map p2i)+drawThermos :: Backend' b => [Thermometer] -> Diagram b+drawThermos = mconcat . map (thermo . map toPoint)  -- | @drawTight d t@ draws the tight-fit value @t@, using @d@ to -- draw the components. drawTight :: Backend' b =>-             (a -> Diagram b R2) -> Tightfit a -> Diagram b R2+             (a -> Diagram b) -> Tightfit a -> Diagram b drawTight d (Single x) = d x drawTight d (UR x y) = stroke ur # lwG onepix                        <> d x # scale s # translate (r2 (-t,t))@@ -90,72 +106,107 @@           s = 2/3  -- | Stack the given words, left-justified.-stackWords :: Backend' b => [String] -> QDiagram b R2 Any-stackWords = vcat' with {_sep = 0.1} . scale 0.8 . map (alignL . text')+stackWords :: Backend' b => [String] -> Diagram b+stackWords = vcat' with {_sep = 0.1} . scale 0.8 . map (alignL . textFixed) +-- | Stack the given words, left-justified, a bit more generous, nice font+stackWordsLeft :: Backend' b => [String] -> Diagram b+stackWordsLeft = vcat' (with & catMethod .~ Distrib & sep .~ 1) . map (alignL . text')++-- | Stack the given words, left-justified, a bit more generous, nice font+stackWordsRight :: Backend' b => [String] -> Diagram b+stackWordsRight = vcat' (with & catMethod .~ Distrib & sep .~ 1) . map (alignR . text')+ -- | Mark a word in a grid of letters.-drawMarkedWord :: Backend' b => MarkedWord -> QDiagram b R2 Any+drawMarkedWord :: Backend' b => MarkedWord -> Diagram b drawMarkedWord (MW s e) = lwG onepix . stroke $ expandTrail' with {_expandCap = LineCapRound} 0.4 t     where t = fromVertices [p2i s, p2i e] # translate (r2 (1/2,1/2))  -- | Apply 'drawMarkedWord' to a list of words.-drawMarkedWords :: Backend' b => [MarkedWord] -> QDiagram b R2 Any+drawMarkedWords :: Backend' b => [MarkedWord] -> Diagram b drawMarkedWords = mconcat . map drawMarkedWord +drawMarkedLine :: (ToPoint a, Backend' b) => MarkedLine a -> Diagram b+drawMarkedLine (MarkedLine s e) = strokePath (toPoint s ~~ toPoint e) # lwG edgewidth # lc gray++drawMarkedLines :: (ToPoint a, Backend' b) => [MarkedLine a] -> Diagram b+drawMarkedLines = mconcat . map drawMarkedLine+ -- | Draw a slalom clue. drawSlalomClue :: (Show a, Backend' b) =>-                  a -> Diagram b R2+                  a -> Diagram b drawSlalomClue x = text' (show x) # scale 0.75                    <> circle 0.4 # fc white # lwG onepix +drawSlalomDiag :: Backend' b+               => SlalomDiag -> Diagram b+drawSlalomDiag d = stroke (v d) # lwG edgewidth+  where+    v SlalomForward = ur+    v SlalomBackward = dr+ -- | Draw text. Shouldn't be more than two characters or so to fit a cell.-drawText :: Backend' b => String -> QDiagram b R2 Any+drawText :: Backend' b => String -> Diagram b drawText = text' +drawTextFixed :: Backend' b => String -> Diagram b+drawTextFixed = textFixed+ -- | Draw an @Int@. drawInt :: Backend' b =>-           Int -> Diagram b R2+           Int -> Diagram b drawInt s = drawText (show s)  -- | Draw a character. drawChar :: Backend' b =>-            Char -> Diagram b R2+            Char -> Diagram b drawChar c = drawText [c] +drawCharFixed :: Backend' b =>+                 Char -> Diagram b+drawCharFixed c = drawTextFixed [c]++drawCharOpaque :: Backend' b =>+                  Char -> Diagram b+drawCharOpaque c = drawChar c <> circle 0.5 # lwG 0 # fc white++hintTL :: Backend' b => String -> Diagram b+hintTL = moveTo (p2 (-0.4,0.4)) . scale 0.5 . alignTL . drawText+ -- | Stack a list of words into a unit square. Scaled such that at least -- three words will fit. drawWords :: Backend' b =>-             [String] -> Diagram b R2+             [String] -> Diagram b drawWords ws = spread (-1.0 *^ unitY)                       (map (centerXY . scale 0.4 . drawText) ws)                # centerY  -- | Fit a line drawing into a unit square. --   For example, a Curve Data clue.-drawCurve :: Backend' b => [Edge] -> Diagram b R2+drawCurve :: Backend' b => [Edge N] -> Diagram b drawCurve = lwG onepix . fit 0.6 . centerXY . mconcat . map (stroke . edge)  -- | Draw a shadow in the style of Afternoon Skyscrapers.-drawShade :: Backend' b => Shade -> Diagram b R2-drawShade (Shade s w) = (if s then south else mempty) <>-                        (if w then west else mempty)+drawShadow :: Backend' b => Shade -> Diagram b+drawShadow (Shade s w) = (if s then south else mempty) <>+                         (if w then west else mempty)   where     shape = translate (r2 (-1/2, -1/2)) . fromVertices . map p2 $         [ (0, 0), (1/4, 1/4), (1, 1/4), (1, 0), (0, 0) ]     south = strokeLocLoop shape # lwG 0 # fc gray-    west = reflectAbout (p2 (0, 0)) (r2 (1, 1)) south+    west = reflectAbout (p2 (0, 0)) (direction $ r2 (1, 1)) south  -- | Draws the digits of a tapa clue, ordered --   left to right, top to bottom. drawTapaClue :: Backend' b =>-                TapaClue -> Diagram b R2+                TapaClue -> Diagram b drawTapaClue (TapaClue [x]) = drawInt x drawTapaClue (TapaClue xs)  = fit 0.8-                            . decoratePath (p (length xs))+                            . atPoints (p (length xs))                             . map drawInt                             $ xs   where-    p n = centerXY (p' n)+    p n = mconcat . pathVertices $ centerXY (p' n)     p' 2 = p2 (-1/4, 1/4) ~~ p2 (1/4, -1/4)     p' 3 = reflectX . rotateBy (1/6) $ triangle 0.8     p' 4 = reflectX . rotateBy (3/8) $ square 0.7@@ -163,7 +214,7 @@     p' _ = error "invalid tapa clue"  drawPrimeDiag :: Backend' b =>-                 PrimeDiag -> Diagram b R2+                 PrimeDiag -> Diagram b drawPrimeDiag (PrimeDiag d) = stroke p # lwG (3 * onepix) # lc (blend 0.5 gray white)   where     p = case d of (False, False) -> mempty@@ -171,8 +222,129 @@                   (False, True)  -> dr                   (True,  True)  -> ur <> dr -drawCrossing :: Backend' b => Crossing -> Diagram b R2+drawAnglePoly :: Backend' b =>+                 Int -> Diagram b+drawAnglePoly 3 = strokePath (triangle 0.3) # fc black+drawAnglePoly 4 = strokePath (square 0.25) # fc gray+drawAnglePoly 5 = strokePath (pentagon 0.2) # fc white+drawAnglePoly _ = error "expected 3..5"++fish :: Double -> Angle Double -> Trail' Loop V2 Double+fish off startAngle = closeLine $ half <> half # reverseLine # reflectY+  where+    half = arc (angleDir startAngle) endAngle # translateY (-off)+    endAngle = acosA off ^+^ (90 @@ deg)++drawFish :: Backend' b =>+            Fish -> Diagram b+drawFish Fish = fit 0.6 . centerXY . fc black . strokeLoop $+                fish 0.7 (30 @@ deg)++drawStar :: Backend' b =>+            Star -> Diagram b+drawStar Star = fc black . stroke . star (StarSkip 2) $ pentagon 0.3++vertexLoop :: VertexLoop -> Located (Trail' Loop V2 Double)+vertexLoop = mapLoc closeLine . fromVertices . map toPoint++note :: Backend' b =>+        Diagram b -> Diagram b+note d = d # frame 0.2 # bg (blend 0.2 black white)++placeNote :: Backend' b =>+             Size -> Diagram b -> Diagram b+placeNote sz d = note d # alignBL # translatep sz # translate (r2 (0.6,0.6))++placeNoteTL :: Backend' b =>+             Size -> Diagram b -> Diagram b+placeNoteTL sz d = note d # alignBR # translatep sz # translate (r2 (-0.6,0.6))++placeNoteBR :: Backend' b =>+             Size -> Diagram b -> Diagram b+placeNoteBR (x,_) d = note d # alignTL # translatep (x,0) # translate (r2 (0.6,-0.6))++miniloop :: Backend' b => Diagram b+miniloop = (drawThinEdges (map unorient out) <> grid gSlither g)+           # centerXY # scale 0.4+  where+    g = sizeGrid (1, 1)+    (out, _) = edgesM g++dominoBG :: Colour Double+dominoBG = blend 0.3 black white++drawDomino :: Backend' b => (Int, Int) -> Diagram b+drawDomino (x, y) =+    (drawInt x # smash ||| strutX 0.65 ||| drawInt y # smash) # centerXY # scale 0.6+    <> strokePath (rect 0.8 0.5) # lwG 0 # fc dominoBG++newtype DominoC = DominoC C+  deriving (Ord, Eq)++instance ToPoint DominoC where+    toPoint (DominoC (C x y)) = p2 ((1.0 * fromIntegral x),+                                    (0.7 * fromIntegral y))++drawDominos :: Backend' b => DigitRange -> Diagram b+drawDominos = centerXY . placeGrid+            . Map.mapKeys DominoC . fmap drawDomino . dominoGrid++drawPill :: Backend' b => Int -> Diagram b+drawPill x = drawInt x # scale 0.6+             <> strokePath (roundedRect 0.8 0.5 0.2) # lwG 0 # fc dominoBG++drawPills :: Backend' b => DigitRange -> Diagram b+drawPills (DigitRange a b) = centerXY . onGrid 1.0 0.7 drawPill $ placed+  where+    n = b - a + 1+    root = head [ x | x <- [n,n-1..], x*x <= n ]+    placed = zip [(x, y) | x <- [0..root], y <- [root,root-1..0]] [a..b]++drawCrossing :: Backend' b => Crossing -> Diagram b drawCrossing = const $ drawChar '+' -drawBahnhofClue :: Backend' b => BahnhofClue -> Diagram b R2+drawBahnhofClue :: Backend' b => BahnhofClue -> Diagram b drawBahnhofClue = either drawInt drawCrossing++kropkiDot :: Backend' b => KropkiDot -> Diagram b+kropkiDot KNone = mempty+kropkiDot c = circle 0.1 # lwG 0.03 # fc (col c) # smash+    where col KWhite = white+          col KBlack = blend 0.98 black white+          col KNone  = error "can't reach"++drawFraction :: Backend' b => Fraction -> Diagram b+drawFraction f = centerX $ case f of+    FInt a      -> drawText a # scale 0.8+    FFrac a b   -> frac a b+    FComp a b c -> (drawText a # scale 0.8) ||| strutX (1/10) ||| frac b c+  where+    frac b c = stroke slash # scale (1/4) # lwG onepix+               <> drawText b # scale s # translate (r2 (-t,t))+               <> drawText c # scale s # translate (r2 (t,-t))+      where t = 1/6+            s = 1/2+            slash :: Path V2 Double+            slash = fromVertices [p2 (-1/3,-1/2), p2 (1/3,1/2)]++drawMyopia :: Backend' b => Myopia -> Diagram b+drawMyopia = foldMap d'+  where+    d' = lwG onepix . scale (1/3) . d+    d U = a (0, 0) (0, 1)+    d R = a (0, 0) (1, 0)+    d D = a (0, 0) (0, -1)+    d L = a (0, 0) (-1, 0)+    a p q = arrowBetween' (with & arrowHead .~ tri & lengths .~ verySmall) (p2 p) (p2 q)++greaterClue :: Backend' b => GreaterClue -> [Diagram b]+greaterClue [] = mempty+greaterClue (_:rs) = g rs+  where+    g [] = [placeholder]+    g (r:rs') = placeholder : drawRel r : g rs'+    drawRel RUndetermined = mempty+    drawRel RLess = drawText "<"+    drawRel RGreater = drawText ">"+    drawRel REqual = drawText "="+    placeholder = circle 0.35 # lwG onepix # dashingG [0.05, 0.05] 0
src/Diagrams/Puzzles/Grid.hs view
@@ -5,76 +5,68 @@  import Data.Char (isUpper) import qualified Data.Map as M+import qualified Data.Set as S -import Diagrams.Prelude+import Diagrams.Prelude hiding (size, E, N, dot, outer)+import Diagrams.TwoD.Offset (offsetPath) +import qualified Data.AffineSpace as AS++import Data.Puzzles.Util import Data.Puzzles.Grid-import Data.Puzzles.GridShape hiding (size, cells)+import Data.Puzzles.GridShape hiding (edge) +import Diagrams.Puzzles.Style import Diagrams.Puzzles.Lib import Diagrams.Puzzles.Widths --- | Draw a small black dot with no envelope.-dot :: Backend' b => Diagram b R2-dot = circle 0.05 # fc black # smash---- | Draw a Slither Link style grid of dots of the specified size.-slithergrid :: Backend' b =>-               Size -> Diagram b R2-slithergrid (x, y) =-    hcatsep . replicate (x + 1) . vcatsep . replicate (y + 1) $ dot--fence :: [Double] -> Double -> Path R2-fence xs h = decoratePath xspath (repeat v)-  where-    xspath = fromVertices [ p2 (x, 0) | x <- xs ]-    v = alignB (vrule h)+(.--.) :: AS.AffineSpace p => p -> p -> AS.Diff p+(.--.) = (AS..-.) --- | The inner grid lines of a square grid of the specified size.-gridlines :: Size -> Path R2-gridlines (w, h) = fence' w h <> mirror (fence' h w)-  where-    fence' n l = fence (map fromIntegral [1..n-1]) (fromIntegral l)+class ToPoint a where+    toPoint :: a -> P2 Double -fullgridlines :: Size -> Path R2-fullgridlines (w, h) = fence' w h <> mirror (fence' h w)-  where-    fence' n l = fence (map fromIntegral [0..n]) (fromIntegral l)+instance ToPoint C where+    toPoint c = p2 (1/2, 1/2) .+^ r2i (c .--. C 0 0) --- | Draw a frame around the outside of a rectangle.-outframe' :: Backend' b => Double -> Size -> Diagram b R2-outframe' f (w, h) = strokePointLoop r # lwG fw-    where wd = fromIntegral w-          hd = fromIntegral h-          strokePointLoop = strokeLocTrail . mapLoc (wrapLoop . closeLine)-                            . fromVertices . map p2-          fw = f * gridwidth-          e = fw / 2 - gridwidth / 2-          r = [(-e, -e), (wd + e, -e), (wd + e, hd + e), (-e, hd + e)]+instance ToPoint N where+    toPoint c = origin .+^ r2i (c .--. N 0 0) -outframe :: Backend' b => Size -> Diagram b R2-outframe = outframe' framewidthfactor+-- | Draw a small black dot with no envelope.+dot :: Backend' b => Diagram b+dot = circle 0.05 # fc black # smash --- | Draw a square grid, applying the given style to the grid lines.-grid' :: Backend' b =>-         (Diagram b R2 -> Diagram b R2) -> Size -> Diagram b R2-grid' gridstyle s =-    outframe s-    <> stroke (gridlines s) # lwG gridwidth # gridstyle+grid :: Backend' b+     => GridStyle -> Grid C a -> Diagram b+grid s g =+    (placeGrid . fmap (const vertex) . nodeGrid $ g)+    <> stroke inner # linestyle (_line s)+    <> stroke outer # linestyle (_border s)+    <> frm+  where+    vertex = case _vertex s of+        VertexDot    -> dot+        VertexNone   -> mempty+    linestyle LineNone   = const mempty+    linestyle LineThin   = lwG gridwidth+    linestyle LineDashed = gridDashing . lwG gridwidth+    linestyle LineThick  = lwG edgewidth+    frm = case _frame s of+        Just (FrameStyle f c)  -> outLine f outer # fc c+        Nothing                -> mempty --- | Draw a square grid with default grid line style.-grid :: Backend' b =>-        Size -> Diagram b R2-grid = grid' id+    (outer, inner) = irregularGridPaths g --- | Draw a square grid with thin frame.-plaingrid :: Backend' b =>-             Size -> Diagram b R2-plaingrid s = stroke (fullgridlines s) # lwG gridwidth+outLine :: Backend' b => Double -> Path V2 Double -> Diagram b+outLine f p = lwG 0 . stroke $ pin <> pout+  where+    pout = reversePath $ offsetPath (f * onepix - e) p+    pin = offsetPath (-e) p+    e = onepix / 2 -bgdashingG :: (Semigroup a, HasStyle a, V a ~ R2) =>-             [Double] -> Double -> Colour Double -> a -> a-bgdashingG ds offs c x = x # dashingG ds offs <> x # lc c+bgdashingG :: (Semigroup a, HasStyle a, InSpace V2 Double a) =>+             [Double] -> Double -> AlphaColour Double -> a -> a+bgdashingG ds offs c x = x # dashingG ds offs <> x # lcA c  dashes :: [Double] dashes = [5 / 40, 3 / 40]@@ -82,141 +74,109 @@ dashoffset :: Double dashoffset = 2.5 / 40 -gridDashing :: (Semigroup a, HasStyle a, V a ~ R2) => a -> a+gridDashing :: (Semigroup a, HasStyle a, InSpace V2 Double a) => a -> a gridDashing = bgdashingG dashes dashoffset white'   where-    white' = blend 0.95 white black---- | Draw a square grid with dashed grid lines. The gaps---   between dashes are off-white to aid in using filling---   tools.-dashedgrid :: Backend' b =>-              Size -> Diagram b R2-dashedgrid = grid' gridDashing--edgePath :: Edge' (Vertex Square) -> Path R2-edgePath (E' v R) = p2i v ~~ p2i (v ^+^ (1,0))-edgePath (E' v L) = p2i v ~~ p2i (v ^+^ (-1,0))-edgePath (E' v U) = p2i v ~~ p2i (v ^+^ (0,1))-edgePath (E' v D) = p2i v ~~ p2i (v ^+^ (0,-1))+    white' = black `withOpacity` (0.05 :: Double) -irregularGridPaths :: SGrid a -> (Path R2, Path R2)-irregularGridPaths (Grid _ m) = (toPath outer, toPath inner)+-- | `irregularGridPaths g` is a pair `(outer, inner)` of paths.+--+-- `outer` consists of the loops that make up the border of the+-- grid (assuming the grid is connected orthogonally). They are+-- reoriented to be compatible with `outLine`; for some reason,+-- reversePath on the immediate result does not work.+--+-- `inner` consists of the individual inner segments.+irregularGridPaths :: Grid C a -> (Path V2 Double, Path V2 Double)+irregularGridPaths m = (path' (map revEdge outer), path inner)   where     (outer, inner) = edges (M.keysSet m) (`M.member` m)-    toPath = mconcat . map edgePath+    path  es = mconcat . map (conn . ends) $ es+    path' es = case loops (map ends' es) of+        Just ls   -> mconcat . map (pathFromLoopVertices . map toPoint) $ ls+        Nothing   -> mempty+    pathFromLoopVertices = pathFromLocTrail+                         . mapLoc (wrapLoop . closeLine)+                         . fromVertices+    conn (v, w) = toPoint v ~~ toPoint w -irregularGrid :: Backend' b =>-                 SGrid a -> Diagram b R2-irregularGrid g = stroke outer # lwG (3 * gridwidth) # lineCap LineCapSquare <>-                  stroke inner # lwG gridwidth+irregPathToVertices :: (Path V2 Double, Path V2 Double) -> (S.Set (P2 Double), S.Set (P2 Double), S.Set (P2 Double))+irregPathToVertices (pouter, pinner) = (outer, inner S.\\ outer, inner `S.union` outer)   where-    (outer, inner) = irregularGridPaths g---- | In a square grid, use the first argument to draw things at the centres---   of cells given by coordinates.-atCentres :: (Transformable a, Monoid a, V a ~ R2) =>-             (t -> a) -> [(Coord, t)] -> a-atCentres dc = translate (r2 (1/2, 1/2)) . atVertices dc+    outer = S.fromList . mconcat . pathVertices $ pouter+    inner = S.fromList . mconcat . pathVertices $ pinner -atCentres' :: (Transformable a, V a ~ R2) => SGrid a -> [a]-atCentres' = translate (r2 (1/2, 1/2)) . atVertices'+onGrid :: (Transformable a, Monoid a, InSpace V2 Double a) =>+          Double -> Double -> (t -> a) -> [(Coord, t)] -> a+onGrid dx dy f = mconcat . map g+  where+    g (p, c) = f c # translate (r2coord p)+    r2coord (x, y) = r2 (dx * fromIntegral x, dy * fromIntegral y) --- | In a square grid, use the first argument to draw things---   at the grid vertices given by coordinates.-atVertices :: (Transformable a, Monoid a, V a ~ R2) =>-              (t -> a) -> [(Coord, t)] -> a-atVertices dc = mconcat . map (\ (p, c) -> dc c # translatep p)+placeGrid :: (ToPoint k, HasOrigin a, Monoid a, InSpace V2 Double a)+          => Grid k a -> a+placeGrid = M.foldMapWithKey (moveTo . toPoint) -atVertices' :: (Transformable a, V a ~ R2) => SGrid a -> [a]-atVertices' g = [ (g ! c) # translatep c | c <- cells g ]+placeGrid' :: (HasOrigin a, Monoid a, InSpace V2 Double a)+          => Grid (P2 Double) a -> a+placeGrid' = M.foldMapWithKey moveTo -edge :: Edge -> Path R2-edge (E c d) = rule d # translate (r2i c)+edge :: (ToPoint k) => Edge k -> Path V2 Double+edge (E c d) = rule d # translate (toPoint c .-. origin)   where-    rule V = vrule 1.0 # alignB-    rule H = hrule 1.0 # alignL+    rule Vert = vrule 1.0 # alignB+    rule Horiz = hrule 1.0 # alignL -dualEdge :: Edge -> Path R2-dualEdge = translate (r2 (1/2, 1/2)) . edge+midPoint :: (AS.AffineSpace k, AS.Diff k ~ (Int, Int), ToPoint k) => Edge k -> P2 Double+midPoint e = c .+^ 0.5 *^ (d .-. c)+  where+    (a, b) = ends e+    c = toPoint a+    d = toPoint b -edgeStyle :: (HasStyle a, V a ~ R2) => a -> a+edgeStyle :: (HasStyle a, InSpace V2 Double a) => a -> a edgeStyle = lineCap LineCapSquare . lwG edgewidth -thinEdgeStyle :: (HasStyle a, V a ~ R2) => a -> a+thinEdgeStyle :: (HasStyle a, InSpace V2 Double a) => a -> a thinEdgeStyle = lineCap LineCapSquare . lwG onepix -drawEdges :: Backend' b => [Edge] -> Diagram b R2+drawEdges :: (ToPoint k, Backend' b) => [Edge k] -> Diagram b drawEdges = edgeStyle . stroke . mconcat . map edge -drawDualEdges :: Backend' b => [Edge] -> Diagram b R2-drawDualEdges = edgeStyle . stroke . mconcat . map dualEdge--drawThinDualEdges :: Backend' b => [Edge] -> Diagram b R2-drawThinDualEdges = thinEdgeStyle . stroke . mconcat . map dualEdge+drawThinEdges :: (ToPoint k, Backend' b) => [Edge k] -> Diagram b+drawThinEdges = thinEdgeStyle . stroke . mconcat . map edge -drawAreaGrid :: (Backend' b, Eq a) =>-                  SGrid a -> Diagram b R2-drawAreaGrid = drawEdges . borders <> grid . size+drawAreas :: (Backend' b, Eq a) =>+             Grid C a -> Diagram b+drawAreas = drawEdges . borders -fillBG :: Backend' b => Colour Double -> Diagram b R2+fillBG :: Backend' b => Colour Double -> Diagram b fillBG c = square 1 # lwG onepix # fc c # lc c  shadeGrid :: Backend' b =>-              SGrid (Maybe (Colour Double)) -> Diagram b R2-shadeGrid = mconcat . atCentres' . fmap (maybe mempty fillBG)+             Grid C (Maybe (Colour Double)) -> Diagram b+shadeGrid = placeGrid . fmap fillBG . clues -drawShadedGrid :: Backend' b =>-                  SGrid Bool -> Diagram b R2-drawShadedGrid = shadeGrid . fmap f+drawShade :: Backend' b =>+             Grid C Bool -> Diagram b+drawShade = shadeGrid . fmap f   where     f True  = Just gray     f False = Nothing -drawAreaGridGray :: Backend' b =>-                    SGrid Char -> Diagram b R2-drawAreaGridGray = drawAreaGrid <> shadeGrid . fmap cols+drawAreasGray :: Backend' b =>+                 Grid C Char -> Diagram b+drawAreasGray = drawAreas <> shadeGrid . fmap cols   where     cols c | isUpper c  = Just (blend 0.1 black white)            | otherwise  = Nothing -irregAreaGridX :: Backend' b =>-                      SGrid Char -> Diagram b R2-irregAreaGridX = irregularGrid <> drawEdges . borders <> shadeGrid . fmap cols-  where-    cols 'X' = Just gray-    cols _   = Nothing- -- Place a list of diagrams along a ray, with steps of size -- @f@.-distrib :: (Transformable c, Monoid c, V c ~ R2) =>-           R2 -> (Int, Int) -> Double -> [c] -> c+distrib :: (Transformable c, Monoid c, InSpace V2 Double c) =>+           V2 Double -> (Int, Int) -> Double -> [c] -> c distrib base dir f xs =     translate (0.75 *^ dir' ^+^ base) . mconcat $         zipWith (\i d -> translate (fromIntegral i *^ dir') d) [(0 :: Int)..] xs   where     dir' = f *^ r2i dir--outsideGen :: (Transformable c, Monoid c, V c ~ R2) =>-              (OutsideClue [c] -> R2) -> Double -> [OutsideClue [c]] -> c-outsideGen tobase f ocs = mconcat . map placeOC $ ocs-  where-    placeOC o = distrib (tobase o) (ocDir o) f (ocValue o)--outsideCells :: (Transformable c, Monoid c, V c ~ R2) =>-                Double -> [OutsideClue [c]] -> c-outsideCells = outsideGen base-  where-    base (OClue (bx, by) (dx, dy) _)-        | dx /= 0   = r2 (fromIntegral bx - 1, fromIntegral by - 1/2)-        | dy /= 0   = r2 (fromIntegral bx - 1/2, fromIntegral by)-        | otherwise = error "invalid outside clue"--outsideVertices :: (Transformable c, Monoid c, V c ~ R2) =>-                   Double -> [OutsideClue [c]] -> c-outsideVertices = outsideGen base-  where-    base (OClue (bx, by) (dx, dy) _)-        | dx /= 0   = r2 (fromIntegral bx, fromIntegral by)-        | dy /= 0   = r2 (fromIntegral bx, fromIntegral by)-        | otherwise = error "invalid outside clue"
src/Diagrams/Puzzles/Lib.hs view
@@ -1,99 +1,132 @@-{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, TypeFamilies #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ConstraintKinds #-}  module Diagrams.Puzzles.Lib where +import Diagrams.Path (pathPoints) import Diagrams.Prelude -import Graphics.SVGFonts.ReadFont+import Graphics.SVGFonts.Text (TextOpts(..), Mode(..), Spacing(..), textSVG')+import Graphics.SVGFonts.Fonts (bit)+import Graphics.SVGFonts.ReadFont (PreparedFont, loadFont)+ import Control.Arrow ((***))  import Paths_puzzle_draw (getDataFileName) import System.IO.Unsafe (unsafePerformIO) -type Backend' b = (Backend b R2, Renderable (Path R2) b)+type Backend' b = (V b ~ V2, N b ~ Double,+                   Renderable (Path V2 Double) b, Backend b V2 Double)  -- | Vertical/horizontal stroked line of given length.-vline, hline :: Backend' b => Double -> Diagram b R2+vline, hline :: Backend' b => Double -> Diagram b vline n = strokeLine . fromVertices . map p2 $ [(0, 0), (0, n)] hline n = strokeLine . fromVertices . map p2 $ [(0, 0), (n, 0)]  -- | Variant of 'hcat'' that spreads with distance @1@.-hcatsep :: (Juxtaposable a, HasOrigin a, Monoid' a, V a ~ R2) => [a] -> a+hcatsep :: (InSpace V2 Double a, Juxtaposable a, HasOrigin a, Monoid' a)+        => [a] -> a hcatsep = hcat' with {_sep = 1}  -- | Variant of 'vcat'' that spreads with distance @1@, -- and stacks towards the top.-vcatsep :: (Juxtaposable a, HasOrigin a, Monoid' a, V a ~ R2) => [a] -> a+vcatsep :: (InSpace V2 Double a, Juxtaposable a, HasOrigin a, Monoid' a)+        => [a] -> a vcatsep = cat' (r2 (0,1)) with {_sep = 1}  -- | Collapse the envelope to a point.-smash :: Backend' b => QDiagram b R2 Any -> QDiagram b R2 Any-smash = withEnvelope (pointDiagram origin :: D R2)+smash :: Backend' b => Diagram b -> Diagram b+smash = withEnvelope (pointDiagram origin :: D V2 Double)  -- | Helper to translate by a point given as @(Int, Int)@.-translatep :: (Transformable t, V t ~ R2) => (Int, Int) -> t -> t+translatep :: (InSpace V2 Double t, Transformable t)+           => (Int, Int) -> t -> t translatep = translate . r2i  -- | Convert pair of @Int@ to vector.-r2i :: (Int, Int) -> R2+r2i :: (Int, Int) -> V2 Double r2i = r2 . (fromIntegral *** fromIntegral)  -- | Convert pair of @Int@ to point.-p2i :: (Int, Int) -> P2+p2i :: (Int, Int) -> P2 Double p2i = p2 . (fromIntegral *** fromIntegral) -mirror :: (Transformable t, V t ~ R2) => t -> t-mirror = reflectAbout (p2 (0, 0)) (r2 (1, 1))+mirror :: (InSpace V2 Double t, Transformable t) => t -> t+mirror = reflectAbout (p2 (0, 0)) (direction $ r2 (1, -1))  -- | Interleave two lists. interleave :: [a] -> [a] -> [a] interleave [] _ = [] interleave (x:xs) ys = x : interleave ys xs +magnitude :: V2 Double -> Double+magnitude = norm+ -- | Spread diagrams evenly along the given vector.-spread :: Backend' b => R2 -> [Diagram b R2] -> Diagram b R2+spread :: Backend' b => V2 Double -> [Diagram b] -> Diagram b spread v things = cat v . interleave (repeat (strut vgap)) $ things     where ds = map (diameter v) things           gap' = (magnitude v - sum ds) / fromIntegral (length things + 1)           vgap = (gap' / magnitude v) *^ v -dmid ::  (Enveloped a, V a ~ R2) => a -> Double-dmid a = (dtop + dbot) / 2 - dbot+dmid ::  (InSpace V2 Double a, Enveloped a) => V2 Double -> a -> Double+dmid u a = (dtop + dbot) / 2 - dbot     where menv v = magnitude . envelopeV v-          dtop = menv unitY a-          dbot = menv ((-1) *^ unitY) a+          dtop = menv u a+          dbot = menv ((-1) *^ u) a  -- | Place the second diagram to the right of the first, aligning both -- vertically. The origin is the origin of the left diagram.-besidesL :: (Backend' b, Semigroup m, Monoid m) =>-            QDiagram b R2 m -> QDiagram b R2 m -> QDiagram b R2 m+besidesL :: Backend' b =>+            Diagram b -> Diagram b -> Diagram b besidesL a b = a ||| strutX 0.5 ||| b'-    where b' = b # centerY # translate (dmid a *^ unitY)+    where b' = b # centerY # translate (dmid unitY a *^ unitY)  -- | Variant of 'besidesL' where the origin is that of the right diagram.-besidesR :: (Backend' b, Semigroup m, Monoid m) =>-           QDiagram b R2 m -> QDiagram b R2 m -> QDiagram b R2 m+besidesR :: Backend' b =>+           Diagram b  -> Diagram b -> Diagram b besidesR b a =  b' ||| strutX 0.5 ||| a-    where b' = b # centerY # translate (dmid a *^ unitY)+    where b' = b # centerY # translate (dmid unitY a *^ unitY) +aboveT :: Backend' b =>+          Diagram b -> Diagram b -> Diagram b+aboveT a b = a === strutY 0.5 === b'+    where b' = b # centerX # translate (dmid unitX a *^ unitX)+ -- | @fit f a@ scales @a@ to fit into a square of size @f@.-fit :: (Transformable t, Enveloped t, V t ~ R2) =>+fit :: (Transformable t, Enveloped t, InSpace V2 Double t) =>        Double -> t -> t fit f a = scale (f / m) a-    where m = max (magnitude (diameter unitX a))-                  (magnitude (diameter unitY a))+    where m = max (diameter unitX a)+                  (diameter unitY a) +type Font = PreparedFont Double+ -- | Write text that is centered both vertically and horizontally and that -- has an envelope. Sized such that single capital characters fit nicely -- into a square of size @1@.-text' :: Backend' b => String -> Diagram b R2-text' t = stroke (textSVG' $ TextOpts t fnt INSIDE_H KERN False 1 1)-          # lwG 0 # fc black # scale 0.8+text'' :: Backend' b => Font -> String -> Diagram b+text'' fnt t = stroke (textSVG' (TextOpts fnt INSIDE_H KERN False 1 1) t)+             # lwG 0 # rfc black # scale 0.8   where-    fnt = outlMap . unsafePerformIO . getDataFileName-        $ "data/fonts/gen-light.svg"+    rfc :: (HasStyle a, InSpace V2 Double a) => Colour Double -> a -> a+    rfc = recommendFillColor +text' :: Backend' b => String -> Diagram b+text' = text'' fontGenLight++textFixed :: Backend' b => String -> Diagram b+textFixed = text'' fontBit++fontGenLight :: Font+fontGenLight = unsafePerformIO . loadFont . unsafePerformIO . getDataFileName+    $ "data/fonts/gen-light.svg"++fontBit :: Font+fontBit = bit+ -- text' t = text t # fontSize 0.8 # font "Helvetica" # translate (r2 (0.04, -0.07)) --          <> phantom' (textrect t) --textrect :: Backend' b => String -> Diagram b R2@@ -102,5 +135,19 @@ --text'' t = text' t `atop` textrect t  -- | Variant of 'phantom' that forces the argument backend type.-phantom' :: Backend' b => Diagram b R2 -> Diagram b R2+phantom' :: Backend' b => Diagram b -> Diagram b phantom' = phantom++debugPath :: Backend' b => Path V2 Double -> Diagram b+debugPath p = mconcat . map draw $ prts'+  where+    prts = zip (pathVertices p) ['a'..]+    prts' = concatMap (\(ps,c) -> zipWith (\pt d -> (pt, c:d:[])) ps ['0'..]) prts+    draw (pt, l) = moveTo pt $ text' l++debugPath' :: Backend' b => Path V2 Double -> Diagram b+debugPath' p = mconcat . map draw $ prts'+  where+    prts = zip (pathPoints p) ['a'..]+    prts' = concatMap (\(ps,c) -> zipWith (\pt d -> (pt, c:d:[])) ps ['0'..]) prts+    draw (pt, l) = moveTo pt $ text' l
src/Diagrams/Puzzles/PuzzleGrids.hs view
@@ -3,91 +3,91 @@ {-# LANGUAGE TypeFamilies              #-} {-# LANGUAGE ConstraintKinds           #-} -module Diagrams.Puzzles.PuzzleGrids where+module Diagrams.Puzzles.PuzzleGrids+    (+      drawIntGrid+    , drawCharGrid+    , outsideIntGrid+    , drawSlitherGrid+    , drawTightGrid+    , sudokugrid+    , drawWordsClues+    , drawOutsideGrid+    , drawMultiOutsideGrid+    , drawOutsideGridN+    , drawMultiOutsideGridN+    , placeMultiOutside+    ) where -import Diagrams.Prelude+import Diagrams.Prelude hiding (size, N) +import qualified Data.Map as Map+import Data.Maybe (maybeToList)+ import Data.Puzzles.Grid+import Data.Puzzles.GridShape import Data.Puzzles.Elements import Data.Puzzles.Sudoku  import Diagrams.Puzzles.Lib+import Diagrams.Puzzles.Style import Diagrams.Puzzles.Grid-import Diagrams.Puzzles.Widths import Diagrams.Puzzles.Elements -drawClueGrid :: Backend' b =>-                SGrid (Clue Char) -> Diagram b R2-drawClueGrid = atCentres drawChar . clues <> grid . size--drawIntClues :: Backend' b =>-                SGrid (Clue Int) -> Diagram b R2-drawIntClues = atCentres drawInt . clues--drawInts :: Backend' b =>-            SGrid Int -> Diagram b R2-drawInts = atCentres drawInt . values+drawCharGrid :: Backend' b =>+                Grid C (Maybe Char) -> Diagram b+drawCharGrid = placeGrid . fmap drawChar . clues <> grid gDefault  drawIntGrid :: Backend' b =>-               SGrid (Clue Int) -> Diagram b R2-drawIntGrid = drawIntClues <> grid . size+               Grid C (Maybe Int) -> Diagram b+drawIntGrid = placeGrid . fmap drawInt . clues  <> grid gDefault  drawSlitherGrid :: Backend' b =>-                   SGrid (Clue Int) -> Diagram b R2-drawSlitherGrid = atCentres drawInt . clues <> slithergrid . size--drawMasyuGrid :: Backend' b =>-                 SGrid MasyuClue -> Diagram b R2-drawMasyuGrid = atCentres pearl . clues <> grid . size--drawCompassClues :: Backend' b =>-                    SGrid CompassClue -> Diagram b R2-drawCompassClues = atCentres drawCompassClue . clues--drawCompassGrid :: Backend' b =>-                   SGrid CompassClue -> Diagram b R2-drawCompassGrid = drawCompassClues <> grid . size+                   Grid C (Maybe Int) -> Diagram b+drawSlitherGrid = placeGrid . fmap  drawInt . clues <> grid gSlither  sudokugrid :: Backend' b =>-              SGrid a -> Diagram b R2-sudokugrid = drawEdges . sudokubordersg  <> grid . size+              Grid C a -> Diagram b+sudokugrid = drawEdges . sudokubordersg <> grid gDefault  drawWordsClues :: Backend' b =>-                  SGrid (Clue [String]) -> Diagram b R2-drawWordsClues = atCentres drawWords . clues+                  Grid C (Maybe [String]) -> Diagram b+drawWordsClues = placeGrid . fmap drawWords . clues  drawTightGrid :: Backend' b =>-                 (t -> Diagram b R2) -> SGrid (Tightfit t) -> Diagram b R2-drawTightGrid d g = atCentres (drawTight d) (values g)-                    <> grid (size g)-                    <> phantom' (stroke $ p2i (-1,-1) ~~ p2i (sx + 1, sy + 1))-    where (sx, sy) = size g+                 (t -> Diagram b) -> Grid C (Tightfit t) -> Diagram b+drawTightGrid d g = (placeGrid . fmap (drawTight d) $ g)+                    <> grid gDefault g+                    <> phantom' (strokePath $ p2i (-1,-1) ~~ p2i (sx + 1, sy + 1))+    where (sx, sy) = size (Map.mapKeys toCoord g) -drawSlalomGrid :: Backend' b =>-                  SGrid (Clue Int) -> Diagram b R2-drawSlalomGrid g = atVertices drawSlalomClue (clues g)-                   <> grid (w-1, h-1)-    where (w, h) = size g+placeMultiOutside :: (Ord k, FromCoord k, ToPoint k,+                      HasOrigin a, Monoid a,+                      InSpace V2 Double a)+                  => OutsideClues k [a] -> a+placeMultiOutside = placeGrid . multiOutsideClues -drawSlalomDiags :: Backend' b =>-                   SGrid SlalomDiag -> Diagram b R2-drawSlalomDiags = atCentres diag . clues . fmap Just-    where diag SlalomForward  = stroke ur # lwG edgewidth-          diag SlalomBackward = stroke dr # lwG edgewidth+placeOutside :: (Ord k, FromCoord k, ToPoint k,+                 HasOrigin a, Monoid a,+                 InSpace V2 Double a)+             => OutsideClues k (Maybe a) -> a+placeOutside = placeMultiOutside . fmap maybeToList -drawCrosses ::  Backend' b =>-                 SGrid Bool -> Diagram b R2-drawCrosses = atCentres (if' drawCross mempty) . values-  where-    if' a b x = if x then a else b+drawOutsideGrid :: Backend' b => OutsideClues C (Maybe String) -> Diagram b+drawOutsideGrid = placeOutside . fmap (fmap (scale 0.8 . drawText))+                  <> grid gDefault . outsideGrid +drawOutsideGridN :: Backend' b => OutsideClues N (Maybe String) -> Diagram b+drawOutsideGridN = placeOutside . fmap (fmap (scale 0.8 . drawText))+                  <> grid gDefault . cellGrid . outsideGrid -outsideGrid :: Backend' b =>-               OutsideClues [String] -> Diagram b R2-outsideGrid = atCentres (scale 0.8 . drawText) . multiOutsideClues-              <> grid . outsideSize+drawMultiOutsideGrid :: Backend' b => OutsideClues C [String] -> Diagram b+drawMultiOutsideGrid = placeMultiOutside . fmap (fmap (scale 0.8 . drawText))+                     <> grid gDefault . outsideGrid -outsideIntGrid :: Backend' b =>-                  OutsideClues [Int] -> Diagram b R2-outsideIntGrid = atCentres (scale 0.8 . drawInt) . multiOutsideClues-                 <> grid . outsideSize+drawMultiOutsideGridN :: Backend' b => OutsideClues N [String] -> Diagram b+drawMultiOutsideGridN = placeMultiOutside . fmap (fmap (scale 0.8 . drawText))+                      <> grid gDefault . cellGrid . outsideGrid++outsideIntGrid :: Backend' b => OutsideClues C [Int] -> Diagram b+outsideIntGrid = drawMultiOutsideGrid . fmap (fmap show)
src/Diagrams/Puzzles/PuzzleTypes.hs view
@@ -1,3 +1,4 @@+ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-}@@ -10,11 +11,25 @@     curvedata, doubleback, slalom, compass, boxof2or3,     afternoonskyscrapers, meanderingnumbers, tapa, japanesesums,     coral, maximallengths, primeplace, labyrinth, bahnhof,-    cave+    cave, angleLoop, shikaku, slovaksums,+    blackoutDominos, anglers, skyscrapers,+    summon, baca, buchstabensalat, doppelblock, sudokuDoppelblock,+    dominos, skyscrapersStars, fillominoCheckered, numberlink,+    slithermulti, dominoPills, fillominoLoop, loopki, litssym,+    scrabble, neighbors, starwars, heyawake, wormhole, pentominous,+    starbattle, colorakari, persistenceOfMemory, abctje, kropki,+    statuepark, pentominousBorders, nanroSignpost, tomTom,+    horseSnake, illumination, pentopia,+    pentominoPipes, greaterWall, galaxies   ) where -import Diagrams.Prelude hiding (Loop, coral)+import Diagrams.Prelude hiding (Loop, N, coral, size) +import Data.Char (isUpper)+import Data.List (nub, sort, sortOn)+import qualified Data.Map as Map++import Diagrams.Puzzles.Style import Diagrams.Puzzles.PuzzleGrids import Diagrams.Puzzles.Draw import Diagrams.Puzzles.Grid@@ -24,64 +39,97 @@ import Diagrams.Puzzles.Widths  import Data.Puzzles.Grid-import Data.Puzzles.GridShape (Edge)+import Data.Puzzles.GridShape import Data.Puzzles.Elements import qualified Data.Puzzles.Pyramid as Pyr  lits :: Backend' b => RenderPuzzle b AreaGrid ShadedGrid lits = (,)-    drawAreaGridGray-    (drawAreaGrid . fst <> drawShadedGrid . snd)+    (grid gDefault <> drawAreasGray)+    ((drawAreas <> grid gDefault) . fst <> drawShade . snd)  litsplus :: Backend' b => RenderPuzzle b AreaGrid ShadedGrid litsplus = lits -solstyle :: (HasStyle a, V a ~ R2) => a -> a+litssym :: Backend' b => RenderPuzzle b AreaGrid ShadedGrid+litssym = (,)+    p+    (p . fst <> drawShade . snd)+  where+    p g = drawAreas g <> grid gDefault g <> translate (c g) (scale 0.5 $ smallPearl MBlack)+    c g = let (rs, cs) = size . Map.mapKeys toCoord $ g+          in r2 ((fromIntegral rs) / 2, (fromIntegral cs) / 2)++solstyle :: (HasStyle a, InSpace V2 Double a) => a -> a solstyle = lc (blend 0.8 black white) . lwG (3 * onepix) -geradeweg :: Backend' b => RenderPuzzle b IntGrid Loop+geradeweg :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) (Loop C) geradeweg = (,)     drawIntGrid-    (drawIntClues . fst-     <> solstyle . drawDualEdges . snd-     <> grid . size . fst)+    (placeGrid . fmap drawInt . clues . fst+     <> solstyle . drawEdges . snd+     <> grid gDefault . fst) -fillomino :: Backend' b => RenderPuzzle b IntGrid (SGrid Int)+fillomino :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) (Grid C Int) fillomino = (,)-    (atCentres drawInt . clues <> dashedgrid . size)-    ((atCentres drawInt . values <> drawEdges . borders <> dashedgrid . size) . snd)+    (placeGrid . fmap drawInt . clues <> grid gDashed)+    ((placeGrid . fmap drawInt <> drawEdges . borders <> grid gDashed) . snd) +fillominoCheckered :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) (Grid C Int)+fillominoCheckered = (,)+    (placeGrid . fmap  drawInt . clues <> grid gDashed)+    ((placeGrid . fmap drawInt+      <> drawEdges . borders+      <> grid gDashed+      <> shadeGrid . checker) . snd)+  where+    checker = fmap pickColour . colour+    pickColour 1 = Nothing+    pickColour 2 = Just gray+    pickColour _ = Just red++fillominoLoop :: Backend' b => RenderPuzzle b (Grid C (Maybe Int))+                                              (Grid C Int, Loop C)+fillominoLoop = (,)+    (fst fillomino)+    ((placeGrid . fmap drawInt . fst+      <> solstyle . drawEdges . snd+      <> drawEdges . borders . fst+      <> grid gDashed . fst) . snd)+ masyu :: Backend' b =>-         RenderPuzzle b (SGrid (Clue MasyuPearl)) Loop+         RenderPuzzle b (Grid C (Maybe MasyuPearl)) (Loop C) masyu = (,)-    drawMasyuGrid-    (solstyle . drawDualEdges . snd <> drawMasyuGrid . fst)+    p+    (solstyle . drawEdges . snd <> p . fst)+  where+    p = placeGrid . fmap pearl . clues <> grid gDefault  nurikabe :: Backend' b =>-            RenderPuzzle b IntGrid ShadedGrid+            RenderPuzzle b (Grid C (Maybe Int)) ShadedGrid nurikabe = (,)     drawIntGrid-    (drawIntGrid . fst <> drawShadedGrid . snd)+    (drawIntGrid . fst <> drawShade . snd)  latintapa :: Backend' b =>-             RenderPuzzle b (SGrid (Clue [String])) CharClueGrid+             RenderPuzzle b (Grid C (Maybe [String])) (Grid C (Maybe Char)) latintapa = (,)     l-    (l . fst <> atCentres drawChar . clues . snd)+    (l . fst <> placeGrid . fmap drawChar . clues . snd)   where-    l = grid . size <> drawWordsClues+    l = grid gDefault <> drawWordsClues  sudoku :: Backend' b =>-          RenderPuzzle b IntGrid IntGrid+          RenderPuzzle b (Grid C (Maybe Int)) (Grid C (Maybe Int)) sudoku = (,)-    (drawIntClues <> sudokugrid)-    ((drawIntClues <> sudokugrid) . snd)+    (placeGrid . fmap drawInt . clues <> sudokugrid)+    ((placeGrid . fmap drawInt . clues <> sudokugrid) . snd)  thermosudoku :: Backend' b =>-                RenderPuzzle b (SGrid Int, [Thermometer]) IntGrid+                RenderPuzzle b (Grid C (Maybe Int), [Thermometer]) (Grid C (Maybe Int)) thermosudoku = (,)-    (drawInts . fst <> sudokugrid . fst <> drawThermos . snd)-    (drawIntClues . snd <> sudokugrid . snd <> drawThermos . snd . fst)+    (placeGrid . fmap drawInt . clues . fst <> sudokugrid . fst <> drawThermos . snd)+    (placeGrid . fmap drawInt . clues . snd <> sudokugrid . snd <> drawThermos . snd . fst)  pyramid :: Backend' b =>     RenderPuzzle b Pyr.Pyramid Pyr.PyramidSol@@ -100,158 +148,610 @@     merge (p, q) = Pyr.mergekpyramidsol p q  slither :: Backend' b =>-           RenderPuzzle b IntGrid Loop+           RenderPuzzle b (Grid C (Maybe Int)) (Loop N) slither = (,)     drawSlitherGrid     (drawSlitherGrid . fst <> solstyle . drawEdges . snd)  liarslither :: Backend' b =>-               RenderPuzzle b IntGrid (Loop, SGrid Bool)+               RenderPuzzle b (Grid C (Maybe Int)) (Loop N, Grid C Bool) liarslither = (,)     drawSlitherGrid-    (solstyle . drawCrosses . snd . snd+    (placeGrid . fmap (solstyle . drawCross) . snd . snd      <> drawSlitherGrid . fst      <> solstyle . drawEdges . fst . snd) +slithermulti :: Backend' b =>+                RenderPuzzle b (Grid C (Maybe Int), Int) [Edge N]+slithermulti = (,)+    (drawSlitherGrid . fst <> n)+    (drawSlitherGrid . fst . fst <> solstyle . drawEdges . snd)+  where+    n (g, l) = placeNote (size' g) (drawInt l ||| strutX 0.2 ||| miniloop)+    size' = size . Map.mapKeys toCoord+ tightfitskyscrapers :: Backend' b =>-                       RenderPuzzle b (OutsideClues (Maybe Int), SGrid (Tightfit ()))-                                      (SGrid (Tightfit Int))+                       RenderPuzzle b (OutsideClues C (Maybe Int), Grid C (Tightfit ()))+                                      (Grid C (Tightfit Int)) tightfitskyscrapers = (,)-    (atCentres drawInt . outsideClues . fst+    (placeGrid . fmap drawInt . clues . outsideClues . fst      <> drawTightGrid (const mempty) . snd)-    (atCentres drawInt . outsideClues . fst . fst+    (placeGrid . fmap drawInt . clues . outsideClues . fst . fst      <> drawTightGrid drawInt . snd)  wordgrid :: Backend' b =>-            SGrid (Maybe Char) -> [String] -> Diagram b R2-wordgrid g ws = stackWords ws `besidesR` drawClueGrid g+            Grid C (Maybe Char) -> [String] -> Diagram b+wordgrid g ws = stackWords ws `besidesR` drawCharGrid g  wordloop :: Backend' b =>-            RenderPuzzle b (CharClueGrid, [String]) CharClueGrid+            RenderPuzzle b (Grid C (Maybe Char), [String]) (Grid C (Maybe Char)) wordloop = (,)     (uncurry wordgrid)-    (drawClueGrid . snd)+    (drawCharGrid . snd)  wordsearch :: Backend' b =>-              RenderPuzzle b (CharClueGrid, [String]) (CharClueGrid, [MarkedWord])+              RenderPuzzle b (Grid C (Maybe Char), [String])+                             (Grid C (Maybe Char), [MarkedWord]) wordsearch = (,)     (uncurry wordgrid)      (solstyle . drawMarkedWords . snd . snd-     <> drawClueGrid . fst . snd)+     <> drawCharGrid . fst . snd)  curvedata :: Backend' b =>-             RenderPuzzle b (SGrid (Clue [Edge])) [Edge]+             RenderPuzzle b (Grid C (Maybe [Edge N])) [Edge C] curvedata = (,)     cd-    ((solstyle . drawDualEdges . snd) <> cd . fst)+    ((solstyle . drawEdges . snd) <> cd . fst)   where-    cd = atCentres drawCurve . clues <> grid . size+    cd = placeGrid . fmap drawCurve . clues <> grid gDefault  doubleback :: Backend' b =>-              RenderPuzzle b AreaGrid Loop+              RenderPuzzle b AreaGrid (Loop C) doubleback = (,)-    drawAreaGridGray-    (solstyle . drawDualEdges . snd <> drawAreaGridGray . fst)+    p+    (solstyle . drawEdges . snd <> p . fst)+  where+    p = grid gDefault <> drawAreasGray  slalom :: Backend' b =>-          RenderPuzzle b IntGrid (SGrid SlalomDiag)+          RenderPuzzle b (Grid N (Maybe Int)) (Grid C SlalomDiag) slalom = (,)-    drawSlalomGrid-    (drawSlalomGrid . fst <> solstyle . drawSlalomDiags . snd)+    p+    (p . fst <> placeGrid . fmap (solstyle . drawSlalomDiag) . snd)+  where+    p = placeGrid . fmap drawSlalomClue . clues+        <> grid gDefault . cellGrid  compass :: Backend' b =>-           RenderPuzzle b (SGrid (Clue CompassC)) AreaGrid+           RenderPuzzle b (Grid C (Maybe CompassC)) AreaGrid compass = (,)-    drawCompassGrid-    (drawCompassClues . fst <> drawAreaGridGray . snd)+    (placeGrid . fmap drawCompassClue . clues <> grid gDashed)+    (placeGrid . fmap drawCompassClue . clues . fst+     <> (grid gDashed <> drawAreasGray) . snd)  boxof2or3 :: Backend' b =>-             RenderPuzzle b (SGrid MasyuPearl, [Edge]) ()+             RenderPuzzle b (Grid N MasyuPearl, [Edge N]) () boxof2or3 = (,)-    (atCentres smallPearl . values . fst-     <> phantom' . grid . size . fst-     <> drawThinDualEdges . snd)+    (placeGrid . fmap smallPearl . fst+     <> drawThinEdges . snd)     (error "boxof2or3 solution not implemented")  afternoonskyscrapers :: Backend' b =>-                        RenderPuzzle b (SGrid Shade) IntGrid+                        RenderPuzzle b (Grid C Shade) (Grid C (Maybe Int)) afternoonskyscrapers = (,)-    (grid . size <> atCentres drawShade . values)-    (drawIntGrid . snd <> atCentres drawShade . values . fst)+    (grid gDefault <> placeGrid . fmap drawShadow)+    (drawIntGrid . snd <> placeGrid . fmap drawShadow . fst)  meanderingnumbers :: Backend' b =>-                        RenderPuzzle b AreaGrid IntGrid+                        RenderPuzzle b AreaGrid (Grid C (Maybe Int)) meanderingnumbers = (,)-    drawAreaGrid-    (drawIntGrid . snd <> drawAreaGrid . fst)+    (grid gDefault <> drawAreas)+    (drawIntGrid . snd <> drawAreas . fst)  tapa :: Backend' b =>-        RenderPuzzle b (SGrid TapaClue) ShadedGrid+        RenderPuzzle b (Grid C (Maybe TapaClue)) ShadedGrid tapa = (,)     tapaGrid-    (tapaGrid . fst <> drawShadedGrid . snd)+    (tapaGrid . fst <> drawShade . snd)   where-    tapaGrid = atCentres drawTapaClue . values <> grid . size+    tapaGrid = placeGrid . fmap drawTapaClue . clues <> grid gDefault  japanesesums :: Backend' b =>-                RenderPuzzle b (OutsideClues [Int]) (SGrid (Either Black Int))+                RenderPuzzle b (OutsideClues C [Int], String)+                               (Grid C (Either Black Int)) japanesesums = (,)-    outsideIntGrid-    (outsideIntGrid . fst <> japcells . snd)+    (outsideIntGrid . fst <> n)+    (outsideIntGrid . fst . fst <> japcells . snd)   where-    japcells = atCentres japcell . values+    n (ocs, ds) = placeNoteTL (0, h ocs) (drawText ds # scale 0.8)+    japcells = placeGrid . fmap japcell     japcell (Left Black) = fillBG gray     japcell (Right x) = drawInt x+    h = snd . outsideSize  coral :: Backend' b =>-          RenderPuzzle b (OutsideClues [String]) ShadedGrid+          RenderPuzzle b (OutsideClues C [String]) ShadedGrid coral = (,)-    outsideGrid-    (outsideGrid . fst <> drawShadedGrid . snd)+    drawMultiOutsideGrid+    (drawMultiOutsideGrid . fst <> drawShade . snd)  maximallengths :: Backend' b =>-                  RenderPuzzle b (OutsideClues (Maybe Int)) Loop+                  RenderPuzzle b (OutsideClues C (Maybe Int)) (Loop C) maximallengths = (,)     g-    (solstyle . drawDualEdges . snd <> g . fst)+    (solstyle . drawEdges . snd <> g . fst)   where-    g = atCentres drawInt . outsideClues-        <> grid . outsideSize+    g = placeGrid . fmap drawInt . clues . outsideClues+        <> grid gDefault . outsideGrid  primeplace :: Backend' b =>-              RenderPuzzle b (SGrid PrimeDiag) (SGrid Int)+              RenderPuzzle b (Grid C PrimeDiag) (Grid C Int) primeplace = (,)     g-    (atCentres drawInt . values . snd <> g . fst)+    (placeGrid . fmap drawInt . snd <> g . fst)   where-    g = irregularGrid <> atCentres drawPrimeDiag . values+    g = grid gStyle+        <> placeGrid . fmap drawPrimeDiag+    gStyle = GridStyle LineThin LineThick Nothing VertexNone  labyrinth :: Backend' b =>-             RenderPuzzle b (SGrid (Clue Int), [Edge]) (SGrid (Clue Int))+             RenderPuzzle b (Grid C (Maybe Int), [Edge N]) (Grid C (Maybe Int)) labyrinth = (,)-    (atCentres drawInt . clues . fst <> g)-    (atCentres drawInt . clues . snd <> g . fst)+    (placeGrid . fmap drawInt . clues . fst <> g)+    (placeGrid . fmap drawInt . clues . snd <> g . fst)   where-    g = drawEdges . snd <> plaingrid . size . fst+    g = drawEdges . snd <> grid gPlain . fst  bahnhof :: Backend' b =>-            RenderPuzzle b (SGrid (Maybe BahnhofClue)) [Edge]+            RenderPuzzle b (Grid C (Maybe BahnhofClue)) [Edge C] bahnhof = (,)-    (atCentres drawBahnhofClue . clues <> grid . size)-    (atCentres drawBahnhofStation . clues . fst-     <> solstyle . drawDualEdges . snd-     <> grid . size . fst)+    (placeGrid . fmap drawBahnhofClue . clues <> grid gDefault)+    (placeGrid . fmap drawBahnhofStation . clues . fst+     <> solstyle . drawEdges . snd+     <> grid gDefault . fst)   where     drawBahnhofStation = either drawInt (const mempty) +blackoutDominos :: Backend' b =>+                   RenderPuzzle b (Grid C (Clue Int), DigitRange)+                                  (Grid C (Clue Int), AreaGrid)+blackoutDominos = (,)+    p+    ((placeGrid . fmap drawInt . clues . fst+      <> grid gDashedThick . fst +      <> drawAreas . snd+      <> shadeGrid . fmap cols . snd) . snd)+  where+    p (g, ds) = (placeGrid . fmap drawInt . clues <> grid gDashedThick $ g)+                `aboveT`+                drawDominos ds+    cols 'X' = Just gray+    cols _   = Nothing++angleLoop ::+    Backend' b =>+    RenderPuzzle b (Grid N (Clue Int)) VertexLoop+angleLoop = (,)+    (cs <> gr)+    (cs . fst+     <> lineJoin LineJoinBevel . solstyle . strokeLocLoop . vertexLoop . snd+     <> gr . fst)+  where+    cs = placeGrid . fmap drawAnglePoly . clues+    gr = grid gPlainDashed . cellGrid++anglers ::+    Backend' b =>+    RenderPuzzle b (OutsideClues C (Clue Int), Grid C (Maybe Fish)) [Edge C]+anglers = (,)+    (p <> g)+    (p . fst <> solstyle . drawEdges . snd <> g . fst)+  where+    p = placeGrid . fmap drawInt' . clues . outsideClues . fst <>+        placeGrid . fmap drawFish' . clues . snd+    g = grid gDefault . snd+    drawInt' x = drawInt x <> (square 0.6 # lc white # fc white)+    drawFish' x = drawFish x <> (square 0.6 # lc white # fc white)+ cave ::-    (Backend b R2, Renderable (Path R2) b) =>-    RenderPuzzle b (SGrid (Clue Int)) ShadedGrid+    Backend' b =>+    RenderPuzzle b (Grid C (Maybe Int)) ShadedGrid cave = (,)-    g+    (grid gDashDash <> placeGrid . fmap drawInt . clues)     (drawEdges . edgesGen (/=) not . snd-     <> drawShadedGrid . snd <> fr . fst-     <> g . fst)+     <> placeGrid . fmap drawInt . clues . fst+     <> drawShade . snd+     <> grid gStyle . fst)   where-    g = gridDashing . plaingrid . size <> atCentres drawInt . clues-    fr gr = outframe' 8 (size gr) # lc gray+    gDashDash = GridStyle LineDashed LineDashed Nothing VertexNone+    gStyle = GridStyle LineDashed LineNone (Just $ FrameStyle 8 gray)+                       VertexNone++skyscrapers ::+    Backend' b =>+    RenderPuzzle b (OutsideClues C (Maybe Int), String) (Grid C (Maybe Int))+skyscrapers = (,)+    (g . fst <> n)+    (g . fst . fst <> placeGrid . fmap drawInt . clues . snd)+  where+    g = placeGrid . fmap drawInt . clues . outsideClues+        <> grid gDefault . outsideGrid+    n (oc, s) = placeNote (outsideSize oc) (drawText s)++shikaku :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) AreaGrid+shikaku = (,)+    p+    (drawAreas . snd <> p . fst)+  where+    p = placeGrid . fmap drawInt . clues <> grid gDashed++slovaksums :: Backend' b => RenderPuzzle b (Grid C (Maybe SlovakClue), String) (Grid C (Maybe Int))+slovaksums = (,)+    (p . fst <> n)+    (placeGrid . fmap drawInt . clues . snd <> p . fst . fst)+  where+    n (g, ds) = placeNote (size' g) (drawText ds # scale 0.8)+    p = grid gDefault <> placeGrid . fmap drawSlovakClue . clues+    size' = size . Map.mapKeys toCoord++skyscrapersStars ::+    Backend' b =>+    RenderPuzzle b (OutsideClues C (Maybe Int), Int)+                   (Grid C (Either Int Star))+skyscrapersStars = (,)+    (g <> n)+    (g . fst <> placeGrid . fmap (either drawInt drawStar) . snd)+  where+    g = (placeGrid . fmap drawInt . clues . outsideClues+         <> grid gDefault . outsideGrid) . fst+    n (oc, s) = placeNote (outsideSize oc)+                          (drawInt s ||| strutX 0.2 ||| drawStar Star)++summon ::+    Backend' b =>+    RenderPuzzle b (AreaGrid, OutsideClues C (Maybe Int), String) (Grid C (Maybe Int))+summon = (,)+    (p <> n)+    (placeGrid . fmap drawInt . clues . snd <> p . fst)+  where+    p (g, oc, _) = grid gDefault g <> drawAreasGray g+                <> (placeGrid . clues . outsideClues+                    . al . fmap (fmap (scale 0.7 . drawInt)) $ oc)+    al :: Backend' b => OutsideClues k (Maybe (Diagram b)) -> OutsideClues k (Maybe (Diagram b))+    al (OC l r b t) = OC l (map (fmap alignL) r) b t++    n (g, _, ds) = placeNoteBR (size' g) (drawText ds # scale 0.7)+    size' = size . Map.mapKeys toCoord++baca ::+    Backend' b =>+    RenderPuzzle b (Grid C (Maybe Char),+                    OutsideClues C [Int],+                    OutsideClues C (Maybe Char))+                   (Grid C (Either Black Char))+baca = (,)+    (inside <> outside)+    (outside . fst <> placeGrid . fmap drawVal . snd <> inside . fst)+  where+    inside (g,_,_) = placeGrid . fmap (fc gray . drawChar) . clues $ g+    outside (g,tl,br) =+              grid gDefault g+              <> (placeGrid . fmap drawInt+                  . multiOutsideClues $ tl)+              <> (placeGrid . fmap drawChar . clues+                  . outsideClues $ br)+    drawVal (Right c) = drawChar c+    drawVal (Left _) = fillBG gray++buchstabensalat ::+    Backend' b =>+    RenderPuzzle b (OutsideClues C (Maybe Char), String) (Grid C (Maybe Char))+buchstabensalat = (p <> n, p . fst <> placeGrid . fmap drawChar . clues . snd)+  where+    p = (placeGrid . fmap drawChar . clues . outsideClues+         <> grid gDefault . outsideGrid) . fst+    n (ocs, ls) = placeNote (outsideSize ocs) (drawText ls # scale 0.8)++doppelblock ::+    Backend' b =>+    RenderPuzzle b (OutsideClues C (Maybe Int))+                   (Grid C (Either Black Int))+doppelblock = (,)+    p+    (p . fst <> placeGrid . fmap drawVal . snd)+  where+    p = placeGrid . fmap (scale 0.8 . drawInt) . clues . outsideClues+        <> grid gDefault . outsideGrid+    drawVal (Right c) = drawInt c+    drawVal (Left _) = fillBG gray++sudokuDoppelblock ::+    Backend' b =>+    RenderPuzzle b (AreaGrid, OutsideClues C (Maybe Int))+                   (Grid C (Either Black Int))+sudokuDoppelblock = (,)+    p+    (p . fst <> placeGrid . fmap drawVal . snd)+  where+    p = placeGrid . fmap (scale 0.8 . drawInt) . clues . outsideClues . snd+        <> (grid gDefault <> drawAreas) . fst+    drawVal (Right c) = drawInt c+    drawVal (Left _) = fillBG gray++dominos ::+    Backend' b =>+    RenderPuzzle b (Grid C (Clue Int), DigitRange) AreaGrid+dominos = (,)+    p+    (placeGrid . fmap drawInt . clues . fst . fst+     <> (grid gDashed <> drawAreasGray) . snd)+  where+    p (g, r) =+        ((placeGrid . fmap drawInt . clues <> grid gDashed) $ g)+        `aboveT`+        drawDominos r++dominoPills ::+    Backend' b =>+    RenderPuzzle b (Grid C (Clue Int), DigitRange, DigitRange) AreaGrid+dominoPills = (,)+    p+    (placeGrid . fmap drawInt . clues . fst3 . fst+     <> (grid gDashed <> drawAreasGray) . snd)+  where+    fst3 (a,_,_) = a+    p (g, ds, ps) =+        ((placeGrid . fmap drawInt . clues <> grid gDashed) $ g)+        `aboveT`+        (drawDominos ds ||| strutX 0.5 ||| drawPills ps)++numberlink ::+    Backend' b =>+    RenderPuzzle b (Grid C (Maybe Int)) [Edge C]+numberlink = (,)+    drawIntGrid+    (placeGrid . fmap drawInt' . clues . fst+     <> solstyle . drawEdges . snd+     <> grid gDefault . fst)+  where+    drawInt' x = drawInt x <> (square 0.7 # lc white # fc white)++loopki :: Backend' b =>+          RenderPuzzle b (Grid C (Maybe MasyuPearl)) (Loop N)+loopki = (,)+    p+    (solstyle . drawEdges . snd <> p . fst)+  where+    p = placeGrid . fmap (scale 0.5 . pearl) . clues <> grid gSlither++scrabble :: Backend' b =>+            RenderPuzzle b (Grid C Bool, [String]) (Grid C (Maybe Char))+scrabble = (,)+    p+    (placeGrid . fmap drawCharFixed . clues . snd <> gr . fst . fst)+  where+    p (g, ws) = stackWords ws `besidesR` gr g+    gr = grid gDefault <> drawShade++neighbors :: Backend' b =>+             RenderPuzzle b (Grid C Bool, Grid C (Maybe Int)) (Grid C Int)+neighbors = (,)+    (placeGrid . fmap drawInt . clues . snd <> (grid gDefault <> drawShade) . fst)+    (placeGrid . fmap drawInt . snd <> (grid gDefault <> drawShade) . fst . fst)++starwars :: Backend' b =>+            RenderPuzzle b (AreaGrid, [MarkedLine C]) (Grid C (Maybe Star))+starwars = (,)+    p+    (p . fst <> placeGrid . fmap drawStar . clues . snd)+  where+    p = ((drawAreas <> grid gDefault) . fst <> drawMarkedLines . snd)++starbattle :: Backend' b =>+              RenderPuzzle b (AreaGrid, Int) (Grid C (Maybe Star))+starbattle = (,)+    (p <> n)+    ((p <> n) . fst <> placeGrid . fmap drawStar . clues . snd)+  where+    p = (drawAreas <> grid gDefault) . fst+    n (g, k) = placeNote (size' g)+                         (drawInt k ||| strutX 0.2 ||| drawStar Star)+    size' = size . Map.mapKeys toCoord++heyawake :: Backend' b =>+            RenderPuzzle b (AreaGrid, Grid C (Maybe Int)) (Grid C Bool)+heyawake = (,)+    (as <> cs)+    (as . fst <> drawShade . snd <> cs . fst)+  where+    as = (drawAreas <> grid gDefault) . fst+    cs = placeGrid . fmap drawInt . clues . snd++wormhole :: Backend' b =>+            RenderPuzzle b (Grid C (Maybe (Either Int Char))) ()+wormhole = (,)+    (placeGrid . fmap (either drawInt drawChar) . clues <> grid gDashed)+    mempty++pentominous ::+    Backend' b =>+    RenderPuzzle b (Grid C (Maybe Char)) (Grid C Char)+pentominous = (,)+    (placeGrid . fmap drawChar . clues <> grid gDashed)+    (placeGrid . fmap drawChar . clues . fst <>+     (drawAreas <> grid gDashed) . snd)++colorakari ::+    Backend' b =>+    RenderPuzzle b (Grid C (Maybe Char)) (Grid C (Maybe Char))+colorakari = (,)+    (placeGrid . fmap drawColorClue . clues <> grid gDefault)+    (error "color akari solution not implemented")+  where+    drawColorClue 'X' = fillBG black+    drawColorClue c = case col c of Nothing -> error "invalid color"+                                    Just c' -> drawText [c] # scale 0.5+                                               <> circle (1/3) # fc c'+                                               <> fillBG black+    col c = case c of 'R' -> Just red+                      'G' -> Just green+                      'B' -> Just blue+                      'Y' -> Just yellow+                      'C' -> Just cyan+                      'M' -> Just magenta+                      'W' -> Just white+                      _   -> Nothing++persistenceOfMemory ::+    Backend' b =>+    RenderPuzzle b (AreaGrid, (Grid C (Maybe MEnd))) (Loop C)+persistenceOfMemory = (,)+    (ends_ <> areas)+    (ends_ . fst <> solstyle . drawEdges . snd <> areas . fst)+  where+    ends_ = placeGrid . fmap drawEnd . clues . snd+    areas = (drawAreas <> grid gDashed <> shadeGrid . fmap cols) . fst+    cols c | isUpper c  = Just (blend 0.25 black white)+           | otherwise  = Nothing++abctje ::+    Backend' b =>+    RenderPuzzle b (DigitRange, [(String, Int)]) [(Int, Char)]+abctje = (,)+    p+    ((b . g . h  ||| const (strutX 1.0) ||| b . g . h') . snd)+  where+    p (ds, cs) = (digNote ds `aboveT` (stackWordsLeft ws ||| strutX 1.0 ||| stackWordsRight ns))+                 `besidesR` (strutX 2.0 ||| (b . g $ ps) ||| strutX 1.0 ||| (b . g $ ps'))+      where+        ws = map fst cs+        ns = map (show . snd) cs+        ls = nub . sort . concatMap fst $ cs+        ps = [ (x:[], "") | x <- ls ]+        ps' = [ (show x, "") | x <- digitList ds ]+    digNote (DigitRange x y) = note . drawText $ show x ++ "-" ++ show y+    b = placeGrid . fmap drawText <> grid gPlain+    h = sortOn fst . map (\(x, y) -> (y:[], show x))+    h' = map (\(x, y) -> (show x, y:[]))+    g ps = Map.fromList $+               [ (C 0 (l-i-1), x) | (i, x) <- zip [0..] c1 ] +++               [ (C 1 (l-i-1), x) | (i, x) <- zip [0..] c2 ]+      where+        l = length ps+        c1 = map fst ps+        c2 = map snd ps++kropki ::+    Backend' b =>+    RenderPuzzle b (Map.Map (Edge N) KropkiDot) (Grid C Int)+kropki = (,)+    p+    (placeGrid . fmap drawInt . snd <> p . fst)+  where+    p = placeGrid' . Map.mapKeys midPoint . fmap kropkiDot <> grid gDefault . sizeGrid . sz+    sz m = edgeSize (Map.keys m)++statuepark ::+    Backend' b =>+    RenderPuzzle b (Grid C (Maybe MasyuPearl)) (Grid C Bool)+statuepark = (,)+    p+    (p . fst <> drawShade . snd)+  where+    p = placeGrid . fmap pearl . clues <> grid gDashed++pentominousBorders ::+    Backend' b =>+    RenderPuzzle b (Grid C (), [Edge N]) (Grid C Char)+pentominousBorders = (,)+    (drawEdges . snd <> grid gDashed . fst)+    ((drawAreas <> grid gDashed) . snd)++nanroSignpost ::+    Backend' b =>+    RenderPuzzle b (AreaGrid, Grid C (Maybe Int)) (Grid C Int)+nanroSignpost = (,)+    p+    (placeGrid . fmap drawInt . snd <> p . fst)+  where+    p = ((drawAreas <> grid gDashed) . fst <> placeGrid . fmap hintTL . fmap show . clues . snd)++tomTom ::+    Backend' b =>+    RenderPuzzle b (AreaGrid, Grid C (Maybe String)) (Grid C Int)+tomTom = (,)+    p+    (placeGrid . fmap drawInt . snd <> p . fst)+  where+    p = ((drawAreas <> grid gDashed) . fst <> placeGrid . fmap hintTL . clues . snd)++horseSnake ::+    Backend' b =>+    RenderPuzzle b (Grid C (Maybe (Either MEnd Int))) [Edge C]+horseSnake = (,)+    p+    (solstyle . drawEdges . snd <> p . fst)+  where+    p = (placeGrid . fmap (either drawEnd drawInt) . clues <> grid gDashed)++illumination ::+    Backend' b =>+    RenderPuzzle b (OutsideClues C (Maybe Fraction)) (Grid N (Maybe PlainNode), [Edge N])+illumination = (,)+    p+    ((placeGrid . fmap (const (smallPearl MWhite)) . clues . fst <> drawEdges . snd) . snd <> p . fst)+  where+    p = placeGrid . fmap drawFraction . clues . outsideClues+        <> grid gDashed . outsideGrid++pentopia ::+    Backend' b =>+    RenderPuzzle b (Grid C (Maybe Myopia)) (Grid C Bool)+pentopia = (,)+    p+    (p . fst <> drawShade . snd)+  where+    p = placeGrid . fmap drawMyopia . clues <> grid gDefault++pentominoPipes ::+    Backend' b =>+    RenderPuzzle b (Grid N Char) (Grid N KropkiDot, [Edge N])+pentominoPipes = (,)+    (placeGrid . fmap drawCharOpaque <> grid gSlither . cellGrid)+    ((placeGrid . fmap kropkiDot . fst+      <> drawEdges . snd) . snd+     <> grid gSlither . cellGrid . fst)++greaterWall ::+    Backend' b =>+    RenderPuzzle b ([GreaterClue], [GreaterClue]) (Grid C Bool)+greaterWall = (,)+    ((plc <> grid gDefault . outsideGrid) . munge)+    undefined+  where+    munge (rs,cs) = OC (map (reverse . greaterClue) (reverse rs)) [] []+                       (map (map (rotateBy (-1/4))) . map (reverse . greaterClue) $ cs)+    plc ocs = placeGrid' . Map.mapKeys toPt . multiOutsideClues $ ocs+      where+        OC l _ _ _ = ocs+        h = length l+        h' = fromIntegral h+        -- toPoint c = p2 (1/2, 1/2) .+^ r2i (c .--. C 0 0)+        -- terrible hack+        toPt c@(C x y) | x < 0  = let p = toPoint c in scaleX 0.7 p .+^ r2 (-1/2, 0)+                       | y >= h = let p = toPoint c in scaleY 0.7 (p .-^ r2 (0,h')) .+^ r2 (0, 1/2 + h')+        toPt c = toPoint c++galaxies ::+    Backend' b =>+    RenderPuzzle b (Grid C (), Grid N (), Grid C (), Map.Map (Edge N) ()) AreaGrid+galaxies = (,)+    p+    (p . fst <> drawAreas . snd)+  where+    p = (gals <> grid gDashed . fst4)+    gal = const (kropkiDot KWhite)+    gals (_, a,b,c) = (placeGrid . fmap gal $ a)+                   <> (placeGrid . fmap gal $ b)+                   <> (placeGrid' . fmap gal . Map.mapKeys midPoint $ c)+    fst4 (a,_,_,_) = a
src/Diagrams/Puzzles/Pyramid.hs view
@@ -1,47 +1,41 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE TypeFamilies #-}  module Diagrams.Puzzles.Pyramid where  import Diagrams.Prelude -import Data.Puzzles.Elements import Data.Puzzles.Pyramid+import Diagrams.Puzzles.Elements import Diagrams.Puzzles.Lib import Diagrams.Puzzles.Widths  pgray :: Colour Double pgray = blend 0.6 white black -cell :: Backend' b => Bool -> Diagram b R2+cell :: Backend' b => Bool -> Diagram b cell s = square 1 # lwG onepix # if s then fc pgray else id -clue :: Backend' b => Maybe Int -> Diagram b R2+clue :: Backend' b => Maybe Int -> Diagram b clue Nothing = mempty clue (Just c) = text' (show c) -cellc :: Backend' b => Bool -> Maybe Int -> Diagram b R2+cellc :: Backend' b => Bool -> Maybe Int -> Diagram b cellc s c = clue c `atop` cell s -row :: Backend' b => Row -> Diagram b R2+row :: Backend' b => Row -> Diagram b row (R cs s) = centerX . hcat . map (cellc s) $ cs -pyramid :: Backend' b => Pyramid -> Diagram b R2+pyramid :: Backend' b => Pyramid -> Diagram b pyramid = alignBL . vcat . map row . unPyr -kropki :: Backend' b => KropkiDot -> Diagram b R2-kropki KNone = mempty-kropki c = circle 0.1 # lwG 0.03 # fc (col c) # smash-    where col KWhite = white-          col KBlack = blend 0.98 black white-          col KNone  = error "can't reach"--krow :: Backend' b => KropkiRow -> Diagram b R2+krow :: Backend' b => KropkiRow -> Diagram b krow (KR cs s ks) = ccat dots <> ccat clues     where ccat = centerX . hcat           clues = map (cellc s) cs-          dots = interleave (map phantom' clues) (map kropki ks)+          dots = interleave (map phantom clues) (map kropkiDot ks) -kpyramid :: Backend' b => RowKropkiPyramid -> Diagram b R2+kpyramid :: Backend' b => RowKropkiPyramid -> Diagram b kpyramid = alignBL . vcat . map krow . unKP
+ src/Diagrams/Puzzles/Style.hs view
@@ -0,0 +1,50 @@+module Diagrams.Puzzles.Style+    ( LineStyle (..)+    , FrameStyle (..)+    , VertexStyle (..)+    , GridStyle (..)++    , gDefault+    , gDashed+    , gDashedThick+    , gPlain+    , gPlainDashed+    , gSlither+    ) where++import Diagrams.Puzzles.Widths++import Diagrams.Prelude++data LineStyle =+      LineNone+    | LineThin+    | LineDashed+    | LineThick++data FrameStyle = FrameStyle+    { _fWidthFactor :: Double+    , _fColour      :: Colour Double+    }++data VertexStyle =+      VertexNone+    | VertexDot++data GridStyle = GridStyle+    { _line    :: LineStyle+    , _border  :: LineStyle+    , _frame   :: Maybe FrameStyle+    , _vertex  :: VertexStyle+    }++gDefault, gSlither, gDashed, gDashedThick, gPlain, gPlainDashed :: GridStyle+gDefault = GridStyle LineThin LineThin+                     (Just (FrameStyle framewidthfactor black)) VertexNone+gSlither = GridStyle LineNone LineNone Nothing VertexDot+gDashed  = GridStyle LineDashed LineThin+                     (Just (FrameStyle framewidthfactor black)) VertexNone+gDashedThick  = GridStyle LineDashed LineThick+                          Nothing VertexNone+gPlain   = GridStyle LineThin LineThin Nothing VertexNone+gPlainDashed = GridStyle LineDashed LineDashed Nothing VertexNone
src/Diagrams/Puzzles/Widths.hs view
@@ -19,6 +19,8 @@ framewidthfactor :: Double framewidthfactor = 4 -edgewidth, borderwidth :: Double+edgewidth :: Double edgewidth = 3 * onepix++borderwidth :: Double borderwidth = 1 / 2
+ src/Text/Puzzles/Code.hs view
@@ -0,0 +1,22 @@+{-# LANGUAGE OverloadedStrings #-}++module Text.Puzzles.Code where++import Data.Puzzles.Code+import Text.Puzzles.Util++import Data.Yaml++import Data.Maybe (catMaybes)++parseCode :: Value -> Parser [CodePart]+parseCode (Object v) = fmap catMaybes . sequenceA $+    [ fmap Rows'   <$> v .:? "cell_rows_bottom"+    , fmap Cols    <$> v .:? "cell_cols"+    , fmap RowsN'  <$> v .:? "node_rows_bottom"+    , fmap ColsN   <$> v .:? "node_cols"+    , fmap (LabelsN . fmap (fmap unAlpha . blankToMaybe)) <$> (do+        v' <- v .:? "node_labels"+        sequenceA (parseGrid <$> v'))+    ] +parseCode _          = fail "expected object"
+ src/Text/Puzzles/Parsec.hs view
@@ -0,0 +1,30 @@+-- | Parsec helper for puzzle file parsing.+module Text.Puzzles.Parsec (+    toParser+  , toStringParser+  , fraction+  ) where++import Text.ParserCombinators.Parsec hiding ((<|>))+import qualified Data.Text as T+import qualified Data.Yaml as Yaml+import Control.Applicative++import Data.Puzzles.Elements++toParser :: GenParser a () b -> [a] -> Yaml.Parser b+toParser p v = case parse p "(unknown)" v of Left e  -> fail (show e)+                                             Right x -> pure x++toStringParser :: GenParser Char () b -> Yaml.Value -> Yaml.Parser b+toStringParser p v = case v of+    Yaml.String t -> toParser p (T.unpack t)+    _             -> fail "expected string"++-- | fraction is meant to parse things like "1 1/2", "3/10", "7".+fraction :: GenParser Char st Fraction+fraction = do+    a <- many1 digit <* spaces+    FComp a <$> many1 digit <*>  (char '/' *> many1 digit)+      <|> FFrac a <$> (char '/' *> many1 digit)+      <|> pure (FInt a)
src/Text/Puzzles/Puzzle.hs view
@@ -7,14 +7,15 @@  import Data.Puzzles.PuzzleTypes -data TypedPuzzle = TP (Maybe String) Value (Maybe Value)+data TypedPuzzle = TP (Maybe String) Value (Maybe Value) (Maybe Value)     deriving Show  instance FromJSON TypedPuzzle where     parseJSON (Object v) = TP <$>                            v .:? "type" <*>                            v .:  "puzzle" <*>-                           v .:? "solution"+                           v .:? "solution" <*>+                           v .:? "code"     parseJSON _          = empty  -- | A pair of parsers for a puzzle type.
src/Text/Puzzles/PuzzleTypes.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeFamilies #-}  module Text.Puzzles.PuzzleTypes (     lits, litsplus, geradeweg, fillomino, masyu, nurikabe, latintapa,@@ -6,47 +7,64 @@     liarslither, tightfitskyscrapers, wordloop, wordsearch,     curvedata, doubleback, slalom, compass, boxof2or3,     afternoonskyscrapers, meanderingnumbers, tapa, japanesesums, coral,-    maximallengths, primeplace, labyrinth, bahnhof, cave+    maximallengths, primeplace, labyrinth, bahnhof, cave, angleLoop,+    shikaku, slovaksums, blackoutDominos,+    anglers, skyscrapers, summon, baca,+    buchstabensalat, doppelblock, sudokuDoppelblock, dominos,+    skyscrapersStars, numberlink, slithermulti, dominoPills,+    fillominoLoop, loopki, scrabble, neighbors, starwars,+    heyawake, wormhole, pentominous, starbattle, colorakari,+    persistenceOfMemory, abctje, kropki, statuepark, pentominousBorders,+    nanroSignpost, tomTom, horseSnake, illumination, pentopia,+    pentominoPipes, greaterWall, galaxies   ) where  import Control.Applicative import Control.Monad +import qualified Data.Map.Strict as M import Data.Yaml  import Text.Puzzles.Util import Text.Puzzles.Puzzle import Data.Puzzles.Grid-import Data.Puzzles.GridShape hiding (size)+import Data.Puzzles.GridShape import qualified Data.Puzzles.Pyramid as Pyr import Data.Puzzles.Elements -lits :: ParsePuzzle AreaGrid ShadedGrid+lits :: ParsePuzzle AreaGrid (Grid C Bool) lits = (parseGrid, parseShadedGrid) -litsplus :: ParsePuzzle AreaGrid ShadedGrid+litsplus :: ParsePuzzle AreaGrid (Grid C Bool) litsplus = lits -geradeweg :: ParsePuzzle (SGrid (Clue Int)) Loop+geradeweg :: ParsePuzzle (Grid C (Maybe Int)) (Loop C) geradeweg = (parseClueGrid, parseEdges) -fillomino :: ParsePuzzle IntGrid (SGrid Int)-fillomino = (parseClueGrid, parseExtGrid)+fillomino :: ParsePuzzle (Grid C (Maybe Int)) (Grid C Int)+fillomino = (parseExtClueGrid, parseExtGrid) -masyu :: ParsePuzzle (SGrid (Clue MasyuPearl)) Loop+fillominoLoop :: ParsePuzzle (Grid C (Maybe Int)) (Grid C Int, Loop C)+fillominoLoop = (,)+    parseClueGrid+    (\v -> (,) <$> parseFrom ["grid"] parseExtGrid v+               <*> parseFrom ["loop"] parseEdges v)++masyu :: ParsePuzzle (Grid C (Maybe MasyuPearl)) (Loop C) masyu = (parseClueGrid, parseEdges) -nurikabe :: ParsePuzzle IntGrid ShadedGrid-nurikabe = (parseSpacedClueGrid, parseShadedGrid)+nurikabe :: ParsePuzzle (Grid C (Maybe Int)) (Grid C Bool)+nurikabe = (parseExtClueGrid, parseShadedGrid) -latintapa :: ParsePuzzle (SGrid (Clue [String])) (SGrid (Maybe Char))+latintapa :: ParsePuzzle (Grid C (Maybe [String])) (Grid C (Maybe Char)) latintapa = ((unRG <$>) . parseJSON,              fmap (fmap (fmap unAlpha)) . parseClueGrid') -sudoku :: ParsePuzzle IntGrid IntGrid+sudoku :: ParsePuzzle (Grid C (Maybe Int)) (Grid C (Maybe Int)) sudoku = (parseClueGrid, parseClueGrid) -thermosudoku :: ParsePuzzle (SGrid Int, [Thermometer]) IntGrid+thermosudoku :: ParsePuzzle (Grid C (Maybe Int), [Thermometer])+                            (Grid C (Maybe Int)) thermosudoku = ((parseThermoGrid =<<) . parseJSON, parseClueGrid)  pyramid :: ParsePuzzle Pyr.Pyramid Pyr.PyramidSol@@ -55,25 +73,30 @@ kpyramid :: ParsePuzzle Pyr.RowKropkiPyramid Pyr.PyramidSol kpyramid = (parseJSON, parseJSON) -slither :: ParsePuzzle (SGrid (Clue Int)) Loop+slither :: ParsePuzzle (Grid C (Clue Int)) (Loop N) slither = (parseClueGrid, parseEdges) -newtype LSol = LSol { unLSol :: (Loop, SGrid Bool) }+slithermulti :: ParsePuzzle (Grid C (Clue Int), Int) [Edge N]+slithermulti = (p, parseEdges)+  where p v = (,) <$> parseFrom ["grid"] parseClueGrid v+                  <*> parseFrom ["loops"] parseJSON v++newtype LSol = LSol { unLSol :: (Loop N, Grid C Bool) } instance FromJSON LSol where     parseJSON (Object v) = LSol <$> ((,) <$>                            (parseEdges =<< v .: "loop") <*>                            (parseShadedGrid =<< v .: "liars"))     parseJSON _          = mzero -liarslither :: ParsePuzzle (SGrid (Clue Int)) (Loop, SGrid Bool)+liarslither :: ParsePuzzle (Grid C (Maybe Int)) (Loop N, Grid C Bool) liarslither = (parseClueGrid, (unLSol <$>) . parseJSON)  tightfitskyscrapers :: ParsePuzzle-                       (OutsideClues (Maybe Int), SGrid (Tightfit ()))-                       (SGrid (Tightfit Int))-tightfitskyscrapers = (parseTightOutside, parseTightIntGrid)+                       (OutsideClues C (Maybe Int), Grid C (Tightfit ()))+                       (Grid C (Tightfit Int))+tightfitskyscrapers = (parseTightOutside, parseSpacedGrid) -newtype GridWords = GW { unGW :: (CharClueGrid, [String]) }+newtype GridWords = GW { unGW :: (Grid C (Maybe Char), [String]) }  instance FromJSON GridWords where     parseJSON (Object v) = GW <$> ((,) <$>@@ -81,10 +104,10 @@                                    v .: "words")     parseJSON _ = empty -wordloop :: ParsePuzzle (CharClueGrid, [String]) CharClueGrid+wordloop :: ParsePuzzle (Grid C (Maybe Char), [String]) (Grid C (Maybe Char)) wordloop = ((unGW <$>) . parseJSON, parseClueGrid) -newtype GridMarked = GM { unGM :: (CharClueGrid, [MarkedWord]) }+newtype GridMarked = GM { unGM :: (Grid C (Maybe Char), [MarkedWord]) }  instance FromJSON GridMarked where     parseJSON (Object v) = GM <$> ((,) <$>@@ -92,60 +115,311 @@                                    (map unPMW <$> v .: "words"))     parseJSON _          = mzero -wordsearch :: ParsePuzzle (CharClueGrid, [String]) (CharClueGrid, [MarkedWord])+wordsearch :: ParsePuzzle (Grid C (Maybe Char), [String])+                          (Grid C (Maybe Char), [MarkedWord]) wordsearch = ((unGW <$>) . parseJSON, (unGM <$>) . parseJSON) -newtype Curve = Curve { unCurve :: [Edge] }+newtype Curve = Curve { unCurve :: [Edge N] }  instance FromJSON Curve where     parseJSON v = Curve <$> parsePlainEdges v -curvedata :: ParsePuzzle (SGrid (Clue [Edge])) [Edge]+curvedata :: ParsePuzzle (Grid C (Maybe [Edge N])) [Edge C] curvedata = ((fmap (fmap unCurve) . unRG <$>) . parseJSON, parsePlainEdges) -doubleback :: ParsePuzzle AreaGrid Loop+doubleback :: ParsePuzzle AreaGrid (Loop C) doubleback = (parseGrid, parseEdges) -slalom :: ParsePuzzle (SGrid (Clue Int)) (SGrid SlalomDiag)-slalom = (parseClueGrid, \v -> rectToSGrid <$> parseJSON v)+slalom :: ParsePuzzle (Grid N (Maybe Int)) (Grid C SlalomDiag)+slalom = (parseClueGrid, parseGrid) -compass :: ParsePuzzle (SGrid (Clue CompassC)) CharGrid+compass :: ParsePuzzle (Grid C (Maybe CompassC)) AreaGrid compass = ((fmap (fmap unPCC) . unRG <$>) . parseJSON, parseGrid) -boxof2or3 :: ParsePuzzle (SGrid MasyuPearl, [Edge]) ()+boxof2or3 :: ParsePuzzle (Grid N MasyuPearl, [Edge N]) () boxof2or3 = (parseNodeEdges, error "boxof2or3 parsing not implemented") -afternoonskyscrapers :: ParsePuzzle (SGrid Shade) IntGrid+afternoonskyscrapers :: ParsePuzzle (Grid C Shade) (Grid C (Maybe Int)) afternoonskyscrapers = (parseAfternoonGrid, parseGrid)  -- this should be changed to support clue numbers-meanderingnumbers :: ParsePuzzle AreaGrid IntGrid+meanderingnumbers :: ParsePuzzle AreaGrid (Grid C (Maybe Int)) meanderingnumbers = (parseGrid, parseGrid) -tapa :: ParsePuzzle (SGrid TapaClue) ShadedGrid-tapa = (\v -> fmap unParseTapaClue . unRG <$> parseJSON v,+tapa :: ParsePuzzle (Grid C (Maybe TapaClue)) (Grid C Bool)+tapa = (\v -> fmap (fmap unParseTapaClue) . unRG <$> parseJSON v,         parseShadedGrid) -japanesesums :: ParsePuzzle (OutsideClues [Int]) (SGrid (Either Black Int))-japanesesums = (parseMultiOutsideClues, parseGrid)+japanesesums :: ParsePuzzle (OutsideClues C [Int], String)+                            (Grid C (Either Black Int))+japanesesums = (p, parseGrid)+  where+    p v@(Object o) = (,) <$> parseMultiOutsideClues v <*> o .: "digits"+    p _            = empty -coral :: ParsePuzzle (OutsideClues [String]) ShadedGrid+coral :: ParsePuzzle (OutsideClues C [String]) (Grid C Bool) coral = (,)     (fmap (fmap (map unIntString)) . parseMultiOutsideClues)     parseShadedGrid -maximallengths :: ParsePuzzle (OutsideClues (Maybe Int)) Loop+maximallengths :: ParsePuzzle (OutsideClues C (Maybe Int)) (Loop C) maximallengths = (\v -> fmap blankToMaybe <$> parseCharOutside v,                   parseEdges) -primeplace :: ParsePuzzle (SGrid PrimeDiag) (SGrid Int)+primeplace :: ParsePuzzle (Grid C PrimeDiag) (Grid C Int) primeplace = (parseIrregGrid, parseIrregGrid) -labyrinth :: ParsePuzzle (SGrid (Clue Int), [Edge]) (SGrid (Clue Int))+labyrinth :: ParsePuzzle (Grid C (Maybe Int), [Edge N]) (Grid C (Maybe Int)) labyrinth = (parseCellEdges, parseClueGrid') -bahnhof :: ParsePuzzle (SGrid (Maybe BahnhofClue)) [Edge]+bahnhof :: ParsePuzzle (Grid C (Maybe BahnhofClue)) [Edge C] bahnhof = (parseClueGrid, parseEdges) -cave :: ParsePuzzle (SGrid (Clue Int)) ShadedGrid+blackoutDominos :: ParsePuzzle (Grid C (Clue Int), DigitRange)+                               (Grid C (Clue Int), AreaGrid)+blackoutDominos = (,)+    (\v -> (,) <$> parseFrom ["grid"] parseIrregGrid v+               <*> parseFrom ["digits"] parseStringJSON v)+    (\v -> (,) <$> parseFrom ["values"] parseIrregGrid v+               <*> parseFrom ["dominos"] parseIrregGrid v)++angleLoop :: ParsePuzzle (Grid N (Clue Int)) VertexLoop+angleLoop = (parseClueGrid, parseCoordLoop)++shikaku :: ParsePuzzle (Grid C (Maybe Int)) AreaGrid+shikaku = (parseExtClueGrid, parseGrid)++slovaksums :: ParsePuzzle (Grid C (Maybe SlovakClue), String) (Grid C (Maybe Int))+slovaksums = (p, parseClueGrid)+  where+    p v@(Object o) = (,) <$> g v <*> o .: "digits"+    p _ = empty+    g = (fmap (fmap unPSlovakClue) . unRG <$>) . parseJSON++anglers :: ParsePuzzle (OutsideClues C (Maybe Int), Grid C (Maybe Fish)) [Edge C]+anglers = ( parseOutsideGridMap blankToMaybe blankToMaybe'+          , parseEdgesFull )++cave :: ParsePuzzle (Grid C (Maybe Int)) (Grid C Bool) cave = (parseClueGrid, parseShadedGrid)++parseOut :: FromJSON a =>+            Value -> Parser (OutsideClues k (Maybe a))+parseOut v = fmap (blankToMaybe' . unEither') <$> parseOutside v++skyscrapers :: ParsePuzzle (OutsideClues C (Maybe Int), String) (Grid C (Maybe Int))+skyscrapers = (,)+    (\v -> (,) <$> parseOut v+               <*> parseFrom ["digits"] parseJSON v)+    parseClueGrid++skyscrapersStars :: ParsePuzzle (OutsideClues C (Maybe Int), Int)+                                (Grid C (Either Int Star))+skyscrapersStars = (p, parseGrid)+  where+    p v@(Object o) = (,) <$> parseOut v <*> o .: "stars"+    p _            = empty++summon :: ParsePuzzle (AreaGrid, OutsideClues C (Maybe Int), String) (Grid C (Maybe Int))+summon = ( \v@(Object o) -> (,,) <$> parseFrom ["grid"] parseGrid v+                                 <*> parseFrom ["outside"] parseOut v+                                 <*> o .: "digits"+         , parseClueGrid+         )++baca :: ParsePuzzle+            (Grid C (Maybe Char), OutsideClues C [Int], OutsideClues C (Maybe Char))+            (Grid C (Either Black Char))+baca = ( \v -> (,,) <$> parseFrom ["grid"] parseClueGrid v+                    <*> parseFrom ["outside"] parseTopLeft v+                    <*> parseFrom ["outside"] parseBottomRight v+       , parseGrid+       )+  where+    parseTopLeft (Object v) = do+        l <- reverse <$> v .: "left"+        t <- v .: "top"+        return $ OC (map reverse l) [] [] (map reverse t)+    parseTopLeft _ = empty+    parseBottomRight (Object v) = do+        b <- v .: "bottom"+        r <- reverse <$> v .: "right"+        oc <- OC [] <$> parseLine r <*> parseLine b <*> pure []+        return $ fmap blankToMaybe' oc+    parseBottomRight _ = empty++buchstabensalat :: ParsePuzzle (OutsideClues C (Maybe Char), String)+                               (Grid C (Maybe Char))+buchstabensalat =+    ( p+    , fmap (fmap blankToMaybe') . parseGrid+    )+  where+    p v = (,)+        <$> (fmap blankToMaybe <$> parseCharOutside v)+        <*> parseFrom ["letters"] parseJSON v++doppelblock :: ParsePuzzle (OutsideClues C (Maybe Int))+                           (Grid C (Either Black Int))+doppelblock =+    ( \v -> fmap (blankToMaybe' . unEither') <$> parseOutside v+    , parseGrid+    )++sudokuDoppelblock :: ParsePuzzle (AreaGrid, OutsideClues C (Maybe Int))+                                 (Grid C (Either Black Int))+sudokuDoppelblock =+    ( \v -> (,) <$> parseFrom ["grid"] parseGrid v+                <*> parseFrom ["outside"] parseOutInts v+    , parseGrid+    )+  where+    parseOutInts v = fmap (blankToMaybe' . unEither') <$> parseOutside v++dominos :: ParsePuzzle (Grid C (Maybe Int), DigitRange) AreaGrid+dominos = (p, parseGrid)+  where+    p v = (,) <$> parseFrom ["grid"] parseClueGrid v+              <*> parseFrom ["digits"] parseStringJSON v++dominoPills :: ParsePuzzle (Grid C (Maybe Int), DigitRange, DigitRange)+                           AreaGrid+dominoPills = (p, parseGrid)+  where+    p v = (,,) <$> parseFrom ["grid"] parseClueGrid v+               <*> parseFrom ["digits"] parseStringJSON v+               <*> parseFrom ["pills"] parseStringJSON v++numberlink :: ParsePuzzle (Grid C (Maybe Int)) [Edge C]+numberlink = (p, fmap collectLines . p)+  where+    p = fmap (fmap (blankToMaybe . unEither')) . parseExtGrid++loopki :: ParsePuzzle (Grid C (Maybe MasyuPearl)) (Loop N)+loopki = (parseClueGrid, parseEdges)++scrabble :: ParsePuzzle (Grid C Bool, [String]) (Grid C (Maybe Char))+scrabble = (p, parseClueGrid)+  where+    p v = (,) <$> parseFrom ["grid"] parseStarGrid v+              <*> parseFrom ["words"] parseJSON v+    parseStarGrid v = fmap ((==) '*') <$> parseGrid v++neighbors :: ParsePuzzle (Grid C Bool, Grid C (Maybe Int)) (Grid C Int)+neighbors = (p, parseGrid)+  where+    p v = (,) <$> parseFrom ["shading"] parseShadedGrid v+              <*> parseFrom ["clues"] parseGrid v++starwars :: ParsePuzzle (AreaGrid, [MarkedLine C]) (Grid C (Maybe Star))+starwars = (p, parseClueGrid)+  where+    p v = (,) <$> parseFrom ["grid"] parseGrid v+              <*> (map unPML <$> parseFrom ["lines"] parseJSON v)++starbattle :: ParsePuzzle (AreaGrid, Int) (Grid C (Maybe Star))+starbattle = (p, parseClueGrid)+  where+    p v@(Object o) = (,) <$> parseFrom ["grid"] parseGrid v+                         <*> o .: "stars"+    p _            = empty++heyawake :: ParsePuzzle (AreaGrid, Grid C (Maybe Int)) (Grid C Bool)+heyawake = (p, parseShadedGrid)+  where+    p v = (,) <$> parseFrom ["rooms"] parseGrid v+              <*> parseFrom ["clues"] parseClueGrid v++wormhole :: ParsePuzzle (Grid C (Maybe (Either Int Char))) ()+wormhole = (,) p (const $ return ())+  where+    p v = fmap (fmap unEither') <$> parseExtClueGrid v++pentominous :: ParsePuzzle (Grid C (Maybe Char)) (Grid C Char)+pentominous = (,) parseClueGrid parseGrid++colorakari :: ParsePuzzle (Grid C (Maybe Char)) (Grid C (Maybe Char))+colorakari = (,) parseClueGrid parseClueGrid++persistenceOfMemory :: ParsePuzzle (AreaGrid, Grid C (Maybe MEnd)) (Loop C)+persistenceOfMemory = (p, parseEdgesFull)+  where+    p v = do g <- parseGrid v+             return (areas g, ends_ g)+    areas = fmap (\c -> case c of 'o' -> '.'+                                  _   -> c)+    ends_ = fmap (\c -> case c of 'o' -> Just MEnd+                                  _   -> Nothing)++abctje :: ParsePuzzle (DigitRange, [(String, Int)]) [(Int, Char)]+abctje = (,)+    (\v -> (,) <$> parseFrom ["numbers"] parseStringJSON v+               <*> parseFrom ["clues"] pl v)+    (\v -> pl v >>= sequence . map x)+  where+    pl :: FromJSON b => Value -> Parser [(String, b)]+    pl v = parseJSON v >>= sequence . map pair++    x :: FromString a => (String, b) -> Parser (a, b)+    x (k, v) = (\k' -> (k',v)) <$> parseString k++    pair :: M.Map a b -> Parser (a, b)+    pair m = if M.size m == 1 then (return . head . M.toList $ m) else empty++kropki :: ParsePuzzle (M.Map (Edge N) KropkiDot) (Grid C Int)+kropki = (,) parseAnnotatedEdges parseGrid++statuepark :: ParsePuzzle (Grid C (Maybe MasyuPearl)) (Grid C Bool)+statuepark = (\v -> parseFrom ["grid"] parseClueGrid v, parseShadedGrid)++pentominousBorders :: ParsePuzzle (Grid C (), [Edge N]) (Grid C Char)+pentominousBorders = (,) parseCellEdges parseGrid++nanroSignpost :: ParsePuzzle (AreaGrid, Grid C (Maybe Int)) (Grid C Int)+nanroSignpost = (,)+    (\v -> (,) <$> parseFrom ["rooms"] parseGrid v <*> parseFrom ["clues"] parseGrid v)+    parseGrid++tomTom :: ParsePuzzle (AreaGrid, Grid C (Maybe String)) (Grid C Int)+tomTom = (,)+    (\v -> (,) <$> parseFrom ["rooms"] parseGrid v <*> parseFrom ["clues"] ((unRG <$>) . parseJSON) v)+    parseGrid++horseSnake :: ParsePuzzle (Grid C (Maybe (Either MEnd Int))) [Edge C]+horseSnake = (parseGrid, parseEdgesFull)++illumination :: ParsePuzzle (OutsideClues C (Maybe Fraction)) (Grid N (Maybe PlainNode), [Edge N])+illumination = (,)+    (fmap (fmap (fmap unPFraction)) . parseOut)+    parseNodeEdges++newtype Myo = Myo { unMyo :: Myopia }+instance FromJSON Myo where+    parseJSON v = do+        s <- parseJSON v+        fmap Myo . sequence . map parseChar $ s++pentopia :: ParsePuzzle (Grid C (Maybe Myopia)) (Grid C Bool)+pentopia = (,)+    (fmap (fmap (fmap unMyo)) . fmap unRG . parseJSON)+    parseShadedGrid++pentominoPipes :: ParsePuzzle (Grid N Char) (Grid N KropkiDot, [Edge N])+pentominoPipes = (,)+    parseGrid+    parseNodeEdges++greaterWall :: ParsePuzzle ([GreaterClue], [GreaterClue]) (Grid C Bool)+greaterWall = (,)+    (\v -> (,) <$> parseFrom ["rows"] parseGreaterClues v+               <*> parseFrom ["columns"] parseGreaterClues v)+    parseShadedGrid++galaxies :: ParsePuzzle (Grid C (), Grid N (), Grid C (), M.Map (Edge N) ()) AreaGrid+galaxies = (,)+    (\v -> do (a,b,c) <- parseEdgeGrid v+              return $ (fmap (const ()) b, f a, f b, f c))+    parseGrid+  where+    toUnit GalaxyCentre = ()+    f = fmap toUnit . M.mapMaybe id . fmap blankToMaybe''
src/Text/Puzzles/Util.hs view
@@ -1,4 +1,6 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE TypeFamilies #-}  module Text.Puzzles.Util where @@ -8,14 +10,15 @@ import Control.Arrow import Control.Monad hiding (mapM) -import Data.Hashable-import Data.Maybe (catMaybes, fromMaybe)+import Data.List (sortBy, intersect)+import Data.Maybe (catMaybes, fromMaybe, isJust, fromJust)+import Data.Ord (comparing)+import Data.Either (isRight) import qualified Data.Map as Map+import qualified Data.Set as Set import qualified Data.HashMap.Strict as HMap-import Data.Traversable (traverse, sequenceA, mapM, Traversable)-import Data.Foldable (Foldable, foldMap)+import Data.Traversable (mapM) import Data.Monoid ((<>))-import Data.List (intersect)  import Data.Char (digitToInt, isAlpha, isDigit) import Text.Read (readMaybe)@@ -24,33 +27,46 @@ import Data.Yaml  import Data.Puzzles.Grid-import Data.Puzzles.GridShape hiding (size)+import Data.Puzzles.GridShape import Data.Puzzles.Elements +import Text.Puzzles.Parsec+ type Path = [String] +impossible :: a+impossible = error "impossible"+ field :: Path -> Value -> Parser Value field = field' . map T.pack   where-    field' [] v                = pure v+    field' [] v              = pure v     field' (f:fs) (Object v) = v .: f >>= field' fs-    field' _  _                = empty+    field' (f:_)  _          = fail $ "expected field '" ++ T.unpack f ++ "'" -parseFrom :: Path -> (Value -> Parser b) -> (Value -> Parser b)+parseFrom :: Path -> (Value -> Parser b) -> Value -> Parser b parseFrom fs p v = field fs v >>= p +chars :: [Char] -> Char -> Parser Char+chars cs c = if c `elem` cs+                 then pure c+                 else (fail $ "got '" ++ [c] ++ "', expected '" ++ cs ++ "'")++char :: Char -> Char -> Parser Char+char c = chars [c]+ class FromChar a where     parseChar :: Char -> Parser a -failChar :: Char -> String -> Parser a-failChar c expect = fail $ "got '" ++ [c] ++ "', expected " ++ expect- instance FromChar Char where     parseChar = pure  class FromString a where     parseString :: String -> Parser a +parseStringJSON :: FromString a => Value -> Parser a+parseStringJSON v = parseJSON v >>= parseString+ parseLine :: FromChar a => String -> Parser [a] parseLine = mapM parseChar @@ -112,21 +128,25 @@ data BorderedRect a b = BorderedRect !Int !Int [[a]] (Border b)     deriving Show +parseBorderedRect :: (Char -> Parser a) -> (Char -> Parser b)+                  -> Value -> Parser (BorderedRect a b)+parseBorderedRect parseIn parseOut v = do+    Rect w h ls <- parseJSON v+    let b = Border (reverse . map head . middle h $ ls)+                   (reverse . map last . middle h $ ls)+                   (middle w . last $ ls)+                   (middle w . head $ ls)+        ls' = map (middle w) . middle h $ ls+    mapM_ ((parseChar :: Char -> Parser Space) . flip ($) ls)+          [head . head, head . last, last . head, last . last]+    lsparsed <- mapM (mapM parseIn) ls'+    bparsed  <- mapM parseOut b+    return $ BorderedRect (w-2) (h-2) lsparsed bparsed+  where+    middle len = take (len - 2) . drop 1+ instance (FromChar a, FromChar b) => FromJSON (BorderedRect a b) where-    parseJSON v = do-        Rect w h ls <- parseJSON v-        let b = Border (reverse . map head . middle h $ ls)-                       (reverse . map last . middle h $ ls)-                       (middle w . last $ ls)-                       (middle w . head $ ls)-            ls' = map (middle w) . middle h $ ls-        mapM_ ((parseChar :: Char -> Parser Space) . flip ($) ls)-              [head . head, head . last, last . head, last . last]-        lsparsed <- mapM (mapM parseChar) ls'-        bparsed  <- mapM parseChar b-        return $ BorderedRect (w-2) (h-2) lsparsed bparsed-      where-        middle len = take (len - 2) . drop 1+    parseJSON = parseBorderedRect parseChar parseChar  newtype SpacedRect a = SpacedRect { unSpaced :: Rect a } @@ -141,19 +161,21 @@         p = mapM (mapM (parseString . T.unpack)) ls     parseJSON _          = empty +instance FromChar () where+    parseChar = fmap (const ()) . chars ['.', ' ']+ data Space = Space  instance FromChar Space where-    parseChar ' ' = pure Space-    parseChar _   = empty+    parseChar = fmap (const Space) . char ' '  data Blank = Blank data Blank' = Blank'+data Blank'' = Blank'' data Empty = Empty  instance FromChar Blank where-    parseChar '.' = pure Blank-    parseChar _   = empty+    parseChar = fmap (const Blank) . char '.'  parseCharJSON :: FromChar a => Value -> Parser a parseCharJSON v = do@@ -164,33 +186,32 @@    parseJSON = parseCharJSON  instance FromChar Blank' where-    parseChar '.' = pure Blank'-    parseChar '-' = pure Blank'-    parseChar _   = empty+    parseChar = fmap (const Blank') . chars ['.', '-']  instance FromJSON Blank' where     parseJSON (String ".") = pure Blank'     parseJSON (String "-") = pure Blank'-    parseJSON _            = empty+    parseJSON _            = fail "expected '.-'" +instance FromChar Blank'' where+    parseChar = fmap (const Blank'') . chars ['.', ' ', '-', '|']+ instance FromChar Empty where-    parseChar ' ' = pure Empty-    parseChar _   = empty+    parseChar = fmap (const Empty) . char ' '  instance FromString Blank where     parseString "." = pure Blank-    parseString _   = empty--data PlainNode = PlainNode+    parseString _   = fail "expected '.'"  instance FromChar PlainNode where-    parseChar 'o' = pure PlainNode-    parseChar _   = empty+    parseChar = fmap (const PlainNode) . char 'o'  instance FromChar MasyuPearl where-    parseChar '*' = pure MBlack-    parseChar 'o' = pure MWhite-    parseChar c   = failChar c "'*' or 'o'"+    parseChar = fmap f . chars ['*', 'o']+      where+        f '*' = MBlack+        f 'o' = MWhite+        f _   = impossible  instance FromChar SlalomDiag where     parseChar '/'  = pure SlalomForward@@ -198,10 +219,20 @@     parseChar _    = empty  instance FromChar Black where-    parseChar 'X' = pure Black-    parseChar 'x' = pure Black-    parseChar _   = empty+    parseChar = fmap (const Black) . chars "xX" +instance FromChar Fish where+    parseChar = fmap (const Fish) . char '*'++instance FromChar Star where+    parseChar = fmap (const Star) . char '*'++instance FromChar MEnd where+    parseChar = fmap (const MEnd) . chars "o*"++instance FromChar GalaxyCentre where+    parseChar = fmap (const GalaxyCentre) . char 'o'+ instance (FromChar a, FromChar b) => FromChar (Either a b) where     parseChar c = Left <$> parseChar c <|> Right <$> parseChar c @@ -220,15 +251,15 @@ instance FromChar a => FromChar (Maybe a) where     parseChar = optional . parseChar -listListToMap :: [[a]] -> Map.Map (Cell Square) a+listListToMap :: [[a]] -> Grid Coord a listListToMap ls = Map.fromList . concat                  . zipWith (\y -> zipWith (\x -> (,) (x, y)) [0..]) [h-1,h-2..]                  $ ls   where     h = length ls -rectToSGrid :: Rect a -> SGrid a-rectToSGrid (Rect w h ls) = Grid (Square w h) (listListToMap ls)+rectToCoordGrid :: Rect a -> Grid Coord a+rectToCoordGrid (Rect _ _ ls) = listListToMap ls  blankToMaybe :: Either Blank a -> Maybe a blankToMaybe = either (const Nothing) Just@@ -236,18 +267,14 @@ blankToMaybe' :: Either Blank' a -> Maybe a blankToMaybe' = either (const Nothing) Just -rectToClueGrid :: Rect (Either Blank a) -> SGrid (Clue a)-rectToClueGrid = fmap blankToMaybe . rectToSGrid--rectToClueGrid' :: Rect (Either Blank' a) -> SGrid (Clue a)-rectToClueGrid' = fmap blankToMaybe' . rectToSGrid+blankToMaybe'' :: Either Blank'' a -> Maybe a+blankToMaybe'' = either (const Nothing) Just -rectToIrregGrid :: Rect (Either Empty a) -> SGrid a-rectToIrregGrid = fmap fromRight . filterG isRight . rectToSGrid+rectToIrregGrid :: Rect (Either Empty a) -> Grid Coord a+rectToIrregGrid = fmap fromRight . Map.filter isRight . rectToCoordGrid   where-    isRight = either (const False) (const True)     fromRight (Right r) = r-    fromRight _         = error "no way"+    fromRight _         = impossible  newtype Shaded = Shaded { unShaded :: Bool } @@ -256,70 +283,102 @@     parseChar 'X'  = pure . Shaded $ True     parseChar _    = pure . Shaded $ False -parseShadedGrid :: Value -> Parser (SGrid Bool)-parseShadedGrid v = rectToSGrid . fmap unShaded <$> parseJSON v+parseShadedGrid :: Key k => Value -> Parser (Grid k Bool)+parseShadedGrid v = fmap unShaded <$> parseGrid v -parseGrid :: FromChar a => Value -> Parser (SGrid a)-parseGrid v = rectToSGrid <$> parseJSON v+parseCoordGrid :: (FromChar a)+               => Value -> Parser (Grid Coord a)+parseCoordGrid v = rectToCoordGrid <$> parseJSON v -parseGridWith :: (Char -> Parser a) -> Value -> Parser (SGrid a)+parseGrid :: (Key k, FromChar a)+          => Value -> Parser (Grid k a)+parseGrid v = fromCoordGrid <$> parseCoordGrid v++parseGridWith :: Key k+              => (Char -> Parser a) -> Value -> Parser (Grid k a) parseGridWith pChar v = traverse pChar =<< parseGrid v  parseWithReplacement :: FromChar a =>     (Char -> Maybe a) -> Char -> Parser a parseWithReplacement replace c = maybe (parseChar c) pure (replace c) +parseSpacedGrid :: (Key k, FromString a)+                => Value -> Parser (Grid k a)+parseSpacedGrid v = fromCoordGrid . rectToCoordGrid . unSpaced <$> parseJSON v+ parseCharMap :: FromJSON a => Value -> Parser (Map.Map Char a) parseCharMap v = do     m <- parseJSON v     guard . all (\k -> length k == 1) . Map.keys $ m     return $ Map.mapKeys head m -parseExtGrid :: (FromChar a, FromJSON a) => Value -> Parser (SGrid a)-parseExtGrid v@(String _) = parseGrid v-parseExtGrid v = do-    repl <- parseFrom ["replace"] parseCharMap v+parseExtGrid' :: (Key k, FromJSON a, FromChar b)+              => (a -> b) -> Value -> Parser (Grid k b)+parseExtGrid' _ v@(String _) = parseGrid v+parseExtGrid' f v = do+    repl <- fmap f <$> parseFrom ["replace"] parseCharMap v     parseFrom ["grid"] (parseGridWith                         (parseWithReplacement (`Map.lookup` repl))) v -parseClueGrid :: FromChar a => Value -> Parser (SGrid (Clue a))-parseClueGrid v = rectToClueGrid <$> parseJSON v+parseExtGrid :: (Key k, FromChar a, FromJSON a) => Value -> Parser (Grid k a)+parseExtGrid = parseExtGrid' id -parseClueGrid' :: FromChar a => Value -> Parser (SGrid (Clue a))-parseClueGrid' v = rectToClueGrid' <$> parseJSON v+parseExtClueGrid :: (Key k, FromChar a, FromJSON a) => Value -> Parser (Grid k (Maybe a))+parseExtClueGrid v = fmap blankToMaybe <$> parseExtGrid' Right v -parseIrregGrid :: FromChar a => Value -> Parser (SGrid a)-parseIrregGrid v = rectToIrregGrid <$> parseJSON v+fromCoordGrid :: Key k => Grid Coord a -> Grid k a+fromCoordGrid = Map.mapKeys fromCoord -parseSpacedClueGrid :: FromString a => Value -> Parser (SGrid (Clue a))-parseSpacedClueGrid v = rectToClueGrid . unSpaced <$> parseJSON v+fromCoordEdge :: Key k => Edge Coord -> Edge k+fromCoordEdge (E c d) = E (fromCoord c) d +fromCoordEdges :: Key k => [Edge Coord] -> [Edge k]+fromCoordEdges = map fromCoordEdge++parseClueGrid :: (FromChar a, Key k)+              => Value -> Parser (Grid k (Maybe a))+parseClueGrid v = fmap blankToMaybe <$> parseGrid v++parseClueGrid' :: (FromChar a, Key k) => Value -> Parser (Grid k (Maybe a))+parseClueGrid' v = fmap blankToMaybe' <$> parseGrid v++parseSpacedClueGrid :: (Key k, FromString a) => Value -> Parser (Grid k (Maybe a))+parseSpacedClueGrid v = fmap blankToMaybe <$> parseSpacedGrid v++parseIrregGrid :: (Key k, FromChar a) => Value -> Parser (Grid k a)+parseIrregGrid v = fromCoordGrid . rectToIrregGrid <$> parseJSON v+ -- parses a string like --  o-o-o --  |   | --  o-o o --    | | --    o-o-parsePlainEdges :: Value -> Parser [Edge]-parsePlainEdges v = readEdges <$> parseGrid v+parsePlainEdges :: Key k => Value -> Parser [Edge k]+parsePlainEdges v = filterPlainEdges <$> parseAnnotatedEdges v -readEdges :: SGrid Char -> [Edge]-readEdges g = horiz ++ vert-    where (w, h) = size g-          w' = w `div` 2-          h' = h `div` 2-          isHoriz (x, y) = g ! (2 * x + 1, 2 * y) == '-'-          isVert  (x, y) = g ! (2 * x, 2 * y + 1) == '|'-          horiz = [ E (x, y) H | x <- [0 .. w' - 1]-                               , y <- [0 .. h']-                               , isHoriz (x, y)-                               ]-          vert =  [ E (x, y) V | x <- [0 .. w']-                               , y <- [0 .. h' - 1]-                               , isVert (x, y)-                               ]+filterPlainEdges :: Map.Map (Edge k) Char -> [Edge k]+filterPlainEdges = Map.keys . Map.filterWithKey p+  where+    p (E _ Horiz) '-' = True+    p (E _ Vert)  '|' = True+    p _           _   = False -parseGridChars :: FromChar a => SGrid Char -> Parser (SGrid a)+parseAnnotatedEdges :: (Key k, FromChar a) => Value -> Parser (Map.Map (Edge k) a)+parseAnnotatedEdges v = do+    g <- readEdges <$> parseCoordGrid v+    Map.mapKeys fromCoordEdge <$> traverse parseChar g++readEdges :: Grid Coord Char -> Map.Map (Edge Coord) Char+readEdges = Map.mapKeysMonotonic fromJust . Map.filterWithKey (const . isJust) . Map.mapKeys toEdge+  where+    toEdge c@(x, y) = case (x `mod` 2, y `mod` 2) of+                        (1, 0) -> Just $ E (div2 c) Horiz+                        (0, 1) -> Just $ E (div2 c) Vert+                        _      -> Nothing+    div2 (x', y') = (x' `div` 2, y' `div` 2)++parseGridChars :: FromChar a => Grid k Char -> Parser (Grid k a) parseGridChars = traverse parseChar  -- | Parse a grid with edges and values at nodes and in cells.@@ -327,33 +386,31 @@ -- E.g. o-*-*-o --      |1|2 3 --      *-o--- to a grid of masyu pearls, a grid of integers, and some edges.-parseEdgeGrid :: (FromChar a, FromChar b) =>-                 Value -> Parser (SGrid a, SGrid b, [Edge])+-- to a grid of masyu pearls, a grid of integers, and some annotated edges.+parseEdgeGrid :: (FromChar a, FromChar b, FromChar c) =>+                 Value -> Parser (Grid N a, Grid C b, Map.Map (Edge N) c) parseEdgeGrid v = uncurry (,,) <$>                   parseBoth <*>-                  parsePlainEdges v+                  parseAnnotatedEdges v   where     parseBoth = do-        g <- parseGrid v-        (gn, gc) <- halveGrid g+        g <- parseCoordGrid v+        let (gn, gc) = halveGrid g         gn' <- parseGridChars gn         gc' <- parseGridChars gc         return (gn', gc')     both f (x, y) = (f x, f y)-    halveGrid (Grid (Square w h) m)-        | odd w && odd h = pure (Grid snode (divkeys mnode),-                                 Grid scell (divkeys mcell))-        | otherwise      = fail "non-odd grid size"+    halveGrid m =+        (fromCoordGrid . divkeys $ mnode, fromCoordGrid . divkeys $ mcell)       where-        w' = (w + 1) `div` 2-        h' = (h + 1) `div` 2-        snode = Square w' h'-        scell = Square (w' - 1) (h' - 1)         mnode = Map.filterWithKey (const . uncurry (&&) . both even) m         mcell = Map.filterWithKey (const . uncurry (&&) . both odd)  m         divkeys = Map.mapKeys (both (`div` 2)) +parsePlainEdgeGrid :: (FromChar a, FromChar b) =>+                 Value -> Parser (Grid N a, Grid C b, [Edge N])+parsePlainEdgeGrid v = (\(a,b,c) -> (a, b, filterPlainEdges c)) <$> parseEdgeGrid v+ -- | Parse a grid of edges with values at the nodes. -- -- E.g. o-*-*-o@@ -361,26 +418,28 @@ --      *-o -- to a grid of masyu pearls and some edges. parseNodeEdges :: FromChar a =>-                  Value -> Parser (SGrid a, [Edge])-parseNodeEdges v = proj13 <$> parseEdgeGrid v+                  Value -> Parser (Grid N a, [Edge N])+parseNodeEdges v = proj13 <$> parsePlainEdgeGrid v   where-    proj13 :: (SGrid a, SGrid Empty, [Edge]) -> (SGrid a, [Edge])+    proj13 :: (Grid N a, Grid C Char, [Edge N])+              -> (Grid N a, [Edge N])     proj13 (x,_,z) = (x,z)  parseCellEdges :: FromChar a =>-                  Value -> Parser (SGrid a, [Edge])-parseCellEdges v = proj23 <$> parseEdgeGrid v+                  Value -> Parser (Grid C a, [Edge N])+parseCellEdges v = proj23 <$> parsePlainEdgeGrid v   where-    proj23 :: (SGrid PlainNode, SGrid a, [Edge]) -> (SGrid a, [Edge])+    proj23 :: (Grid N Char, Grid C a, [Edge N])+              -> (Grid C a, [Edge N])     proj23 (_,y,z) = (y,z)  data HalfDirs = HalfDirs {unHalfDirs :: [Dir]}  instance FromChar HalfDirs where-    parseChar c | c `elem` "└┴├┼" = pure . HalfDirs $ [V, H]-                | c `elem` "│┘┤"  = pure . HalfDirs $ [V]-                | c `elem` "─┌┬"  = pure . HalfDirs $ [H]-                | otherwise       = pure . HalfDirs $ []+    parseChar c | c `elem` ("└┴├┼" :: String) = pure . HalfDirs $ [Vert, Horiz]+                | c `elem` ("│┘┤"  :: String) = pure . HalfDirs $ [Vert]+                | c `elem` ("─┌┬"  :: String) = pure . HalfDirs $ [Horiz]+                | otherwise                   = pure . HalfDirs $ []  -- parses a string like --  ┌┐┌─┐@@ -388,55 +447,103 @@ --  │└─┘│ --  └──┐│ --     └┘-parseEdges :: Value -> Parser [Edge]+parseEdges :: Key k => Value -> Parser [Edge k] parseEdges v = do-    Grid _ m <- rectToSGrid . fmap unHalfDirs <$> parseJSON v+    m <- fmap unHalfDirs <$> parseGrid v     return [ E p d | (p, ds) <- Map.toList m, d <- ds ] +instance FromChar Dir' where+    parseChar 'u' = pure U+    parseChar 'd' = pure D+    parseChar 'r' = pure R+    parseChar 'l' = pure L+    parseChar _   = fail "expected 'udrl'"++newtype Dirs' = Dirs' { unDirs' :: [Dir'] }++instance FromChar Dirs' where+    parseChar '└' = pure . Dirs' $ [U, R]+    parseChar '│' = pure . Dirs' $ [U, D]+    parseChar '┘' = pure . Dirs' $ [L, U]+    parseChar '─' = pure . Dirs' $ [L, R]+    parseChar '┌' = pure . Dirs' $ [D, R]+    parseChar '┐' = pure . Dirs' $ [L, D]+    parseChar '╶' = pure . Dirs' $ [R]+    parseChar '╴' = pure . Dirs' $ [L]+    parseChar '╷' = pure . Dirs' $ [D]+    parseChar '╵' = pure . Dirs' $ [U]+    parseChar _   = pure . Dirs' $ []++parseEdgesFull :: Key k => Value -> Parser [Edge k]+parseEdgesFull v = do+    m <- parseGrid v+    return . Set.toList . Set.fromList . map unorient+        $ [ E' p d | (p, Dirs' ds) <- Map.toList m, d <- ds ]+ type ThermoRect = Rect (Either Blank (Either Int Alpha))  partitionEithers :: Ord k => Map.Map k (Either a b) -> (Map.Map k a, Map.Map k b) partitionEithers = Map.foldrWithKey insertEither (Map.empty, Map.empty)+   where     insertEither k = either (first . Map.insert k) (second . Map.insert k) -parseThermos :: SGrid Alpha -> Parser [Thermometer]-parseThermos (Grid s m) = catMaybes <$> mapM parseThermo (Map.keys m)+parseThermos :: Grid C Alpha -> Parser [Thermometer]+parseThermos m = catMaybes <$> mapM parseThermo (Map.keys m')   where     m' = fmap unAlpha m-    parseThermo :: Cell Square -> Parser (Maybe Thermometer)+    parseThermo :: C -> Parser (Maybe Thermometer)     parseThermo p | not (isStart p)           = pure Nothing                   | not (isAlmostIsolated p)  = fail $ show p ++ " not almost isolated"                   | otherwise                 = Just <$> parseThermo' p-    parseThermo' :: Cell Square -> Parser Thermometer+    parseThermo' :: C -> Parser Thermometer     parseThermo' p = do         q <- next p         maybe (fail "no succ for thermo bulb") (fmap (p:) . parseThermo'') q-    parseThermo'' :: Cell Square -> Parser Thermometer+    parseThermo'' :: C -> Parser Thermometer     parseThermo'' p = do         q <- next p         maybe (pure [p]) (fmap (p:) . parseThermo'') q-    next :: Cell Square -> Parser (Maybe (Cell Square))+    next :: C -> Parser (Maybe C)     next p = case succs p of         []   -> pure Nothing         [q]  -> pure (Just q)         _    -> fail "multiple successors"-    succs      p = filter    (test ((==) . succ) p) . vertexNeighbours s $ p-    isStart    p = not . any (test ((==) . pred) p) . vertexNeighbours s $ p+    succs      p = filter    (test ((==) . succ) p) . vertexNeighbours $ p+    isStart    p = not . any (test ((==) . pred) p) . vertexNeighbours $ p     test f p q = maybe False (f (m' Map.! p)) (Map.lookup q m')-    isAlmostIsolated p = all disjointSucc . vertexNeighbours s $ p+    isAlmostIsolated p = all disjointSucc . vertexNeighbours $ p       where         disjointSucc q = null $ intersect (succs p) (succs' q)         succs' q = maybe [] (const $ succs q) (Map.lookup q m') -parseThermoGrid :: ThermoRect -> Parser (SGrid Int, [Thermometer])-parseThermoGrid (Rect w h ls) = (,) (Grid s ints)-                              <$> parseThermos (Grid s alphas)+parseThermoGrid :: ThermoRect -> Parser (Grid C (Maybe Int), [Thermometer])+parseThermoGrid (Rect _ _ ls) = (,) ints+                              <$> parseThermos alphas   where-    s = Square w h-    (ints, alphas) = partitionEithers . snd . partitionEithers $-                     listListToMap ls+    m = fromCoordGrid $ listListToMap ls+    ints = either (const Nothing) (either Just (const Nothing)) <$> m+    alphas = fmap fromRight . Map.filter isRight+           . fmap fromRight . Map.filter isRight $ m+    fromRight (Left _) = impossible+    fromRight (Right x) = x +parseOutsideGrid :: Key k =>+                    (Char -> Parser a)+                 -> (Char -> Parser b)+                 -> Value -> Parser (OutsideClues k b, Grid k a)+parseOutsideGrid parseIn parseOut v = do+    BorderedRect w h ls b <- parseBorderedRect parseIn parseOut v+    return (outside b, fromCoordGrid . rectToCoordGrid $ Rect w h ls)+  where outside (Border l r b t) = OC l r b t++parseOutsideGridMap :: (Key k, FromChar a, FromChar b)+                    => (a -> c) -> (b -> d)+                    -> Value -> Parser (OutsideClues k d, Grid k c)+parseOutsideGridMap mapIn mapOut v = do+    (o, g) <- parseOutsideGrid parseChar parseChar v+    return (mapOut <$> o, mapIn <$> g)+ newtype Tight = Tight { unTight :: Tightfit () }  instance FromChar Tight where@@ -445,14 +552,12 @@     parseChar '\\' = pure . Tight $ DR () ()     parseChar _    = empty -parseTightOutside :: Value -> Parser (OutsideClues (Maybe Int),-                                      SGrid (Tightfit ()))-parseTightOutside v = do-    BorderedRect w h ls b <- parseJSON v-        :: Parser (BorderedRect Tight (Either Blank' Int))-    return (outside . fmap (either (const Nothing) Just) $ b,-            fmap unTight . rectToSGrid $ Rect w h ls)-  where outside (Border l r b t) = OC l r b t+parseTightOutside :: Value -> Parser (OutsideClues C (Maybe Int),+                                      Grid C (Tightfit ()))+parseTightOutside = parseOutsideGridMap unTight unBlank'+  where+    unBlank' :: Either Blank' Int -> Maybe Int+    unBlank' = either (const Nothing) Just  instance FromChar a => FromString (Tightfit a) where     parseString [c]           = Single <$> parseChar c@@ -460,9 +565,6 @@     parseString [c,'\\',d]    = DR <$> parseChar c <*> parseChar d     parseString _             = empty -parseTightIntGrid :: Value -> Parser (SGrid (Tightfit Int))-parseTightIntGrid v = rectToSGrid . unSpaced <$> parseJSON v- newtype PMarkedWord = PMW {unPMW :: MarkedWord}  parseNWords :: Int -> String -> Parser [String]@@ -471,15 +573,28 @@   where     ws = words s +parseDoublePair :: FromString a => Value -> Parser ((a, a), (a, a))+parseDoublePair v = (,) <$>+                     ((,) <$> ((!!0) <$> x) <*> ((!!1) <$> x)) <*>+                     ((,) <$> ((!!2) <$> x) <*> ((!!3) <$> x))+    where x = parseJSON v >>= parseNWords 4 >>= mapM parseString+ instance FromJSON PMarkedWord where-    parseJSON v = PMW <$> (MW <$>-                  ((,) <$> ((!!0) <$> x) <*> ((!!1) <$> x)) <*>-                  ((,) <$> ((!!2) <$> x) <*> ((!!3) <$> x)))-        where x = parseJSON v >>= parseNWords 4 >>= mapM parseString+    parseJSON v = PMW . uncurry MW <$> parseDoublePair v  instance FromString Int where     parseString s = maybe empty pure $ readMaybe s +parseMarkedLine :: FromCoord a => Value -> Parser (MarkedLine a)+parseMarkedLine v = do+    (s, e) <- parseDoublePair v+    return $ MarkedLine (fromCoord s) (fromCoord e)++newtype PMarkedLine a = PML {unPML :: MarkedLine a}++instance FromCoord a => FromJSON (PMarkedLine a) where+    parseJSON v = PML <$> parseMarkedLine v+ newtype PCompassC = PCC {unPCC :: CompassC}  instance FromJSON PCompassC where@@ -490,49 +605,62 @@               comp _            = empty     parseJSON _          = empty -newtype RefGrid a = RefGrid { unRG :: SGrid a }+newtype PSlovakClue = PSlovakClue {unPSlovakClue :: SlovakClue} -data Ref = Ref { unRef :: Char }-    deriving Show+instance FromJSON PSlovakClue where+    parseJSON (String t) = svk . map T.unpack . T.words $ t+      where+        svk [s, c] = PSlovakClue <$> (SlovakClue <$> parseString s <*> parseString c)+        svk _      = fail "expect two integers"+    parseJSON _ = fail "expect string of two integers" -instance FromChar Ref where-    parseChar c | isAlpha c = pure (Ref c)-    parseChar _             = empty+newtype RefGrid k a = RefGrid { unRG :: Grid k (Maybe a) } -hashmaptomap :: (Eq a, Hashable a, Ord a) => HMap.HashMap a b -> Map.Map a b+hashmaptomap :: Ord a => HMap.HashMap a b -> Map.Map a b hashmaptomap = Map.fromList . HMap.toList -compose :: (Ord a, Ord b) => Map.Map a b -> Map.Map b c -> Maybe (Map.Map a c)+compose :: Ord b => Map.Map a b -> Map.Map b c -> Maybe (Map.Map a c) compose m1 m2 = mapM (`Map.lookup` m2) m1 -instance FromJSON a => FromJSON (RefGrid a) where-    parseJSON (Object v) = RefGrid <$> do-        Grid s refs <- fmap (fmap ((:[]) . unRef)) . rectToClueGrid <$>-                       (v .: "grid" :: Parser (Rect (Either Blank Ref)))-        m <- hashmaptomap <$> v .: "clues"-        case compose (Map.mapMaybe id refs) m of+newtype MaybeMap k a = MM { unMaybeMap :: Map.Map k (Maybe a) }++instance Functor (MaybeMap k) where+    fmap f (MM m) = MM (fmap (fmap f) m)++instance Foldable (MaybeMap k) where+    foldMap f (MM m) = foldMap (foldMap f) m++instance Traversable (MaybeMap k) where+    traverse f m = MM <$> traverse (traverse f) (unMaybeMap m)++compose' :: Ord b => Map.Map a (Maybe b)+                  -> Map.Map b c+                  -> Maybe (Map.Map a (Maybe c))+compose' m1 m2 = unMaybeMap <$> mapM (`Map.lookup` m2) (MM m1)++instance (Key k, FromJSON a) => FromJSON (RefGrid k a) where+    parseJSON v = RefGrid <$> do+        refs <- fmap (fmap ((:[]) . unAlpha) . blankToMaybe)+                <$> parseFrom ["grid"] parseGrid v+        m <- hashmaptomap <$> parseFrom ["clues"] parseJSON v+        case compose' refs m of             Nothing -> mzero-            Just m' -> return $ Grid s m'-    parseJSON _ = empty+            Just m' -> return m' -parseAfternoonGrid :: Value -> Parser (SGrid Shade)+parseAfternoonGrid :: Value -> Parser (Grid C Shade) parseAfternoonGrid v = do-    (Grid s _ , es) <- parseNodeEdges v :: Parser (SGrid Char, [Edge])-    let (m, b) = splitBorder s $ toMap es-    guard $ Map.null b-    return $ Grid (shrink s) m+    (_, _, es) <- parsePlainEdgeGrid v+                  :: Parser (Grid N Char, Grid C Char, [Edge N])+    return . toMap $ es   where-    shrink (Square w h) = Square (w-1) (h-1)-    toShade V = Shade False True-    toShade H = Shade True  False+    toShade Vert  = Shade False True+    toShade Horiz = Shade True  False     merge (Shade a b) (Shade c d)         | a && c || b && d  = error "shading collision"         | otherwise         = Shade (a || c) (b || d)     toMap es = Map.fromListWith         merge-        [(p, toShade d) | E p d <- es]-    splitBorder (Square w h) = Map.partitionWithKey-        (\(x, y) _ -> x < w - 1 && y < h - 1)+        [(fromCoord . toCoord $ p, toShade d) | E p d <- es]  newtype ParseTapaClue = ParseTapaClue { unParseTapaClue :: TapaClue } @@ -541,10 +669,10 @@                      guard $ length xs > 0 && length xs <= 4                      return . ParseTapaClue . TapaClue $ xs -reorientOutside :: OutsideClues a -> OutsideClues a+reorientOutside :: OutsideClues k a -> OutsideClues k a reorientOutside (OC l r b t) = OC (reverse l) (reverse r) b t -parseCharOutside :: FromChar a => Value -> Parser (OutsideClues a)+parseCharOutside :: FromChar a => Value -> Parser (OutsideClues k a) parseCharOutside (Object v) = reorientOutside <$>                               (OC <$>                                pfield "left" <*> pfield "right" <*>@@ -553,7 +681,7 @@     pfield f = parseLine . fromMaybe [] =<< v .:? f parseCharOutside _          = empty -parseOutside :: FromJSON a => Value -> Parser (OutsideClues a)+parseOutside :: FromJSON a => Value -> Parser (OutsideClues k a) parseOutside (Object v) = reorientOutside <$>                               (OC <$>                                pfield "left" <*> pfield "right" <*>@@ -562,7 +690,7 @@     pfield f = pure . fromMaybe [] =<< v .:? f parseOutside _          = empty -parseMultiOutsideClues :: FromJSON a => Value -> Parser (OutsideClues [a])+parseMultiOutsideClues :: FromJSON a => Value -> Parser (OutsideClues k [a]) parseMultiOutsideClues (Object v) = rev <$> raw   where     raw = OC <$> v `ml` "left" <*> v `ml` "right" <*> v `ml` "bottom" <*> v `ml` "top"@@ -578,6 +706,55 @@     parseChar 'X'  = pure $ PrimeDiag (True,  True)     parseChar _    = empty +parseCoordLoop :: Value -> Parser VertexLoop+parseCoordLoop v = sortCoords <$> parseClueGrid v+  where+    sortCoords :: Grid N (Maybe Char) -> VertexLoop+    sortCoords = map fst . sortBy (comparing snd) . Map.toList . clues++instance FromString DigitRange where+    parseString s = do+        let (a, b) = break (== '-') s+        b' <- case b of ('-':cs) -> pure cs+                        _        -> fail "exected '-' in range"+        DigitRange <$> parseString a <*> parseString b'++newtype PFraction = PFraction { unPFraction :: Fraction }++instance FromJSON PFraction where+    parseJSON v = PFraction <$> toStringParser fraction v+ instance FromChar Crossing where     parseChar '+' = pure Crossing     parseChar _   = fail "expected '+'"++instance FromChar KropkiDot where+    parseChar '*' = pure KBlack+    parseChar 'o' = pure KWhite+    parseChar ' ' = pure KNone+    parseChar '.' = pure KNone+    parseChar _   = fail "expected '*o '"++instance FromChar Relation where+    parseChar '<' = pure RLess+    parseChar '>' = pure RGreater+    parseChar '=' = pure REqual+    parseChar ' ' = pure RUndetermined+    parseChar _   = fail "expected '<>= '"++parseGreaterClues :: Value -> Parser [GreaterClue]+parseGreaterClues v = do+    Rect _ _ ls <- parseJSON v+    mapM parseGreaterClue ls++parseGreaterClue :: [Char] -> Parser GreaterClue+parseGreaterClue [] = pure []+parseGreaterClue xs = p RUndetermined xs+  where+    p rel ('.':cs) = (rel:) <$> q cs+    p _   (' ':_)  = pure [] -- Rect fills the lines up with spaces...+    p _   _        = fail "expected '.'"+    q [] = pure []+    q (r:cs) = do+        rel <- parseChar r+        p rel cs
+ src/tools/checkpuzzle.hs view
@@ -0,0 +1,94 @@+{-# LANGUAGE FlexibleContexts #-}++module Main where++import Text.Puzzles.Puzzle+import Data.Puzzles.PuzzleTypes (typeNames, PuzzleType(..))+import Data.Puzzles.CmdLine (exitErr, readPuzzle, checkType)++import Data.Puzzles.Elements (digitList)+import qualified Text.Puzzles.PuzzleTypes as T++import Options.Applicative+import Control.Monad+import Data.Maybe+import Data.List (intercalate, sort)++import System.Exit+import System.Environment (getProgName)++import qualified Data.Yaml as Y++optListTypes :: Parser (a -> a)+optListTypes =+    infoOption+        (unlines' . sort . map snd $ typeNames)+        (long "list-types"+         <> help "List supported puzzle types")+  where+    unlines' = intercalate "\n"++data PuzzleOpts = PuzzleOpts+    { _type     :: Maybe String+    , _input    :: FilePath+    }++puzzleOpts :: Parser PuzzleOpts+puzzleOpts = PuzzleOpts+    <$> (optional . strOption $+            (long "type" <> short 't'+             <> metavar "TYPE"+             <> help "Puzzle type, overriding type in input file"))+    <*> argument str+            (metavar "INPUT"+             <> help "Puzzle file in .pzl format")++defaultOpts :: Parser a -> IO a+defaultOpts optsParser = do+    prog <- getProgName+    let p = info (helper <*> optListTypes <*> optsParser)+                (fullDesc+                 <> progDesc "Command-line puzzle checking."+                 <> header prog)+    execParser p++main :: IO ()+main = do+    opts <- defaultOpts puzzleOpts+    mp <- readPuzzle (_input opts)+    TP mt pv msv _ <- case mp of Left  e -> exitErr $+                                             "parse failure: " ++ show e+                                 Right p -> return p+    t <- checkType $ _type opts `mplus` mt+    sv <- maybe (exitErr $ "need solution") return msv +    let es = Y.parseEither (check t) (pv, sv)+    case es of Left err  -> exitErr $ "parse failure: " ++ err+               Right []  -> exitSuccess+               Right es' -> mapM_ putStrLn es' >> exitFailure++check :: PuzzleType -> (Y.Value, Y.Value) -> Y.Parser [String]+check t (pv, sv) =+    case t of+        ABCtje -> checkABCtje (pv, sv)+        _      -> return []++checkABCtje :: (Y.Value, Y.Value) -> Y.Parser [String]+checkABCtje (pv, sv) = do+    p <- fst T.abctje $ pv+    s <- snd T.abctje $ sv+    return . catMaybes . map (\c -> c p s) $+        [ solutionKeys, values ]+  where+    solutionKeys (ds, _) s = let have = sort (digitList ds)+                                 want = sort (map fst (s))+                             in if have /= want then Just "unequal digit lists" else Nothing+    values (_, ws) vs = (\es -> case es of [] -> Nothing+                                           _  -> Just $ intercalate ", " es)+                      . catMaybes+                      . map (\(w, v) -> if val w == v+                                            then Nothing+                                            else Just (w ++ " should be " ++ show (val w)))+                      $ ws+      where+        l c = fromMaybe 0 . lookup c . map (\(x, y) -> (y, x)) $ vs+        val = sum . map l
src/tools/drawpuzzle.hs view
@@ -4,12 +4,15 @@  import Diagrams.Prelude hiding (value, option, (<>), Result) +import Data.Puzzles.CmdLine import Diagrams.Puzzles.CmdLine  import Text.Puzzles.Puzzle+import Text.Puzzles.Code import Data.Puzzles.Compose import Data.Puzzles.PuzzleTypes (typeNames) import Diagrams.Puzzles.Draw+import Diagrams.Puzzles.Code  import Options.Applicative import Control.Monad@@ -36,6 +39,7 @@     , _puzzle   :: Bool     , _solution :: Bool     , _example  :: Bool+    , _code     :: Bool     , _input    :: FilePath     } @@ -59,6 +63,9 @@     <*> switch             (long "example" <> short 'e'              <> help "Render example (to base.ext)")+    <*> switch+            (long "code" <> short 'c'+             <> help "Add solution code markers")     <*> argument str             (metavar "INPUT"              <> help "Puzzle file in .pzl format")@@ -83,7 +90,7 @@     base = takeBaseName (_input opts)     out = addExtension (base ++ outputSuffix oc) f -renderPuzzle :: PuzzleOpts -> (OutputChoice -> Maybe (Diagram B R2)) ->+renderPuzzle :: PuzzleOpts -> (OutputChoice -> Maybe (Diagram B)) ->                 (OutputChoice, Bool) -> IO () renderPuzzle opts r (oc, req) = do     let x = r oc@@ -129,17 +136,29 @@     hasSol DrawExample  = True     hasSol DrawPuzzle   = False +maybeSkipCode :: PuzzleOpts -> Maybe Y.Value -> Maybe Y.Value+maybeSkipCode opts = if _code opts then id else const Nothing++parseAndDrawCode :: Y.Value -> IO (CodeDiagrams (Diagram B))+parseAndDrawCode v = case parsed of+    Left  e -> exitErr $ "solution code parse failure: " ++ e+    Right c -> return $ drawCode c+  where+    parsed = Y.parseEither parseCode v+ main :: IO () main = do     opts <- defaultOpts puzzleOpts     ocs <- checkOutput opts     checkFormat (_format opts)     mp <- readPuzzle (_input opts)-    TP mt pv msv <- case mp of Left  e -> exitErr $-                                          "parse failure: " ++ show e-                               Right p -> return p+    TP mt pv msv mc <- case mp of Left  e -> exitErr $+                                             "parse failure: " ++ show e+                                  Right p -> return p     let msv' = maybeSkipSolution ocs msv+        mc'  = maybeSkipCode opts mc     t <- checkType $ _type opts `mplus` mt     let ps = Y.parseEither (handle drawPuzzleMaybeSol t) (pv, msv')-    case ps of Right ps' -> mapM_ (renderPuzzle opts (draw ps')) ocs+    mcode <- sequenceA $ parseAndDrawCode <$> mc'+    case ps of Right ps' -> mapM_ (renderPuzzle opts (draw mcode ps')) ocs                Left    e -> exitErr $ "parse failure: " ++ e
+ tests/Data.hs view
@@ -0,0 +1,244 @@+module Data where++import qualified Data.Text as T+import Data.Yaml+import Data.Maybe (fromJust)+import qualified Data.ByteString as B+import Data.Text.Encoding (encodeUtf8)++packStr :: String -> B.ByteString+packStr = encodeUtf8 . T.pack++decodeLines :: [String] -> Value+decodeLines = fromJust . decode . packStr . unlines++packLine :: String -> Value+packLine = String . T.pack++packLines :: [String] -> Value+packLines = String . T.pack . unlines++geradeweg_1 :: Value+geradeweg_1 = packLines $+    [ ".....    "+    , ".211."+    , "..2..  "+    , "3...4"+    , "....."+    ]++geradeweg_1_sol :: Value+geradeweg_1_sol = packLines $+    [ "┌┐┌─┐"+    , "││└┐│  "+    , "│└─┘│"+    , "└──┐│"+    , "  .└┘ "+    ]++tightfit_1 :: Value+tightfit_1 = packLines $+    [ " --- "+    , "3/\\.-"+    , "-\\.\\4"+    , "-.\\/-"+    , " 35-"+    ]++tightfit_broken_1 :: Value+tightfit_broken_1 = packLines $+    [ " --- "+    , "3/\\.-"+    , "-\\x\\4"+    , "-.\\/-"+    , " 35-"+    ]++tightfit_broken_2 :: Value+tightfit_broken_2 = packLines $+    [ "3/\\.-"+    , "-\\x\\4"+    , "-.\\/-"+    , " 35-"+    ]++tightfit_1_sol :: Value+tightfit_1_sol = packLines $+    [ "2/1 4\\5  3"+    , "  4\\5  3  2\\1 "+    , "   3  1\\2 5/4"+    ]++tightfit_sol_broken :: Value+tightfit_sol_broken = packLine "2/1 4 /5"++tightfit_sol_broken_2 :: Value+tightfit_sol_broken_2 = packLine "2/x 4 3/5"++slalom_sol_broken :: Value+slalom_sol_broken = packLine "//\\ /\\x5 "++kpyramid_1 :: Value+kpyramid_1 = packLines $+    [ "G     3"+    , "G    . ."+    , "G   . . ."+    , "W  .o. . ."+    , "G 1*.*.o.*6"+    ]++kpyramid_1_sol :: Value+kpyramid_1_sol = packLines $+    [ "    3"+    , "   8 5"+    , "  1 9 4"+    , " 3 2 7 3"+    , "1 2 4 3 6"+    ]++kpyramid_broken_1 :: Value+kpyramid_broken_1 = packLines $+    [ "  G     3"+    , "  G    . 22"+    , "  H   . aa ."+    , "  W  .o. .|. "+    , "  G 1*.*.o.*6"+    ]++kpyramid_broken_2 :: Value+kpyramid_broken_2 = packLines $+    [ "G     3"+    , "G    . 22"+    , "H   . aa ."+    , "W  .o. .|. "+    , "G 1*.*.o.*6"+    ]++kpyramid_broken_3 :: Value+kpyramid_broken_3 = packLines $+    [ "G     3"+    , "G    . ."+    , "G   . . ."+    , "W  .o. . ."+    , "G a*.*.o.*6"+    ]++compass_1 :: Value+compass_1 = decodeLines $+    [ "grid: |"+    , "  ..."+    , "  a.b"+    , "clues:"+    , "  a: 2 1 . 2"+    , "  b: 21 . . 0"+    ]++compass_broken_1 :: Value+compass_broken_1 = decodeLines $+    [ "grid: |"+    , "  a.b"+    , "clues:"+    , "  b: 21 . . 0"+    ]++compass_broken_2 :: Value+compass_broken_2 = decodeLines $+    [ "grid: |"+    , "  a.b"+    , "clues:"+    , "  a: x . . 0"+    , "  b: 21 . . 0"+    ]++compass_broken_3 :: Value+compass_broken_3 = decodeLines $+    [ "grid: |"+    , "  a.b"+    , "clues:"+    , "  a: 1 . ."+    , "  b: 21 . . 0"+    ]++compass_broken_4 :: Value+compass_broken_4 = decodeLines $+    [ "grid: |"+    , "  a.b"+    , "clues:"+    , "  a: 1 . . 2 3"+    , "  b: 21 . . 0"+    ]++compass_broken_5 :: Value+compass_broken_5 = decodeLines $+    [ "grid: |"+    , "  a3b"+    , "clues:"+    , "  a: 1 . . 2 3"+    , "  b: 21 . . 0"+    ]++thermo_1 :: Value+thermo_1 = packLines $+    [ ".b..2."+    , "a.c5.1"+    , ".d..6."+    , ".4..c."+    , "5.3b.d"+    , ".2..a."+    ]++thermo_2 :: Value+thermo_2 = packLines $+    [ "....2."+    , "..c5.1"+    , ".b..6."+    , "a..c.."+    , "a...b."+    , ".bc.a."+    ]++thermo_broken_1 :: Value+thermo_broken_1 = packLines $+    [ ".."+    , "a."+    ]++thermo_broken_2 :: Value+thermo_broken_2 = packLines $+    [ "bb"+    , "a."+    ]++multioutside :: Value+multioutside = decodeLines $+    [ "left:"+    , "  - [2, 1]"+    , "  - [3]"+    , "right:"+    , "  - []"+    , "  - [1, 0]"+    , "bottom:"+    , "  - [0, 0, 1]"+    , "top:"+    , "  - [-1, 1]"+    ]++boxof2or3_1 :: Value+boxof2or3_1 = packLines $+    [ "*-*-*-*-*"+    , "| | | | |"+    , "*-*-*-*-*"+    , "| | |"+    , "*-o-o-o-*"+    , "    | | |"+    , "*-*-*-*-*"+    , "| | | | |"+    , "o-*-*-*-*"+    ]++edgeGrid_1 :: Value+edgeGrid_1 = packLines $+   [ "o-*-*-o"+   , "|1|2 3"+   , "*-o"+   ]
+ tests/Data/Puzzles/GridShapeSpec.hs view
@@ -0,0 +1,11 @@+module Data.Puzzles.GridShapeSpec where++import Test.Hspec (Spec, describe, it, shouldBe)++import Data.Puzzles.GridShape++spec :: Spec+spec = do+    describe "edgeSize" $ do+        it "computes the size in cells" $ do+            edgeSize [E (N 1 1) Vert, E (N 2 1) Horiz] `shouldBe` (3, 2)
+ tests/Data/Puzzles/GridSpec.hs view
@@ -0,0 +1,22 @@+module Data.Puzzles.GridSpec where++import qualified Data.Set as Set+import Test.Hspec (Spec, describe, it, shouldBe)++import Data.Puzzles.Grid+import Data.Puzzles.GridShape (N(..))++spec :: Spec+spec = do+    describe "sizeGrid" $ do+        it "creates a rectangular grid of nodes" $ do+            nodes (sizeGrid (2, 3)) `shouldBe` Set.fromList [+                                          N 0 0, N 0 1, N 0 2,+                                          N 1 0, N 1 1, N 1 2]+            nodes (sizeGrid (0, 0)) `shouldBe` Set.empty+            nodes (sizeGrid (2, 0)) `shouldBe` Set.empty+            nodes (sizeGrid (2, 1)) `shouldBe ` Set.fromList [N 0 0, N 1 0]++    describe "nodeGrid" $ do+        it "creates the grid of nodes from a rectangular grid of cells" $ do+            nodes (nodeGrid (sizeGrid (2, 1))) `shouldBe` nodes (sizeGrid (3, 2))
+ tests/Diagrams/Puzzles/GridSpec.hs view
@@ -0,0 +1,32 @@+module Diagrams.Puzzles.GridSpec where++import Diagrams.Prelude (p2)+import Diagrams.Path (pathPoints)+import Test.Hspec (Spec, describe, it, shouldBe, shouldSatisfy)++import Data.Puzzles.Grid+import Data.Puzzles.GridShape+import Diagrams.Puzzles.Grid+import Diagrams.Puzzles.Lib (p2i)++spec :: Spec+spec = do+    describe "irregularGridPaths" $ do+        it "gives the border of a rectangular grid" $ do+            let g = sizeGrid (2, 3) :: Grid C ()+                (outer, _) = irregularGridPaths g+                pts = pathPoints outer+            length pts `shouldBe` 1+            let [opts] = pts+            opts `shouldSatisfy` elem (p2i (0, 0))+            opts `shouldSatisfy` elem (p2i (2, 0))+            opts `shouldSatisfy` elem (p2i (2, 3))+            opts `shouldSatisfy` elem (p2i (0, 3))++    describe "midPoint" $ do+        it "gives the center of a node edge" $ do+            let e = E (N 0 1) Horiz+            midPoint e `shouldBe` p2 (0.5, 1.0)+        it "gives the center of a cell edge" $ do+            let e = E (C 0 0) Vert+            midPoint e `shouldBe` p2 (0.5, 1.0)
+ tests/Text/Puzzles/PuzzleTypesSpec.hs view
@@ -0,0 +1,54 @@+module Text.Puzzles.PuzzleTypesSpec where++import Data.Maybe (isJust)+import qualified Data.Map as Map+import Data.Yaml+import Data.Text.Encoding (encodeUtf8)+import qualified Data.ByteString as B+import qualified Data.Text as T++import Test.Hspec (Spec, describe, it, shouldBe, shouldSatisfy)++import Data.Puzzles.GridShape (edgeSize)+import Data.Puzzles.Elements (DigitRange(..))+import Text.Puzzles.PuzzleTypes++packLines :: [String] -> B.ByteString+packLines = encodeUtf8 . T.pack . unlines++parse :: (Value -> Parser a) -> B.ByteString -> Maybe a+parse p t = decode t >>= parseMaybe p++spec :: Spec+spec = do+    describe "abctje" $ do+        it "parses a list of clues" $ do+            let (p, _) = abctje+                y = packLines [ "numbers: 1-10"+                              , "clues:"+                              , "- HELLO: 15"+                              , "- WORLD: 20"+                              , "- weird stuff, too!: 100"+                              ]+            parse p y `shouldBe` Just (DigitRange 1 10, [("HELLO", 15), ("WORLD", 20), ("weird stuff, too!", 100)])+        it "parses a solution" $ do+            let (_, p) = abctje+                y = packLines [ "- 1: A"+                              , "- 100: C"+                              ]+            parse p y `shouldBe` Just [(1, 'A'), (100, 'C')]++    describe "kropki" $ do+        it "parses edges of the right size" $ do+            let (p, _) = kropki+                y = packLines [ "|"+                              , "  + + + +"+                              , "   . . . "+                              , "  +*+ + +"+                              , "   . .o. "+                              , "  + + + +"+                              ]+                res = parse p y+            res `shouldSatisfy` isJust+            let Just m = res+            edgeSize (Map.keys m) `shouldBe` (3, 2)
+ tests/Text/Puzzles/UtilSpec.hs view
@@ -0,0 +1,68 @@+{-# LANGUAGE TypeFamilies #-}+module Text.Puzzles.UtilSpec where++import qualified Data.Map as Map+import Data.Yaml+import qualified Data.Text as T++import Test.Hspec (Spec, describe, it, shouldBe)++import Data.Puzzles.GridShape (Edge(..), N(..), Dir(..))+import Data.Puzzles.Elements (Relation(..))+import Text.Puzzles.Util (parseCoordGrid, parseAnnotatedEdges, parseGreaterClue)++packLines :: [String] -> Value+packLines = String . T.pack . unlines++spec :: Spec+spec = do+    describe "parseCoordGrid" $ do+        it "parses a rectangle of letters" $ do+            let y = packLines [ "abc"+                              , "def"+                              ]+                want = Map.fromList [ ((0, 0), 'd')+                                    , ((1, 0), 'e')+                                    , ((2, 0), 'f')+                                    , ((0, 1), 'a')+                                    , ((1, 1), 'b')+                                    , ((2, 1), 'c')+                                    ]+            parseMaybe parseCoordGrid y `shouldBe` Just want++    describe "parseAnnotatedEdges" $ do+        it "parses some kropki clues" $ do+            let y = packLines [ "+ + + +"+                              , " . .*.o"+                              , "+ +o+ +"+                              , " . . . "+                              , "+ + + +"+                              ]+                want = Map.fromList+                           [ (E (N 1 1) Horiz, 'o')+                           , (E (N 2 1) Vert,  '*')+                           , (E (N 3 1) Vert,  'o')+                           ]+                parseNonempty v = Map.filter ((/=) ' ') <$> parseAnnotatedEdges v+            parseMaybe parseNonempty y `shouldBe` Just want++    describe "parseGreaterClue" $ do+        it "parses an empty line" $ do+            let y = []+                want = []+            parseMaybe parseGreaterClue y `shouldBe` Just want++        it "parses a single dot" $ do+            let y = ['.']+                want = [RUndetermined]+            parseMaybe parseGreaterClue y `shouldBe` Just want++        it "parses a single dot" $ do+            let y = ['.']+                want = [RUndetermined]+            parseMaybe parseGreaterClue y `shouldBe` Just want++        it "parses a mixed line" $ do+            let y = ['.', '<', '.', ' ', '.', '=', '.']+                want = [RUndetermined, RLess, RUndetermined, REqual]+            parseMaybe parseGreaterClue y `shouldBe` Just want
+ tests/Util.hs view
@@ -0,0 +1,29 @@+module Util where++import Control.DeepSeq+import Data.Yaml++import Test.Tasty.HUnit++-- | Force full evaluation of a showable value.+justShow :: Show a => Maybe a -> Bool+justShow Nothing = False+justShow (Just x) = show x `deepseq` True++-- | Force full evaluation of a showable value.+eitherShow :: Show a => Either e a -> Bool+eitherShow (Left _) = False+eitherShow (Right x) = show x `deepseq` True++-- | Test that a value is parsed correctly, by forcing the+--   parse result to be fully evaluated.+testParse :: Show a => (Value -> Parser a) -> Value -> Assertion+testParse p t = eitherShow res @? "bad parse: " ++ err+  where+    res = parseEither p t+    err = either id (const "no error") res++-- | Test that a value is not parsed.+testNonparse :: Show a => (Value -> Parser a) -> Value -> Assertion+testNonparse p t = (not . justShow . parseMaybe p $ t)+                   @? "parsed but shouldn't"
+ tests/compare.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE CPP #-}++module Main where++import Test.Tasty+import Test.Tasty.Golden++import System.Process+import System.FilePath++main :: IO ()+main = tests >>= defaultMain++render :: FilePath -> IO ()+render fp = callProcess "stack" ["exec", "drawpuzzle", "--", "-e", "-c", "-f",  "png", fp ]++testFile :: FilePath -> TestTree+testFile fp = goldenVsFile ("comparing " ++ fp)+                           fppng+                           (takeFileName fppng)+                           (render fp)+  where+    fppng = replaceExtension fp ".png"++tests :: IO TestTree+tests = testGroup "compare to old output" . map testFile+        <$> findByExtension [".pzl"] "tests/examples"
tests/tests.hs view
@@ -1,45 +1,62 @@ import Test.Tasty+import Test.Tasty.Hspec import Test.Tasty.HUnit  import Data.Yaml import Data.List (sort) import qualified Data.Map as Map-import Control.DeepSeq  import Text.Puzzles.Puzzle import Data.Puzzles.Elements (Thermometer, MasyuPearl(..)) import Text.Puzzles.Util-    (parseChar, parseMultiOutsideClues, parseEdgeGrid)+    (parseChar, parseMultiOutsideClues, parsePlainEdgeGrid) import Text.Puzzles.PuzzleTypes import qualified Data.Puzzles.Grid as Grid import Data.Puzzles.Pyramid (PyramidSol(..))-import Data.Puzzles.Grid (multiOutsideClues, OutsideClues(..))-import Data.Puzzles.GridShape (Edge(..), Square(..), Dir(..))+import Data.Puzzles.Grid+import Data.Puzzles.GridShape+import Data.Puzzles.Util -import Diagrams.Puzzles.PuzzleGrids-import Diagrams.Prelude-import Diagrams.Backend.SVG -import Text.Blaze.Svg.Renderer.Text (renderSvg)- import Data import Util+import qualified Data.Puzzles.GridSpec+import qualified Data.Puzzles.GridShapeSpec+import qualified Diagrams.Puzzles.GridSpec+import qualified Text.Puzzles.PuzzleTypesSpec+import qualified Text.Puzzles.UtilSpec  main :: IO ()-main = defaultMain tests+main = do+    ss <- specs+    defaultMain . testGroup "Tests" $ tests ++ ss -tests :: TestTree-tests = testGroup "Tests"-    [ parseUtilTests, parseTests, parseDataTests, renderTests, dataTests ]+specs :: IO [TestTree]+specs = mapM (uncurry testSpec)+            [ ("Data.Puzzles.Grid",        Data.Puzzles.GridSpec.spec)+            , ("Data.Puzzles.GridShape",   Data.Puzzles.GridShapeSpec.spec)+            , ("Diagrams.Puzzles.Grid",    Diagrams.Puzzles.GridSpec.spec)+            , ("Text.Puzzles.PuzzleTypes", Text.Puzzles.PuzzleTypesSpec.spec)+            , ("Text.Puzzles.Util",        Text.Puzzles.UtilSpec.spec)+            ] -testParsePzl, testParseSol, testNonparsePzl, testNonparseSol ::-    (Show a, Show b) => String -> ParsePuzzle a b -> Value -> TestTree+tests :: [TestTree]+tests = [ parseUtilTests, parseTests, parseDataTests, dataTests ]++testParsePzl ::+    Show a => String -> ParsePuzzle a b -> Value -> TestTree testParsePzl name parser yaml =     testCase ("parse " ++ name) $ testParse (fst parser) yaml+testParseSol ::+    Show b => String -> ParsePuzzle a b -> Value -> TestTree testParseSol name parser yaml =     testCase ("parse " ++ name ++ " (sol)") $ testParse (snd parser) yaml+testNonparsePzl ::+    Show a => String -> ParsePuzzle a b -> Value -> TestTree testNonparsePzl name parser yaml =     testCase ("don't parse broken " ++ name) $ testNonparse (fst parser) yaml+testNonparseSol ::+    Show b => String -> ParsePuzzle a b -> Value -> TestTree testNonparseSol name parser yaml =     testCase ("don't parse broken " ++ name ++ " (sol)") $         testNonparse (snd parser) yaml@@ -92,15 +109,15 @@ test_thermo_2 = either (const []) snd $                 parseEither (fst thermosudoku) thermo_2 -testThermo :: [Thermometer] -> [Thermometer] -> Assertion-testThermo t expect = sort t @?= expect+testThermo :: [Thermometer] -> [[Coord]] -> Assertion+testThermo t expect = sort t @?= map (map fromCoord) expect  test_tightfit_1 :: Bool test_tightfit_1 = either (const False) test_both res   where     res = parseEither (fst tightfitskyscrapers) tightfit_1     test_both (o, g) = test_size g && test_clues o-    test_size g = Grid.size g == (3, 3)+    test_size g = Grid.size (Map.mapKeys toCoord g) == (3, 3)     test_clues (Grid.OC l r b t) = l == [Nothing, Nothing, Just 3] &&                                    r == [Nothing, Just 4, Nothing] &&                                    b == [Just 3, Just 5, Nothing] &&@@ -118,19 +135,23 @@   where     res = parseEither parseMultiOutsideClues multioutside     oc = OC [[3], [1, 2]] [[1, 0], []] [[0, 0, 1]] [[1, -1]]-         :: OutsideClues [Int]+         :: OutsideClues Coord [Int] -test_edge_grid :: Assertion-test_edge_grid = Right (gn, gc, es) @=? res+test_plain_edge_grid :: Assertion+test_plain_edge_grid = Right (gn, gc, sort es) @=? res'   where-    res = parseEither parseEdgeGrid edgeGrid_1-    gn = Grid.Grid (Square 4 2) $ Map.fromList+    res = parseEither parsePlainEdgeGrid edgeGrid_1+    res' = fmap (\(x, y, e) -> (x, y, sort e)) res+    gn :: Grid.Grid N (Maybe MasyuPearl)+    gn = Map.mapKeys fromCoord . Map.fromList $         [((0,0),Just MBlack),((0,1),Just MWhite),((1,0),Just MWhite),((1,1),Just MBlack),          ((2,0),Nothing),((2,1),Just MBlack),((3,0),Nothing),((3,1),Just MWhite)]-    gc :: Grid.SGrid Int-    gc = Grid.Grid (Square 3 1) $ Map.fromList+    gc :: Grid.Grid C Int+    gc = Map.mapKeys fromCoord . Map.fromList $         [((0,0),1),((1,0),2),((2,0),3)]-    es = [E (0,0) H, E (0,1) H, E (1,1) H, E (2,1) H, E (0,0) V, E (1,0) V]+    es = map (\(E c d) -> E (fromCoord c) d)+         [E (0,0) Horiz, E (0,1) Horiz, E (1,1) Horiz, E (2,1) Horiz,+          E (0,0) Vert, E (1,0) Vert]  parseDataTests :: TestTree parseDataTests = testGroup "Parsing tests (full puzzles, result checks)"@@ -144,39 +165,70 @@                                             , [(0, 2), (1, 3), (2, 4)]                                             , [(4, 0), (4, 1), (3, 2)] ]     , testCase "parse multioutsideclues" $ test_multioutside-    , testCase "parse edge grid" $ test_edge_grid+    , testCase "parse edge grid" $ test_plain_edge_grid     ] --- this used to cause a rendering crash, though it's--- caught by parsing by now-testBreakSlalom :: Bool-testBreakSlalom =-    case parseMaybe (snd slalom) slalom_sol_broken of-        Nothing -> True-        Just s  -> let d = drawSlalomDiags s-                       svg = renderDia SVG (SVGOptions (Width 100) Nothing) d-                       svgt = renderSvg svg-                   in (show svgt) `deepseq` True--renderTests :: TestTree-renderTests = testGroup "Rendering tests"-    [ testCase "don't break rendering invalid slalom solution"-         $ testBreakSlalom @? "just testing against errors"-    ]+sorteq :: (Show a, Ord a) => [a] -> [a] -> Assertion+sorteq xs ys = sort xs @?= sort ys  testMultiOutsideClues :: Assertion-testMultiOutsideClues = multiOutsideClues (OC l r b t) `sorteq` res+testMultiOutsideClues = multiOutsideClues (OC l r b t) @=? res   where-    sorteq xs ys = sort xs @?= sort ys     l = [[1, 2], [3]]     r = [[], [1, 0]]     b = [[0, 0, 1]]     t = [[1, -1]]-    res = [ ((-1,0), 1), ((-2,0), 2), ((-1,1), 3),+    res :: Map.Map C Int+    res = Map.mapKeys fromCoord . Map.fromList $+          [ ((-1,0), 1), ((-2,0), 2), ((-1,1), 3),             ((1,1), 1), ((2,1), 0), ((0,-1), 0), ((0,-2), 0), ((0,-3), 1),-            ((0,2), 1), ((0,3), -1) ] :: [((Int, Int), Int)]+            ((0,2), 1), ((0,3), -1) ] +testEdges :: Assertion+testEdges = do+    inner `sorteq` expinner'+    outer `sorteq` expouter'+  where+    (outer, inner) = edges cs (`elem` cs)+    {-+      ###+      # #+      ###+    -}+    cs :: [C]+    cs = map fromCoord [(0,0), (1,0), (2,0), (0,1), (2,1), (0,2), (1,2), (2,2)]+    expouter = [((0,0),(0,1)), ((0,1),(0,2)), ((0,2),(0,3)), ((0,3),(1,3)),+                ((1,3),(2,3)), ((2,3),(3,3)), ((3,3),(3,2)), ((3,2),(3,1)),+                ((3,1),(3,0)), ((3,0),(2,0)), ((2,0),(1,0)), ((1,0),(0,0)),+                ((1,1),(2,1)), ((2,1),(2,2)), ((2,2),(1,2)), ((1,2),(1,1))]+    expouter' = map (uncurry edge' . fromCoord2) expouter+    expinner = [((0,1),(1,1)), ((0,2),(1,2)), ((1,0),(1,1)), ((2,0),(2,1)),+                ((1,3),(1,2)), ((2,3),(2,2)), ((3,1),(2,1)), ((3,2),(2,2))]+    expinner' = map (uncurry edge . fromCoord2) expinner+    fromCoord2 (p,q) = (fromCoord p, fromCoord q)++testLoops :: Assertion+testLoops = loops es @=? Just loopsexp+  -- rotations of the loops would be fine+  -- inside and outside of:+  --   xxx+  --   x x+  --   xxx+  where+    es = [((0,0),(0,1)), ((0,1),(0,2)), ((0,2),(0,3)), ((0,3),(1,3)),+          ((1,3),(2,3)), ((2,3),(3,3)), ((3,3),(3,2)), ((3,2),(3,1)),+          ((3,1),(3,0)), ((3,0),(2,0)), ((2,0),(1,0)), ((1,0),(0,0)),+          ((1,1),(2,1)), ((2,1),(2,2)), ((2,2),(1,2)), ((1,2),(1,1))]+    loopsexp :: [[(Int, Int)]]+    loopsexp =+        [ [(0,0), (0,1), (0,2), (0,3), (1,3), (2,3), (3,3), (3,2), (3,1),+           (3,0), (2,0), (1,0), (0,0)]+        , [(1,1), (2,1), (2,2), (1,2), (1,1)]+        ]+ dataTests :: TestTree dataTests = testGroup "Generic tests for the Data modules"     [ testCase "multiOutsideClues" testMultiOutsideClues+    , testCase "edges" testEdges+    , testCase "loops" testLoops     ]