packages feed

puzzle-draw 0.2.0.0 → 0.3.0.0

raw patch · 70 files changed

+5185/−4569 lines, 70 filesdep +diagrams-rasterificdep −diagrams-cairodep −diagrams-svgdep −processdep ~SVGFontsdep ~aesondep ~blaze-svgPVP ok

version bump matches the API change (PVP)

Dependencies added: diagrams-rasterific

Dependencies removed: diagrams-cairo, diagrams-svg, process, tasty-golden

Dependency ranges changed: SVGFonts, aeson, blaze-svg, bytestring, containers, deepseq, diagrams-lib, filepath, hashable, hspec, linear, mtl, optparse-applicative, parsec, tasty, tasty-hspec, tasty-hunit, text, unordered-containers, vector-space, yaml

API changes (from Hackage documentation)

- 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.Compose: drawPuzzleMaybeSol :: PuzzleHandler b ((Value, Maybe Value) -> Parser (Diagram b, Maybe (Diagram b)))
- Data.Puzzles.Compose: handle :: Backend' b => PuzzleHandler b a -> PuzzleType -> a
- Data.Puzzles.Compose: type PuzzleHandler b a = forall p q. ParsePuzzle p q -> RenderPuzzle b p q -> a
- Data.Puzzles.Elements: Black :: Black
- Data.Puzzles.Elements: CC :: (Maybe Int) -> (Maybe Int) -> (Maybe Int) -> (Maybe Int) -> CompassC
- Data.Puzzles.Elements: Crossing :: Crossing
- Data.Puzzles.Elements: DR :: a -> a -> Tightfit a
- 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: KBlack :: KropkiDot
- Data.Puzzles.Elements: KNone :: KropkiDot
- Data.Puzzles.Elements: KWhite :: KropkiDot
- Data.Puzzles.Elements: MBlack :: MasyuPearl
- Data.Puzzles.Elements: MEnd :: MEnd
- Data.Puzzles.Elements: MW :: Coord -> Coord -> MarkedWord
- Data.Puzzles.Elements: MWhite :: MasyuPearl
- Data.Puzzles.Elements: MarkedLine :: a -> a -> MarkedLine a
- Data.Puzzles.Elements: PlainNode :: PlainNode
- Data.Puzzles.Elements: PrimeDiag :: (Bool, Bool) -> PrimeDiag
- Data.Puzzles.Elements: REqual :: Relation
- Data.Puzzles.Elements: RGreater :: Relation
- Data.Puzzles.Elements: RLess :: Relation
- Data.Puzzles.Elements: RUndetermined :: Relation
- Data.Puzzles.Elements: Shade :: Bool -> Bool -> Shade
- Data.Puzzles.Elements: Single :: a -> Tightfit a
- Data.Puzzles.Elements: SlalomBackward :: SlalomDiag
- Data.Puzzles.Elements: SlalomForward :: SlalomDiag
- Data.Puzzles.Elements: SlovakClue :: !Int -> !Int -> SlovakClue
- Data.Puzzles.Elements: Star :: Star
- Data.Puzzles.Elements: TapaClue :: [Int] -> TapaClue
- Data.Puzzles.Elements: UR :: a -> a -> Tightfit a
- Data.Puzzles.Elements: [mwend] :: MarkedWord -> Coord
- Data.Puzzles.Elements: [mwstart] :: MarkedWord -> Coord
- Data.Puzzles.Elements: data Black
- Data.Puzzles.Elements: data CompassC
- Data.Puzzles.Elements: data Crossing
- Data.Puzzles.Elements: data DigitRange
- Data.Puzzles.Elements: data Fish
- Data.Puzzles.Elements: data Fraction
- Data.Puzzles.Elements: data GalaxyCentre
- Data.Puzzles.Elements: data KropkiDot
- Data.Puzzles.Elements: data MEnd
- Data.Puzzles.Elements: data MarkedLine a
- Data.Puzzles.Elements: data MarkedWord
- Data.Puzzles.Elements: data MasyuPearl
- Data.Puzzles.Elements: data PlainNode
- Data.Puzzles.Elements: data Relation
- Data.Puzzles.Elements: data Shade
- Data.Puzzles.Elements: data SlalomDiag
- Data.Puzzles.Elements: data SlovakClue
- Data.Puzzles.Elements: data Star
- Data.Puzzles.Elements: data Tightfit a
- 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: newtype PrimeDiag
- Data.Puzzles.Elements: newtype TapaClue
- Data.Puzzles.Elements: type BahnhofClue = Either Int Crossing
- Data.Puzzles.Elements: type Clue a = Maybe a
- Data.Puzzles.Elements: type CompassClue = Clue CompassC
- Data.Puzzles.Elements: type GreaterClue = [Relation]
- Data.Puzzles.Elements: type IntClue = Clue Int
- Data.Puzzles.Elements: type Loop a = [Edge a]
- Data.Puzzles.Elements: type MasyuClue = Clue MasyuPearl
- Data.Puzzles.Elements: type Myopia = [Dir']
- Data.Puzzles.Elements: type Thermometer = [C]
- Data.Puzzles.Elements: type VertexLoop = [N]
- Data.Puzzles.Grid: OC :: [a] -> [a] -> [a] -> [a] -> OutsideClues k a
- 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: borders :: Eq a => Grid C a -> [Edge N]
- Data.Puzzles.Grid: cellGrid :: Grid N a -> Grid C ()
- Data.Puzzles.Grid: clues :: Grid k (Maybe a) -> Grid k a
- 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: data OutsideClues k a
- Data.Puzzles.Grid: dominoGrid :: DigitRange -> Grid C (Int, Int)
- Data.Puzzles.Grid: edgesGen :: Dual' k => (a -> a -> Bool) -> (a -> Bool) -> Map k a -> [Edge (Dual k)]
- 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: multiOutsideClues :: (Ord k, FromCoord k) => OutsideClues k [a] -> Map k a
- Data.Puzzles.Grid: nodeGrid :: Grid C a -> Grid N ()
- Data.Puzzles.Grid: nodes :: Grid N a -> Set N
- Data.Puzzles.Grid: outsideClues :: (Ord k, FromCoord k) => OutsideClues k a -> Map k a
- Data.Puzzles.Grid: outsideGrid :: (Ord k, FromCoord k) => OutsideClues k a -> Grid k ()
- Data.Puzzles.Grid: outsideSize :: OutsideClues k a -> Size
- Data.Puzzles.Grid: size :: Grid Coord a -> Size
- Data.Puzzles.Grid: sizeGrid :: (Ord k, FromCoord k) => Size -> Grid k ()
- Data.Puzzles.Grid: type AreaGrid = Grid C Char
- Data.Puzzles.Grid: type Grid k a = Map k a
- Data.Puzzles.Grid: type ShadedGrid = Grid C Bool
- Data.Puzzles.GridShape: C :: !Int -> !Int -> C
- Data.Puzzles.GridShape: D :: Dir'
- Data.Puzzles.GridShape: E :: a -> Dir -> Edge a
- Data.Puzzles.GridShape: E' :: a -> Dir' -> Edge' a
- Data.Puzzles.GridShape: Horiz :: Dir
- Data.Puzzles.GridShape: L :: Dir'
- Data.Puzzles.GridShape: N :: !Int -> !Int -> N
- Data.Puzzles.GridShape: R :: Dir'
- Data.Puzzles.GridShape: Square :: Square
- Data.Puzzles.GridShape: U :: Dir'
- 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 Dir
- Data.Puzzles.GridShape: data Dir'
- Data.Puzzles.GridShape: data Edge a
- Data.Puzzles.GridShape: data Edge' a
- Data.Puzzles.GridShape: data N
- Data.Puzzles.GridShape: data Square
- 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: edgeNeighbours :: C -> [C]
- Data.Puzzles.GridShape: edgeSize :: [Edge N] -> Size
- Data.Puzzles.GridShape: edges :: (Dual' k, Foldable f) => f k -> (k -> Bool) -> ([Edge' (Dual k)], [Edge (Dual k)])
- 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 Coord = (Int, Int)
- 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 Size = (Int, Int)
- Data.Puzzles.GridShape: type family Dual a :: *;
- Data.Puzzles.GridShape: unorient :: (AffineSpace a, Diff a ~ (Int, Int)) => Edge' a -> Edge a
- Data.Puzzles.GridShape: vertexNeighbours :: C -> [C]
- Data.Puzzles.GridShape: }
- Data.Puzzles.PuzzleTypes: ABCtje :: PuzzleType
- Data.Puzzles.PuzzleTypes: AfternoonSkyscrapers :: PuzzleType
- Data.Puzzles.PuzzleTypes: AngleLoop :: PuzzleType
- Data.Puzzles.PuzzleTypes: Anglers :: PuzzleType
- Data.Puzzles.PuzzleTypes: Baca :: PuzzleType
- Data.Puzzles.PuzzleTypes: Bahnhof :: PuzzleType
- Data.Puzzles.PuzzleTypes: BlackoutDominos :: PuzzleType
- Data.Puzzles.PuzzleTypes: BoxOf2Or3 :: PuzzleType
- Data.Puzzles.PuzzleTypes: Buchstabensalat :: PuzzleType
- Data.Puzzles.PuzzleTypes: Cave :: PuzzleType
- Data.Puzzles.PuzzleTypes: ColorAkari :: PuzzleType
- Data.Puzzles.PuzzleTypes: Compass :: PuzzleType
- Data.Puzzles.PuzzleTypes: Coral :: PuzzleType
- Data.Puzzles.PuzzleTypes: CurveData :: PuzzleType
- Data.Puzzles.PuzzleTypes: DominoPillen :: PuzzleType
- Data.Puzzles.PuzzleTypes: Dominos :: PuzzleType
- Data.Puzzles.PuzzleTypes: Doppelblock :: PuzzleType
- Data.Puzzles.PuzzleTypes: DoubleBack :: PuzzleType
- Data.Puzzles.PuzzleTypes: Fillomino :: PuzzleType
- Data.Puzzles.PuzzleTypes: FillominoCheckered :: PuzzleType
- Data.Puzzles.PuzzleTypes: FillominoLoop :: PuzzleType
- Data.Puzzles.PuzzleTypes: Galaxies :: PuzzleType
- Data.Puzzles.PuzzleTypes: Geradeweg :: PuzzleType
- Data.Puzzles.PuzzleTypes: GreaterWall :: PuzzleType
- Data.Puzzles.PuzzleTypes: Heyawake :: PuzzleType
- Data.Puzzles.PuzzleTypes: HorseSnake :: PuzzleType
- Data.Puzzles.PuzzleTypes: Illumination :: PuzzleType
- Data.Puzzles.PuzzleTypes: JapaneseSums :: PuzzleType
- Data.Puzzles.PuzzleTypes: Kropki :: PuzzleType
- Data.Puzzles.PuzzleTypes: LITS :: PuzzleType
- Data.Puzzles.PuzzleTypes: LITSPlus :: PuzzleType
- Data.Puzzles.PuzzleTypes: LITSSym :: PuzzleType
- Data.Puzzles.PuzzleTypes: Labyrinth :: PuzzleType
- Data.Puzzles.PuzzleTypes: LatinTapa :: PuzzleType
- Data.Puzzles.PuzzleTypes: Loopki :: PuzzleType
- Data.Puzzles.PuzzleTypes: Masyu :: PuzzleType
- Data.Puzzles.PuzzleTypes: MaximalLengths :: PuzzleType
- Data.Puzzles.PuzzleTypes: MeanderingNumbers :: PuzzleType
- Data.Puzzles.PuzzleTypes: NanroSignpost :: PuzzleType
- Data.Puzzles.PuzzleTypes: Neighbors :: PuzzleType
- Data.Puzzles.PuzzleTypes: Numberlink :: PuzzleType
- Data.Puzzles.PuzzleTypes: Nurikabe :: 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: PrimePlace :: PuzzleType
- Data.Puzzles.PuzzleTypes: Pyramid :: PuzzleType
- Data.Puzzles.PuzzleTypes: RowKropkiPyramid :: 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: Slalom :: PuzzleType
- Data.Puzzles.PuzzleTypes: SlitherLink :: PuzzleType
- Data.Puzzles.PuzzleTypes: SlitherLinkLiar :: 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: Sudoku :: PuzzleType
- Data.Puzzles.PuzzleTypes: SudokuDoppelblock :: PuzzleType
- Data.Puzzles.PuzzleTypes: Summon :: PuzzleType
- Data.Puzzles.PuzzleTypes: Tapa :: PuzzleType
- Data.Puzzles.PuzzleTypes: TapaCave :: PuzzleType
- Data.Puzzles.PuzzleTypes: ThermoSudoku :: PuzzleType
- Data.Puzzles.PuzzleTypes: TightfitSkyscrapers :: PuzzleType
- Data.Puzzles.PuzzleTypes: TomTom :: PuzzleType
- Data.Puzzles.PuzzleTypes: TurningFences :: PuzzleType
- Data.Puzzles.PuzzleTypes: TwilightTapa :: PuzzleType
- Data.Puzzles.PuzzleTypes: WordLoop :: PuzzleType
- Data.Puzzles.PuzzleTypes: WordSearch :: PuzzleType
- Data.Puzzles.PuzzleTypes: Wormhole :: PuzzleType
- Data.Puzzles.PuzzleTypes: data 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.PuzzleTypes: lookupType :: String -> Maybe PuzzleType
- Data.Puzzles.PuzzleTypes: typeNames :: [(PuzzleType, String)]
- Data.Puzzles.Pyramid: KP :: [KropkiRow] -> RowKropkiPyramid
- Data.Puzzles.Pyramid: KR :: [Maybe Int] -> Bool -> [KropkiDot] -> KropkiRow
- Data.Puzzles.Pyramid: Pyr :: [Row] -> Pyramid
- Data.Puzzles.Pyramid: PyramidSol :: [[Int]] -> PyramidSol
- Data.Puzzles.Pyramid: R :: [Maybe Int] -> Bool -> Row
- 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: data KropkiRow
- Data.Puzzles.Pyramid: data 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.Pyramid: mergekpyramidsol :: RowKropkiPyramid -> PyramidSol -> RowKropkiPyramid
- Data.Puzzles.Pyramid: mergepyramidsol :: Pyramid -> PyramidSol -> Pyramid
- Data.Puzzles.Pyramid: newtype Pyramid
- Data.Puzzles.Pyramid: newtype PyramidSol
- Data.Puzzles.Pyramid: newtype RowKropkiPyramid
- Data.Puzzles.Pyramid: plainpyramid :: RowKropkiPyramid -> Pyramid
- Data.Puzzles.Pyramid: psize :: Pyramid -> Int
- Data.Puzzles.Sudoku: sudokuborders :: Int -> [Edge N]
- Data.Puzzles.Sudoku: sudokubordersg :: Grid C a -> [Edge N]
- Data.Puzzles.Util: loops :: Eq a => [(a, a)] -> Maybe [[a]]
- Data.Puzzles.Util: paths :: Eq a => [(a, a)] -> Maybe [[a]]
- Diagrams.Puzzles.CmdLine: RenderOpts :: FilePath -> Double -> RenderOpts
- Diagrams.Puzzles.CmdLine: [_file] :: RenderOpts -> FilePath
- Diagrams.Puzzles.CmdLine: [_w] :: RenderOpts -> Double
- Diagrams.Puzzles.CmdLine: checkFormat :: String -> IO ()
- Diagrams.Puzzles.CmdLine: data RenderOpts
- Diagrams.Puzzles.CmdLine: formats :: [String]
- Diagrams.Puzzles.CmdLine: renderToFile :: RenderOpts -> Diagram B -> IO ()
- Diagrams.Puzzles.CmdLine: type B = SVG
- 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: DrawExample :: OutputChoice
- Diagrams.Puzzles.Draw: DrawPuzzle :: OutputChoice
- Diagrams.Puzzles.Draw: DrawSolution :: OutputChoice
- Diagrams.Puzzles.Draw: Pixels :: Unit
- Diagrams.Puzzles.Draw: Points :: Unit
- Diagrams.Puzzles.Draw: data OutputChoice
- Diagrams.Puzzles.Draw: data Unit
- Diagrams.Puzzles.Draw: diagramWidth :: Backend' b => Diagram b -> Double
- Diagrams.Puzzles.Draw: draw :: Backend' b => Maybe (CodeDiagrams (Diagram b)) -> PuzzleSol b -> OutputChoice -> Maybe (Diagram b)
- Diagrams.Puzzles.Draw: instance GHC.Show.Show Diagrams.Puzzles.Draw.OutputChoice
- Diagrams.Puzzles.Draw: toOutputWidth :: Unit -> Double -> Double
- Diagrams.Puzzles.Draw: type PuzzleSol b = (Diagram b, Maybe (Diagram b))
- Diagrams.Puzzles.Draw: type RenderPuzzle b p s = (p -> Diagram b, (p, s) -> Diagram b)
- Diagrams.Puzzles.Elements: DominoC :: C -> DominoC
- Diagrams.Puzzles.Elements: cross :: Path V2 Double
- Diagrams.Puzzles.Elements: dominoBG :: Colour Double
- Diagrams.Puzzles.Elements: dr :: Path V2 Double
- Diagrams.Puzzles.Elements: drawAnglePoly :: Backend' b => Int -> Diagram b
- Diagrams.Puzzles.Elements: drawBahnhofClue :: Backend' b => BahnhofClue -> Diagram b
- Diagrams.Puzzles.Elements: drawChar :: Backend' b => Char -> Diagram b
- Diagrams.Puzzles.Elements: drawCharFixed :: Backend' b => Char -> Diagram b
- Diagrams.Puzzles.Elements: drawCharOpaque :: Backend' b => Char -> Diagram b
- Diagrams.Puzzles.Elements: drawCompassClue :: Backend' b => CompassC -> Diagram b
- Diagrams.Puzzles.Elements: drawCross :: Backend' b => Bool -> Diagram b
- Diagrams.Puzzles.Elements: drawCrossing :: Backend' b => Crossing -> Diagram b
- Diagrams.Puzzles.Elements: drawCurve :: Backend' b => [Edge N] -> 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: drawInt :: Backend' b => Int -> 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: drawMarkedWord :: Backend' b => MarkedWord -> Diagram b
- Diagrams.Puzzles.Elements: drawMarkedWords :: Backend' b => [MarkedWord] -> 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: drawPrimeDiag :: Backend' b => PrimeDiag -> Diagram b
- Diagrams.Puzzles.Elements: drawShadow :: Backend' b => Shade -> Diagram b
- Diagrams.Puzzles.Elements: drawSlalomClue :: (Show a, Backend' b) => a -> 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: drawTapaClue :: Backend' b => TapaClue -> Diagram b
- Diagrams.Puzzles.Elements: drawText :: Backend' b => String -> Diagram b
- Diagrams.Puzzles.Elements: drawTextFixed :: Backend' b => String -> Diagram b
- Diagrams.Puzzles.Elements: drawThermos :: Backend' b => [Thermometer] -> Diagram b
- Diagrams.Puzzles.Elements: drawTight :: Backend' b => (a -> Diagram b) -> Tightfit a -> Diagram b
- Diagrams.Puzzles.Elements: drawWords :: 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: pearl :: Backend' b => MasyuPearl -> 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: smallPearl :: Backend' b => MasyuPearl -> Diagram b
- Diagrams.Puzzles.Elements: stackWords :: Backend' b => [String] -> Diagram b
- Diagrams.Puzzles.Elements: stackWordsLeft :: Backend' b => [String] -> Diagram b
- Diagrams.Puzzles.Elements: stackWordsRight :: Backend' b => [String] -> Diagram b
- Diagrams.Puzzles.Elements: thermo :: Backend' b => [P2 Double] -> Diagram b
- Diagrams.Puzzles.Elements: ur :: Path V2 Double
- Diagrams.Puzzles.Elements: vertexLoop :: VertexLoop -> Located (Trail' Loop V2 Double)
- Diagrams.Puzzles.Grid: (.--.) :: AffineSpace p => p -> p -> Diff p
- Diagrams.Puzzles.Grid: bgdashingG :: (Semigroup a, HasStyle a, InSpace V2 Double a) => [Double] -> Double -> AlphaColour Double -> a -> a
- Diagrams.Puzzles.Grid: class ToPoint a
- Diagrams.Puzzles.Grid: dashes :: [Double]
- Diagrams.Puzzles.Grid: dashoffset :: Double
- 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
- 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: drawEdges :: (ToPoint k, Backend' b) => [Edge k] -> 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: edge :: (ToPoint k) => Edge k -> Path V2 Double
- Diagrams.Puzzles.Grid: edgeStyle :: (HasStyle a, InSpace V2 Double a) => a -> a
- Diagrams.Puzzles.Grid: fillBG :: Backend' b => Colour Double -> Diagram b
- Diagrams.Puzzles.Grid: grid :: Backend' b => GridStyle -> Grid C a -> Diagram b
- Diagrams.Puzzles.Grid: gridDashing :: (Semigroup a, HasStyle a, InSpace V2 Double a) => a -> a
- 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: irregularGridPaths :: Grid C a -> (Path V2 Double, Path V2 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: shadeGrid :: Backend' b => Grid C (Maybe (Colour Double)) -> Diagram b
- Diagrams.Puzzles.Grid: thinEdgeStyle :: (HasStyle a, InSpace V2 Double a) => 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: besidesL :: Backend' b => Diagram b -> Diagram b -> Diagram b
- Diagrams.Puzzles.Lib: besidesR :: 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: dmid :: (InSpace V2 Double a, Enveloped a) => V2 Double -> a -> Double
- Diagrams.Puzzles.Lib: fit :: (Transformable t, Enveloped t, InSpace V2 Double t) => Double -> t -> t
- Diagrams.Puzzles.Lib: fontBit :: Font
- Diagrams.Puzzles.Lib: fontGenLight :: Font
- 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
- Diagrams.Puzzles.Lib: interleave :: [a] -> [a] -> [a]
- Diagrams.Puzzles.Lib: magnitude :: V2 Double -> Double
- Diagrams.Puzzles.Lib: mirror :: (InSpace V2 Double t, Transformable t) => t -> t
- Diagrams.Puzzles.Lib: p2i :: (Int, Int) -> P2 Double
- Diagrams.Puzzles.Lib: phantom' :: Backend' b => Diagram b -> Diagram b
- Diagrams.Puzzles.Lib: r2i :: (Int, Int) -> V2 Double
- Diagrams.Puzzles.Lib: smash :: Backend' b => Diagram b -> Diagram b
- Diagrams.Puzzles.Lib: spread :: Backend' b => V2 Double -> [Diagram b] -> Diagram b
- Diagrams.Puzzles.Lib: text' :: Backend' b => String -> Diagram b
- Diagrams.Puzzles.Lib: text'' :: Backend' b => Font -> String -> Diagram b
- Diagrams.Puzzles.Lib: textFixed :: Backend' b => String -> Diagram b
- Diagrams.Puzzles.Lib: translatep :: (InSpace V2 Double t, Transformable t) => (Int, Int) -> t -> t
- Diagrams.Puzzles.Lib: type Backend' b = (V b ~ V2, N b ~ Double, Renderable (Path V2 Double) b, Backend b V2 Double)
- Diagrams.Puzzles.Lib: type Font = PreparedFont Double
- 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
- Diagrams.Puzzles.PuzzleGrids: drawCharGrid :: Backend' b => Grid C (Maybe Char) -> Diagram b
- Diagrams.Puzzles.PuzzleGrids: drawIntGrid :: Backend' b => Grid C (Maybe Int) -> 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: drawSlitherGrid :: Backend' b => Grid C (Maybe Int) -> Diagram b
- Diagrams.Puzzles.PuzzleGrids: drawTightGrid :: Backend' b => (t -> Diagram b) -> Grid C (Tightfit t) -> Diagram b
- Diagrams.Puzzles.PuzzleGrids: drawWordsClues :: Backend' b => Grid C (Maybe [String]) -> Diagram b
- Diagrams.Puzzles.PuzzleGrids: outsideIntGrid :: Backend' b => OutsideClues C [Int] -> 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.PuzzleGrids: sudokugrid :: Backend' b => Grid C a -> Diagram b
- Diagrams.Puzzles.PuzzleTypes: abctje :: Backend' b => RenderPuzzle b (DigitRange, [(String, Int)]) [(Int, Char)]
- Diagrams.Puzzles.PuzzleTypes: afternoonskyscrapers :: Backend' b => RenderPuzzle b (Grid C Shade) (Grid C (Maybe Int))
- 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: bahnhof :: Backend' b => RenderPuzzle b (Grid C (Maybe BahnhofClue)) [Edge C]
- Diagrams.Puzzles.PuzzleTypes: blackoutDominos :: Backend' b => RenderPuzzle b (Grid C (Clue Int), DigitRange) (Grid C (Clue Int), AreaGrid)
- Diagrams.Puzzles.PuzzleTypes: boxof2or3 :: Backend' b => RenderPuzzle b (Grid N MasyuPearl, [Edge N]) ()
- Diagrams.Puzzles.PuzzleTypes: buchstabensalat :: Backend' b => RenderPuzzle b (OutsideClues C (Maybe Char), String) (Grid C (Maybe Char))
- Diagrams.Puzzles.PuzzleTypes: cave :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) ShadedGrid
- Diagrams.Puzzles.PuzzleTypes: colorakari :: Backend' b => RenderPuzzle b (Grid C (Maybe Char)) (Grid C (Maybe Char))
- Diagrams.Puzzles.PuzzleTypes: compass :: Backend' b => RenderPuzzle b (Grid C (Maybe CompassC)) AreaGrid
- Diagrams.Puzzles.PuzzleTypes: coral :: Backend' b => RenderPuzzle b (OutsideClues C [String]) ShadedGrid
- Diagrams.Puzzles.PuzzleTypes: curvedata :: Backend' b => RenderPuzzle b (Grid C (Maybe [Edge N])) [Edge C]
- 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: doubleback :: Backend' b => RenderPuzzle b AreaGrid (Loop C)
- Diagrams.Puzzles.PuzzleTypes: fillomino :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) (Grid C 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: geradeweg :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) (Loop C)
- 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: japanesesums :: Backend' b => RenderPuzzle b (OutsideClues C [Int], String) (Grid C (Either Black Int))
- Diagrams.Puzzles.PuzzleTypes: kpyramid :: Backend' b => RenderPuzzle b RowKropkiPyramid PyramidSol
- Diagrams.Puzzles.PuzzleTypes: kropki :: Backend' b => RenderPuzzle b (Map (Edge N) KropkiDot) (Grid C 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 (Grid C (Maybe [String])) (Grid C (Maybe Char))
- Diagrams.Puzzles.PuzzleTypes: liarslither :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) (Loop N, Grid C Bool)
- Diagrams.Puzzles.PuzzleTypes: lits :: Backend' b => RenderPuzzle b AreaGrid ShadedGrid
- Diagrams.Puzzles.PuzzleTypes: litsplus :: Backend' b => RenderPuzzle b AreaGrid ShadedGrid
- 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: masyu :: Backend' b => RenderPuzzle b (Grid C (Maybe MasyuPearl)) (Loop C)
- Diagrams.Puzzles.PuzzleTypes: maximallengths :: Backend' b => RenderPuzzle b (OutsideClues C (Maybe Int)) (Loop C)
- Diagrams.Puzzles.PuzzleTypes: meanderingnumbers :: Backend' b => RenderPuzzle b AreaGrid (Grid C (Maybe Int))
- 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: nurikabe :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) ShadedGrid
- 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: primeplace :: Backend' b => RenderPuzzle b (Grid C PrimeDiag) (Grid C Int)
- Diagrams.Puzzles.PuzzleTypes: pyramid :: Backend' b => RenderPuzzle b Pyramid PyramidSol
- 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: slalom :: Backend' b => RenderPuzzle b (Grid N (Maybe Int)) (Grid C SlalomDiag)
- Diagrams.Puzzles.PuzzleTypes: slither :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) (Loop N)
- 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: sudoku :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) (Grid C (Maybe Int))
- 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: tapa :: Backend' b => RenderPuzzle b (Grid C (Maybe TapaClue)) ShadedGrid
- 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 C (Maybe Int), Grid C (Tightfit ())) (Grid C (Tightfit Int))
- Diagrams.Puzzles.PuzzleTypes: tomTom :: Backend' b => RenderPuzzle b (AreaGrid, Grid C (Maybe String)) (Grid C Int)
- 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 (Grid C (Maybe Char), [String]) (Grid C (Maybe Char), [MarkedWord])
- Diagrams.Puzzles.PuzzleTypes: wormhole :: Backend' b => RenderPuzzle b (Grid C (Maybe (Either Int Char))) ()
- Diagrams.Puzzles.Pyramid: cell :: Backend' b => Bool -> Diagram b
- Diagrams.Puzzles.Pyramid: cellc :: Backend' b => Bool -> Maybe Int -> Diagram b
- Diagrams.Puzzles.Pyramid: clue :: Backend' b => Maybe Int -> Diagram b
- Diagrams.Puzzles.Pyramid: kpyramid :: Backend' b => RowKropkiPyramid -> Diagram b
- Diagrams.Puzzles.Pyramid: krow :: Backend' b => KropkiRow -> Diagram b
- Diagrams.Puzzles.Pyramid: pgray :: Colour Double
- Diagrams.Puzzles.Pyramid: pyramid :: Backend' b => Pyramid -> Diagram b
- Diagrams.Puzzles.Pyramid: row :: Backend' b => Row -> Diagram b
- 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
- Diagrams.Puzzles.Widths: borderwidth :: Double
- Diagrams.Puzzles.Widths: edgewidth :: Double
- Diagrams.Puzzles.Widths: fourpix :: Double
- Diagrams.Puzzles.Widths: framewidthfactor :: Double
- Diagrams.Puzzles.Widths: gridres :: Int
- Diagrams.Puzzles.Widths: gridresd :: Double
- Diagrams.Puzzles.Widths: gridwidth :: Double
- Diagrams.Puzzles.Widths: onepix :: Double
- Diagrams.Puzzles.Widths: twopix :: Double
- 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: TP :: (Maybe String) -> Value -> (Maybe Value) -> (Maybe Value) -> TypedPuzzle
- Text.Puzzles.Puzzle: data TypedPuzzle
- 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.Puzzle: parseType :: String -> Parser PuzzleType
- Text.Puzzles.Puzzle: type ParsePuzzle a b = (Value -> Parser a, Value -> Parser b)
- Text.Puzzles.PuzzleTypes: abctje :: ParsePuzzle (DigitRange, [(String, Int)]) [(Int, Char)]
- Text.Puzzles.PuzzleTypes: afternoonskyscrapers :: ParsePuzzle (Grid C Shade) (Grid C (Maybe Int))
- 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: bahnhof :: ParsePuzzle (Grid C (Maybe BahnhofClue)) [Edge C]
- Text.Puzzles.PuzzleTypes: blackoutDominos :: ParsePuzzle (Grid C (Clue Int), DigitRange) (Grid C (Clue Int), AreaGrid)
- Text.Puzzles.PuzzleTypes: boxof2or3 :: ParsePuzzle (Grid N MasyuPearl, [Edge N]) ()
- Text.Puzzles.PuzzleTypes: buchstabensalat :: ParsePuzzle (OutsideClues C (Maybe Char), String) (Grid C (Maybe Char))
- Text.Puzzles.PuzzleTypes: cave :: ParsePuzzle (Grid C (Maybe Int)) (Grid C Bool)
- Text.Puzzles.PuzzleTypes: colorakari :: ParsePuzzle (Grid C (Maybe Char)) (Grid C (Maybe Char))
- Text.Puzzles.PuzzleTypes: compass :: ParsePuzzle (Grid C (Maybe CompassC)) AreaGrid
- Text.Puzzles.PuzzleTypes: coral :: ParsePuzzle (OutsideClues C [String]) (Grid C Bool)
- Text.Puzzles.PuzzleTypes: curvedata :: ParsePuzzle (Grid C (Maybe [Edge N])) [Edge C]
- 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: doubleback :: ParsePuzzle AreaGrid (Loop C)
- Text.Puzzles.PuzzleTypes: fillomino :: ParsePuzzle (Grid C (Maybe Int)) (Grid C 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: geradeweg :: ParsePuzzle (Grid C (Maybe Int)) (Loop C)
- 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: japanesesums :: ParsePuzzle (OutsideClues C [Int], String) (Grid C (Either Black Int))
- Text.Puzzles.PuzzleTypes: kpyramid :: ParsePuzzle RowKropkiPyramid PyramidSol
- Text.Puzzles.PuzzleTypes: kropki :: ParsePuzzle (Map (Edge N) KropkiDot) (Grid C Int)
- Text.Puzzles.PuzzleTypes: labyrinth :: ParsePuzzle (Grid C (Maybe Int), [Edge N]) (Grid C (Maybe Int))
- Text.Puzzles.PuzzleTypes: latintapa :: ParsePuzzle (Grid C (Maybe [String])) (Grid C (Maybe Char))
- Text.Puzzles.PuzzleTypes: liarslither :: ParsePuzzle (Grid C (Maybe Int)) (Loop N, Grid C Bool)
- Text.Puzzles.PuzzleTypes: lits :: ParsePuzzle AreaGrid (Grid C Bool)
- Text.Puzzles.PuzzleTypes: litsplus :: ParsePuzzle AreaGrid (Grid C Bool)
- Text.Puzzles.PuzzleTypes: loopki :: ParsePuzzle (Grid C (Maybe MasyuPearl)) (Loop N)
- Text.Puzzles.PuzzleTypes: masyu :: ParsePuzzle (Grid C (Maybe MasyuPearl)) (Loop C)
- Text.Puzzles.PuzzleTypes: maximallengths :: ParsePuzzle (OutsideClues C (Maybe Int)) (Loop C)
- Text.Puzzles.PuzzleTypes: meanderingnumbers :: ParsePuzzle AreaGrid (Grid C (Maybe Int))
- 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: nurikabe :: ParsePuzzle (Grid C (Maybe Int)) (Grid C Bool)
- 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: primeplace :: ParsePuzzle (Grid C PrimeDiag) (Grid C Int)
- Text.Puzzles.PuzzleTypes: pyramid :: ParsePuzzle Pyramid PyramidSol
- 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: slalom :: ParsePuzzle (Grid N (Maybe Int)) (Grid C SlalomDiag)
- Text.Puzzles.PuzzleTypes: slither :: ParsePuzzle (Grid C (Clue Int)) (Loop N)
- 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: sudoku :: ParsePuzzle (Grid C (Maybe Int)) (Grid C (Maybe Int))
- 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: tapa :: ParsePuzzle (Grid C (Maybe TapaClue)) (Grid C Bool)
- Text.Puzzles.PuzzleTypes: thermosudoku :: ParsePuzzle (Grid C (Maybe Int), [Thermometer]) (Grid C (Maybe Int))
- Text.Puzzles.PuzzleTypes: tightfitskyscrapers :: ParsePuzzle (OutsideClues C (Maybe Int), Grid C (Tightfit ())) (Grid C (Tightfit Int))
- Text.Puzzles.PuzzleTypes: tomTom :: ParsePuzzle (AreaGrid, Grid C (Maybe String)) (Grid C Int)
- Text.Puzzles.PuzzleTypes: wordloop :: ParsePuzzle (Grid C (Maybe Char), [String]) (Grid C (Maybe Char))
- Text.Puzzles.PuzzleTypes: wordsearch :: ParsePuzzle (Grid C (Maybe Char), [String]) (Grid C (Maybe Char), [MarkedWord])
- Text.Puzzles.PuzzleTypes: wormhole :: ParsePuzzle (Grid C (Maybe (Either Int Char))) ()
- Text.Puzzles.Util: Alpha :: Char -> Alpha
- Text.Puzzles.Util: Blank :: Blank
- Text.Puzzles.Util: Blank' :: Blank'
- Text.Puzzles.Util: Blank'' :: Blank''
- Text.Puzzles.Util: Border :: [a] -> [a] -> [a] -> [a] -> Border a
- Text.Puzzles.Util: BorderedRect :: !Int -> !Int -> [[a]] -> (Border b) -> BorderedRect a b
- Text.Puzzles.Util: Dirs' :: [Dir'] -> Dirs'
- Text.Puzzles.Util: Either' :: Either a b -> Either' a b
- Text.Puzzles.Util: Empty :: Empty
- Text.Puzzles.Util: HalfDirs :: [Dir] -> HalfDirs
- Text.Puzzles.Util: IntString :: String -> IntString
- Text.Puzzles.Util: MM :: Map k (Maybe a) -> MaybeMap k a
- Text.Puzzles.Util: PCC :: CompassC -> PCompassC
- Text.Puzzles.Util: PFraction :: Fraction -> PFraction
- Text.Puzzles.Util: PML :: MarkedLine a -> PMarkedLine a
- Text.Puzzles.Util: PMW :: MarkedWord -> PMarkedWord
- Text.Puzzles.Util: PSlovakClue :: SlovakClue -> PSlovakClue
- Text.Puzzles.Util: ParseTapaClue :: TapaClue -> ParseTapaClue
- Text.Puzzles.Util: Rect :: !Int -> !Int -> [[a]] -> Rect a
- Text.Puzzles.Util: RefGrid :: Grid k (Maybe a) -> RefGrid k a
- Text.Puzzles.Util: Shaded :: Bool -> Shaded
- Text.Puzzles.Util: Space :: Space
- Text.Puzzles.Util: SpacedRect :: Rect a -> SpacedRect a
- Text.Puzzles.Util: Tight :: Tightfit () -> Tight
- 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: blankToMaybe' :: Either Blank' a -> Maybe a
- 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: class FromChar a
- Text.Puzzles.Util: class FromString a
- Text.Puzzles.Util: compose :: Ord b => Map a b -> Map b c -> Maybe (Map a c)
- 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: data Blank'
- Text.Puzzles.Util: data Blank''
- Text.Puzzles.Util: data Border a
- Text.Puzzles.Util: data BorderedRect a b
- Text.Puzzles.Util: data Empty
- Text.Puzzles.Util: data HalfDirs
- Text.Puzzles.Util: data Rect a
- Text.Puzzles.Util: data Space
- Text.Puzzles.Util: field :: Path -> Value -> Parser Value
- 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: hashmaptomap :: Ord a => HashMap a b -> Map a b
- 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: listListToMap :: [[a]] -> Grid Coord a
- Text.Puzzles.Util: newtype Alpha
- Text.Puzzles.Util: newtype Dirs'
- Text.Puzzles.Util: newtype Either' a b
- Text.Puzzles.Util: newtype IntString
- Text.Puzzles.Util: newtype MaybeMap k a
- Text.Puzzles.Util: newtype PCompassC
- Text.Puzzles.Util: newtype PFraction
- Text.Puzzles.Util: newtype PMarkedLine a
- Text.Puzzles.Util: newtype PMarkedWord
- Text.Puzzles.Util: newtype PSlovakClue
- Text.Puzzles.Util: newtype ParseTapaClue
- Text.Puzzles.Util: newtype RefGrid k a
- Text.Puzzles.Util: newtype Shaded
- Text.Puzzles.Util: newtype SpacedRect a
- Text.Puzzles.Util: newtype Tight
- Text.Puzzles.Util: parseAfternoonGrid :: Value -> Parser (Grid C Shade)
- 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: parseCellEdges :: FromChar a => Value -> Parser (Grid C a, [Edge N])
- Text.Puzzles.Util: parseChar :: FromChar a => Char -> Parser a
- Text.Puzzles.Util: parseCharJSON :: FromChar a => Value -> Parser a
- Text.Puzzles.Util: parseCharMap :: FromJSON a => Value -> Parser (Map Char a)
- Text.Puzzles.Util: parseCharOutside :: FromChar a => Value -> Parser (OutsideClues k a)
- Text.Puzzles.Util: parseClueGrid :: (FromChar a, Key k) => Value -> Parser (Grid k (Maybe a))
- Text.Puzzles.Util: parseClueGrid' :: (FromChar a, Key k) => Value -> Parser (Grid k (Maybe a))
- 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: parseEdgeGrid :: (FromChar a, FromChar b, FromChar c) => Value -> Parser (Grid N a, Grid C b, Map (Edge N) c)
- Text.Puzzles.Util: parseEdges :: Key k => Value -> Parser [Edge k]
- 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, FromChar a, FromJSON a) => Value -> Parser (Grid k a)
- Text.Puzzles.Util: parseExtGrid' :: (Key k, FromJSON a, FromChar b) => (a -> b) -> Value -> Parser (Grid k b)
- Text.Puzzles.Util: parseFrom :: Path -> (Value -> Parser b) -> Value -> Parser b
- Text.Puzzles.Util: parseGreaterClue :: [Char] -> Parser GreaterClue
- Text.Puzzles.Util: parseGreaterClues :: Value -> Parser [GreaterClue]
- Text.Puzzles.Util: parseGrid :: (Key k, FromChar a) => Value -> Parser (Grid k a)
- Text.Puzzles.Util: parseGridChars :: FromChar a => Grid k Char -> Parser (Grid k a)
- Text.Puzzles.Util: parseGridWith :: Key k => (Char -> Parser a) -> Value -> Parser (Grid k a)
- Text.Puzzles.Util: parseIrregGrid :: (Key k, FromChar a) => Value -> Parser (Grid k a)
- Text.Puzzles.Util: parseLine :: FromChar a => String -> Parser [a]
- Text.Puzzles.Util: parseMarkedLine :: FromCoord a => Value -> Parser (MarkedLine a)
- Text.Puzzles.Util: parseMultiOutsideClues :: FromJSON a => Value -> Parser (OutsideClues k [a])
- Text.Puzzles.Util: parseNWords :: Int -> String -> Parser [String]
- Text.Puzzles.Util: parseNodeEdges :: FromChar a => Value -> Parser (Grid N a, [Edge N])
- Text.Puzzles.Util: parseOutside :: FromJSON a => Value -> Parser (OutsideClues k 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: parsePlainEdges :: Key k => Value -> Parser [Edge k]
- Text.Puzzles.Util: parseShadedGrid :: Key k => Value -> Parser (Grid k Bool)
- Text.Puzzles.Util: parseSpacedClueGrid :: (Key k, FromString a) => Value -> Parser (Grid k (Maybe a))
- Text.Puzzles.Util: parseSpacedGrid :: (Key k, FromString a) => Value -> Parser (Grid k a)
- Text.Puzzles.Util: parseString :: FromString a => String -> Parser a
- Text.Puzzles.Util: parseStringJSON :: FromString a => Value -> Parser a
- Text.Puzzles.Util: parseThermoGrid :: ThermoRect -> Parser (Grid C (Maybe Int), [Thermometer])
- Text.Puzzles.Util: parseThermos :: Grid C Alpha -> Parser [Thermometer]
- Text.Puzzles.Util: parseTightOutside :: Value -> Parser (OutsideClues C (Maybe Int), Grid C (Tightfit ()))
- Text.Puzzles.Util: parseWithReplacement :: FromChar a => (Char -> Maybe a) -> Char -> Parser a
- Text.Puzzles.Util: partitionEithers :: Ord k => Map k (Either a b) -> (Map k a, Map k b)
- Text.Puzzles.Util: readEdges :: Grid Coord Char -> Map (Edge Coord) Char
- Text.Puzzles.Util: rectToCoordGrid :: Rect a -> Grid Coord a
- Text.Puzzles.Util: rectToIrregGrid :: Rect (Either Empty a) -> Grid Coord a
- Text.Puzzles.Util: reorientOutside :: OutsideClues k a -> OutsideClues k a
- Text.Puzzles.Util: type Path = [String]
- Text.Puzzles.Util: type ThermoRect = Rect (Either Blank (Either Int Alpha))
+ Data.CmdLine: checkType :: Maybe String -> IO PuzzleType
+ Data.CmdLine: exitErr :: String -> IO a
+ Data.CmdLine: readPuzzle :: FilePath -> IO (Either ParseException TypedPuzzle)
+ Data.Code: Cols :: [Int] -> CodePart
+ Data.Code: ColsN :: [Int] -> CodePart
+ Data.Code: LCols :: (Map Char Int) -> CodePart
+ Data.Code: LColsN :: (Map Char Int) -> CodePart
+ Data.Code: LRows' :: (Map Char Int) -> CodePart
+ Data.Code: LRowsN' :: (Map Char Int) -> CodePart
+ Data.Code: LabelsN :: (Grid N (Maybe Char)) -> CodePart
+ Data.Code: Rows' :: [Int] -> CodePart
+ Data.Code: RowsN' :: [Int] -> CodePart
+ Data.Code: data CodePart
+ Data.Code: type Code = [CodePart]
+ Data.Compose: drawPuzzleMaybeSol :: PuzzleHandler b ((Value, Maybe Value) -> Parser (Diagram b, Maybe (Diagram b)))
+ Data.Compose: handle :: Backend' b => PuzzleHandler b a -> PuzzleType -> a
+ Data.Compose: type PuzzleHandler b a = forall p q. ParsePuzzle p q -> Drawers b p q -> a
+ Data.Elements: Black :: Black
+ Data.Elements: CC :: (Maybe Int) -> (Maybe Int) -> (Maybe Int) -> (Maybe Int) -> CompassC
+ Data.Elements: Crossing :: Crossing
+ Data.Elements: DR :: a -> a -> Tightfit a
+ Data.Elements: DigitRange :: !Int -> !Int -> DigitRange
+ Data.Elements: FComp :: String -> String -> String -> Fraction
+ Data.Elements: FFrac :: String -> String -> Fraction
+ Data.Elements: FInt :: String -> Fraction
+ Data.Elements: Fish :: Fish
+ Data.Elements: GalaxyCentre :: GalaxyCentre
+ Data.Elements: KBlack :: KropkiDot
+ Data.Elements: KNone :: KropkiDot
+ Data.Elements: KWhite :: KropkiDot
+ Data.Elements: MBlack :: MasyuPearl
+ Data.Elements: MEnd :: MEnd
+ Data.Elements: MW :: Coord -> Coord -> MarkedWord
+ Data.Elements: MWhite :: MasyuPearl
+ Data.Elements: MarkedLine :: a -> a -> MarkedLine a
+ Data.Elements: Pentomino :: Char -> Pentomino
+ Data.Elements: PlainNode :: PlainNode
+ Data.Elements: PrimeDiag :: (Bool, Bool) -> PrimeDiag
+ Data.Elements: REqual :: Relation
+ Data.Elements: RGreater :: Relation
+ Data.Elements: RLess :: Relation
+ Data.Elements: RUndetermined :: Relation
+ Data.Elements: Shade :: Bool -> Bool -> Shade
+ Data.Elements: Single :: a -> Tightfit a
+ Data.Elements: SlalomBackward :: SlalomDiag
+ Data.Elements: SlalomForward :: SlalomDiag
+ Data.Elements: SlovakClue :: !Int -> !Int -> SlovakClue
+ Data.Elements: Star :: Star
+ Data.Elements: TapaClue :: [Int] -> TapaClue
+ Data.Elements: Tent :: Dir' -> PlacedTent
+ Data.Elements: Tree :: Tree
+ Data.Elements: UR :: a -> a -> Tightfit a
+ Data.Elements: [mwend] :: MarkedWord -> Coord
+ Data.Elements: [mwstart] :: MarkedWord -> Coord
+ Data.Elements: data Black
+ Data.Elements: data CompassC
+ Data.Elements: data Crossing
+ Data.Elements: data DigitRange
+ Data.Elements: data Fish
+ Data.Elements: data Fraction
+ Data.Elements: data GalaxyCentre
+ Data.Elements: data KropkiDot
+ Data.Elements: data MEnd
+ Data.Elements: data MarkedLine a
+ Data.Elements: data MarkedWord
+ Data.Elements: data MasyuPearl
+ Data.Elements: data Pentomino
+ Data.Elements: data PlacedTent
+ Data.Elements: data PlainNode
+ Data.Elements: data Relation
+ Data.Elements: data Shade
+ Data.Elements: data SlalomDiag
+ Data.Elements: data SlovakClue
+ Data.Elements: data Star
+ Data.Elements: data Tightfit a
+ Data.Elements: data Tree
+ Data.Elements: digitList :: DigitRange -> [Int]
+ Data.Elements: instance GHC.Classes.Eq Data.Elements.Black
+ Data.Elements: instance GHC.Classes.Eq Data.Elements.Crossing
+ Data.Elements: instance GHC.Classes.Eq Data.Elements.DigitRange
+ Data.Elements: instance GHC.Classes.Eq Data.Elements.Fish
+ Data.Elements: instance GHC.Classes.Eq Data.Elements.KropkiDot
+ Data.Elements: instance GHC.Classes.Eq Data.Elements.MasyuPearl
+ Data.Elements: instance GHC.Classes.Eq Data.Elements.Pentomino
+ Data.Elements: instance GHC.Classes.Eq Data.Elements.Relation
+ Data.Elements: instance GHC.Classes.Eq Data.Elements.Star
+ Data.Elements: instance GHC.Classes.Ord Data.Elements.KropkiDot
+ Data.Elements: instance GHC.Show.Show Data.Elements.CompassC
+ Data.Elements: instance GHC.Show.Show Data.Elements.DigitRange
+ Data.Elements: instance GHC.Show.Show Data.Elements.KropkiDot
+ Data.Elements: instance GHC.Show.Show Data.Elements.MasyuPearl
+ Data.Elements: instance GHC.Show.Show Data.Elements.Pentomino
+ Data.Elements: instance GHC.Show.Show Data.Elements.Relation
+ Data.Elements: instance GHC.Show.Show Data.Elements.Shade
+ Data.Elements: instance GHC.Show.Show Data.Elements.SlalomDiag
+ Data.Elements: instance GHC.Show.Show Data.Elements.TapaClue
+ Data.Elements: instance GHC.Show.Show a => GHC.Show.Show (Data.Elements.Tightfit a)
+ Data.Elements: newtype PrimeDiag
+ Data.Elements: newtype TapaClue
+ Data.Elements: type BahnhofClue = Either Int Crossing
+ Data.Elements: type Clue a = Maybe a
+ Data.Elements: type CompassClue = Clue CompassC
+ Data.Elements: type GreaterClue = [Relation]
+ Data.Elements: type IntClue = Clue Int
+ Data.Elements: type Loop a = [Edge a]
+ Data.Elements: type MasyuClue = Clue MasyuPearl
+ Data.Elements: type Myopia = [Dir']
+ Data.Elements: type Thermometer = [C]
+ Data.Elements: type VertexLoop = [N]
+ Data.Grid: OC :: [a] -> [a] -> [a] -> [a] -> OutsideClues k a
+ Data.Grid: [bottom] :: OutsideClues k a -> [a]
+ Data.Grid: [left] :: OutsideClues k a -> [a]
+ Data.Grid: [right] :: OutsideClues k a -> [a]
+ Data.Grid: [top] :: OutsideClues k a -> [a]
+ Data.Grid: borders :: Eq a => Grid C a -> [Edge N]
+ Data.Grid: cellGrid :: Grid N a -> Grid C ()
+ Data.Grid: clues :: Grid k (Maybe a) -> Grid k a
+ Data.Grid: collectLines :: (Key k, Eq a) => Grid k (Maybe a) -> [Edge k]
+ Data.Grid: colour :: Eq a => Grid C a -> Grid C Int
+ Data.Grid: data OutsideClues k a
+ Data.Grid: dominoGrid :: DigitRange -> Grid C (Int, Int)
+ Data.Grid: edgesGen :: Dual' k => (a -> a -> Bool) -> (a -> Bool) -> Map k a -> [Edge (Dual k)]
+ Data.Grid: instance GHC.Base.Functor (Data.Grid.OutsideClues k)
+ Data.Grid: instance GHC.Classes.Eq Data.Grid.OClue
+ Data.Grid: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.Grid.OutsideClues k a)
+ Data.Grid: instance GHC.Classes.Ord Data.Grid.OClue
+ Data.Grid: instance GHC.Show.Show Data.Grid.OClue
+ Data.Grid: instance GHC.Show.Show a => GHC.Show.Show (Data.Grid.OutsideClues k a)
+ Data.Grid: litsGrid :: Grid C (Maybe Char)
+ Data.Grid: litsoGrid :: Grid C (Maybe Char)
+ Data.Grid: multiOutsideClues :: (Ord k, FromCoord k) => OutsideClues k [a] -> Map k a
+ Data.Grid: nodeGrid :: Grid C a -> Grid N ()
+ Data.Grid: nodes :: Grid N a -> Set N
+ Data.Grid: outsideClues :: (Ord k, FromCoord k) => OutsideClues k a -> Map k a
+ Data.Grid: outsideGrid :: (Ord k, FromCoord k) => OutsideClues k a -> Grid k ()
+ Data.Grid: outsideSize :: OutsideClues k a -> Size
+ Data.Grid: outsideValues :: OutsideClues k a -> [a]
+ Data.Grid: pentominoGrid :: Grid C (Maybe Char)
+ Data.Grid: rows :: Map C a -> [[a]]
+ Data.Grid: size :: Grid Coord a -> Size
+ Data.Grid: sizeGrid :: (Ord k, FromCoord k) => Size -> Grid k ()
+ Data.Grid: skeletons :: Eq a => Grid C a -> [Edge C]
+ Data.Grid: type AreaGrid = Grid C Char
+ Data.Grid: type Grid k a = Map k a
+ Data.Grid: type ShadedGrid = Grid C Bool
+ Data.GridShape: C :: !Int -> !Int -> C
+ Data.GridShape: D :: Dir'
+ Data.GridShape: E :: a -> Dir -> Edge a
+ Data.GridShape: E' :: a -> Dir' -> Edge' a
+ Data.GridShape: Horiz :: Dir
+ Data.GridShape: L :: Dir'
+ Data.GridShape: N :: !Int -> !Int -> N
+ Data.GridShape: R :: Dir'
+ Data.GridShape: Square :: Square
+ Data.GridShape: U :: Dir'
+ Data.GridShape: Vert :: Dir
+ Data.GridShape: class Dual2D a where {
+ Data.GridShape: class FromCoord a
+ Data.GridShape: class ToCoord a
+ Data.GridShape: data C
+ Data.GridShape: data Dir
+ Data.GridShape: data Dir'
+ Data.GridShape: data Edge a
+ Data.GridShape: data Edge' a
+ Data.GridShape: data N
+ Data.GridShape: data Square
+ Data.GridShape: dualE :: Dual' a => Edge a -> Edge (Dual a)
+ Data.GridShape: dualE' :: Dual2D a => Edge' a -> Edge' (Dual a)
+ Data.GridShape: edge :: (AffineSpace a, Diff a ~ (Int, Int)) => a -> a -> Edge a
+ Data.GridShape: edge' :: (AffineSpace a, Diff a ~ (Int, Int)) => a -> a -> Edge' a
+ Data.GridShape: edgeBetween :: Dual' k => k -> k -> Edge (Dual k)
+ Data.GridShape: edgeBetween' :: Dual' k => k -> k -> Edge' (Dual k)
+ Data.GridShape: edgeNeighbours :: C -> [C]
+ Data.GridShape: edgeSize :: [Edge N] -> Size
+ Data.GridShape: edges :: (Dual' k, Foldable f) => f k -> (k -> Bool) -> ([Edge' (Dual k)], [Edge (Dual k)])
+ Data.GridShape: edgesM :: Dual' k => Map k a -> ([Edge' (Dual k)], [Edge (Dual k)])
+ Data.GridShape: ends :: (AffineSpace a, Diff a ~ (Int, Int)) => Edge a -> (a, a)
+ Data.GridShape: ends' :: (AffineSpace a, Diff a ~ (Int, Int)) => Edge' a -> (a, a)
+ Data.GridShape: fromCoord :: FromCoord a => Coord -> a
+ Data.GridShape: instance Data.AffineSpace.AffineSpace Data.GridShape.C
+ Data.GridShape: instance Data.AffineSpace.AffineSpace Data.GridShape.N
+ Data.GridShape: instance Data.GridShape.Dual2D Data.GridShape.C
+ Data.GridShape: instance Data.GridShape.Dual2D Data.GridShape.N
+ Data.GridShape: instance Data.GridShape.FromCoord Data.GridShape.C
+ Data.GridShape: instance Data.GridShape.FromCoord Data.GridShape.N
+ Data.GridShape: instance Data.GridShape.ToCoord Data.GridShape.C
+ Data.GridShape: instance Data.GridShape.ToCoord Data.GridShape.N
+ Data.GridShape: instance GHC.Classes.Eq Data.GridShape.C
+ Data.GridShape: instance GHC.Classes.Eq Data.GridShape.Dir
+ Data.GridShape: instance GHC.Classes.Eq Data.GridShape.Dir'
+ Data.GridShape: instance GHC.Classes.Eq Data.GridShape.N
+ Data.GridShape: instance GHC.Classes.Eq Data.GridShape.Square
+ Data.GridShape: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.GridShape.Edge a)
+ Data.GridShape: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.GridShape.Edge' a)
+ Data.GridShape: instance GHC.Classes.Ord Data.GridShape.C
+ Data.GridShape: instance GHC.Classes.Ord Data.GridShape.Dir
+ Data.GridShape: instance GHC.Classes.Ord Data.GridShape.Dir'
+ Data.GridShape: instance GHC.Classes.Ord Data.GridShape.N
+ Data.GridShape: instance GHC.Classes.Ord a => GHC.Classes.Ord (Data.GridShape.Edge a)
+ Data.GridShape: instance GHC.Classes.Ord a => GHC.Classes.Ord (Data.GridShape.Edge' a)
+ Data.GridShape: instance GHC.Show.Show Data.GridShape.C
+ Data.GridShape: instance GHC.Show.Show Data.GridShape.Dir
+ Data.GridShape: instance GHC.Show.Show Data.GridShape.Dir'
+ Data.GridShape: instance GHC.Show.Show Data.GridShape.N
+ Data.GridShape: instance GHC.Show.Show Data.GridShape.Square
+ Data.GridShape: instance GHC.Show.Show a => GHC.Show.Show (Data.GridShape.Edge a)
+ Data.GridShape: instance GHC.Show.Show a => GHC.Show.Show (Data.GridShape.Edge' a)
+ Data.GridShape: orient :: Edge a -> Edge' a
+ Data.GridShape: revEdge :: (AffineSpace a, Diff a ~ (Int, Int)) => Edge' a -> Edge' a
+ Data.GridShape: rows :: Map C a -> [[a]]
+ Data.GridShape: toCoord :: ToCoord a => a -> Coord
+ Data.GridShape: type Coord = (Int, Int)
+ Data.GridShape: type Dual' k = (Key k, Dual2D k, Key (Dual k), Dual2D (Dual k), Dual (Dual k) ~ k)
+ Data.GridShape: type Key k = (AffineSpace k, Diff k ~ (Int, Int), Ord k, FromCoord k)
+ Data.GridShape: type Size = (Int, Int)
+ Data.GridShape: type family Dual a :: *;
+ Data.GridShape: unorient :: (AffineSpace a, Diff a ~ (Int, Int)) => Edge' a -> Edge a
+ Data.GridShape: vertexNeighbours :: C -> [C]
+ Data.GridShape: }
+ Data.PuzzleTypes: ABCtje :: PuzzleType
+ Data.PuzzleTypes: AfternoonSkyscrapers :: PuzzleType
+ Data.PuzzleTypes: AngleLoop :: PuzzleType
+ Data.PuzzleTypes: Anglers :: PuzzleType
+ Data.PuzzleTypes: Baca :: PuzzleType
+ Data.PuzzleTypes: Bahnhof :: PuzzleType
+ Data.PuzzleTypes: BlackoutDominos :: PuzzleType
+ Data.PuzzleTypes: BoxOf2Or3 :: PuzzleType
+ Data.PuzzleTypes: Buchstabensalat :: PuzzleType
+ Data.PuzzleTypes: Cave :: PuzzleType
+ Data.PuzzleTypes: ColorAkari :: PuzzleType
+ Data.PuzzleTypes: Compass :: PuzzleType
+ Data.PuzzleTypes: Coral :: PuzzleType
+ Data.PuzzleTypes: CoralLITS :: PuzzleType
+ Data.PuzzleTypes: CoralLITSO :: PuzzleType
+ Data.PuzzleTypes: CountryRoad :: PuzzleType
+ Data.PuzzleTypes: CurveData :: PuzzleType
+ Data.PuzzleTypes: DominoPillen :: PuzzleType
+ Data.PuzzleTypes: Dominos :: PuzzleType
+ Data.PuzzleTypes: Doppelblock :: PuzzleType
+ Data.PuzzleTypes: DoubleBack :: PuzzleType
+ Data.PuzzleTypes: Fillomino :: PuzzleType
+ Data.PuzzleTypes: FillominoCheckered :: PuzzleType
+ Data.PuzzleTypes: FillominoLoop :: PuzzleType
+ Data.PuzzleTypes: FriendlySudoku :: PuzzleType
+ Data.PuzzleTypes: Galaxies :: PuzzleType
+ Data.PuzzleTypes: Geradeweg :: PuzzleType
+ Data.PuzzleTypes: GreaterWall :: PuzzleType
+ Data.PuzzleTypes: Heyawake :: PuzzleType
+ Data.PuzzleTypes: HorseSnake :: PuzzleType
+ Data.PuzzleTypes: Illumination :: PuzzleType
+ Data.PuzzleTypes: JapaneseSums :: PuzzleType
+ Data.PuzzleTypes: JapaneseSumsMasyu :: PuzzleType
+ Data.PuzzleTypes: KillerSudoku :: PuzzleType
+ Data.PuzzleTypes: Kropki :: PuzzleType
+ Data.PuzzleTypes: LITS :: PuzzleType
+ Data.PuzzleTypes: LITSPlus :: PuzzleType
+ Data.PuzzleTypes: LITSSym :: PuzzleType
+ Data.PuzzleTypes: Labyrinth :: PuzzleType
+ Data.PuzzleTypes: LatinTapa :: PuzzleType
+ Data.PuzzleTypes: Loopki :: PuzzleType
+ Data.PuzzleTypes: Masyu :: PuzzleType
+ Data.PuzzleTypes: MaximalLengths :: PuzzleType
+ Data.PuzzleTypes: MeanderingNumbers :: PuzzleType
+ Data.PuzzleTypes: Mines :: PuzzleType
+ Data.PuzzleTypes: NanroSignpost :: PuzzleType
+ Data.PuzzleTypes: Neighbors :: PuzzleType
+ Data.PuzzleTypes: Numberlink :: PuzzleType
+ Data.PuzzleTypes: Nurikabe :: PuzzleType
+ Data.PuzzleTypes: PentominoPipes :: PuzzleType
+ Data.PuzzleTypes: PentominoSums :: PuzzleType
+ Data.PuzzleTypes: Pentominous :: PuzzleType
+ Data.PuzzleTypes: PentominousBorders :: PuzzleType
+ Data.PuzzleTypes: Pentopia :: PuzzleType
+ Data.PuzzleTypes: PersistenceOfMemory :: PuzzleType
+ Data.PuzzleTypes: PrimePlace :: PuzzleType
+ Data.PuzzleTypes: Pyramid :: PuzzleType
+ Data.PuzzleTypes: RowKropkiPyramid :: PuzzleType
+ Data.PuzzleTypes: Scrabble :: PuzzleType
+ Data.PuzzleTypes: Shikaku :: PuzzleType
+ Data.PuzzleTypes: Skyscrapers :: PuzzleType
+ Data.PuzzleTypes: SkyscrapersFrac :: PuzzleType
+ Data.PuzzleTypes: SkyscrapersStars :: PuzzleType
+ Data.PuzzleTypes: Slalom :: PuzzleType
+ Data.PuzzleTypes: SlitherLink :: PuzzleType
+ Data.PuzzleTypes: SlitherLinkLiar :: PuzzleType
+ Data.PuzzleTypes: SlitherLinkMulti :: PuzzleType
+ Data.PuzzleTypes: SlovakSums :: PuzzleType
+ Data.PuzzleTypes: Snake :: PuzzleType
+ Data.PuzzleTypes: Starbattle :: PuzzleType
+ Data.PuzzleTypes: Starwars :: PuzzleType
+ Data.PuzzleTypes: StatuePark :: PuzzleType
+ Data.PuzzleTypes: Sudoku :: PuzzleType
+ Data.PuzzleTypes: SudokuDoppelblock :: PuzzleType
+ Data.PuzzleTypes: Summon :: PuzzleType
+ Data.PuzzleTypes: Tapa :: PuzzleType
+ Data.PuzzleTypes: TapaCave :: PuzzleType
+ Data.PuzzleTypes: Tents :: PuzzleType
+ Data.PuzzleTypes: ThermoSudoku :: PuzzleType
+ Data.PuzzleTypes: TightfitSkyscrapers :: PuzzleType
+ Data.PuzzleTypes: TomTom :: PuzzleType
+ Data.PuzzleTypes: TurningFences :: PuzzleType
+ Data.PuzzleTypes: TwilightTapa :: PuzzleType
+ Data.PuzzleTypes: WordLoop :: PuzzleType
+ Data.PuzzleTypes: WordSearch :: PuzzleType
+ Data.PuzzleTypes: Wormhole :: PuzzleType
+ Data.PuzzleTypes: data PuzzleType
+ Data.PuzzleTypes: instance GHC.Classes.Eq Data.PuzzleTypes.PuzzleType
+ Data.PuzzleTypes: instance GHC.Show.Show Data.PuzzleTypes.PuzzleType
+ Data.PuzzleTypes: lookupType :: String -> Maybe PuzzleType
+ Data.PuzzleTypes: typeNames :: [(PuzzleType, String)]
+ Data.Pyramid: KP :: [KropkiRow] -> RowKropkiPyramid
+ Data.Pyramid: KR :: [Maybe Int] -> Bool -> [KropkiDot] -> KropkiRow
+ Data.Pyramid: Pyr :: [Row] -> Pyramid
+ Data.Pyramid: PyramidSol :: [[Int]] -> PyramidSol
+ Data.Pyramid: R :: [Maybe Int] -> Bool -> Row
+ Data.Pyramid: [dotsk] :: KropkiRow -> [KropkiDot]
+ Data.Pyramid: [entries] :: Row -> [Maybe Int]
+ Data.Pyramid: [entriesk] :: KropkiRow -> [Maybe Int]
+ Data.Pyramid: [shaded] :: Row -> Bool
+ Data.Pyramid: [shadedk] :: KropkiRow -> Bool
+ Data.Pyramid: [unKP] :: RowKropkiPyramid -> [KropkiRow]
+ Data.Pyramid: [unPyr] :: Pyramid -> [Row]
+ Data.Pyramid: data KropkiRow
+ Data.Pyramid: data Row
+ Data.Pyramid: instance Data.Aeson.Types.FromJSON.FromJSON Data.Pyramid.Pyramid
+ Data.Pyramid: instance Data.Aeson.Types.FromJSON.FromJSON Data.Pyramid.PyramidSol
+ Data.Pyramid: instance Data.Aeson.Types.FromJSON.FromJSON Data.Pyramid.RowKropkiPyramid
+ Data.Pyramid: instance GHC.Show.Show Data.Pyramid.KropkiRow
+ Data.Pyramid: instance GHC.Show.Show Data.Pyramid.Pyramid
+ Data.Pyramid: instance GHC.Show.Show Data.Pyramid.PyramidSol
+ Data.Pyramid: instance GHC.Show.Show Data.Pyramid.Row
+ Data.Pyramid: instance GHC.Show.Show Data.Pyramid.RowKropkiPyramid
+ Data.Pyramid: mergekpyramidsol :: RowKropkiPyramid -> PyramidSol -> RowKropkiPyramid
+ Data.Pyramid: mergepyramidsol :: Pyramid -> PyramidSol -> Pyramid
+ Data.Pyramid: newtype Pyramid
+ Data.Pyramid: newtype PyramidSol
+ Data.Pyramid: newtype RowKropkiPyramid
+ Data.Pyramid: plainpyramid :: RowKropkiPyramid -> Pyramid
+ Data.Pyramid: psize :: Pyramid -> Int
+ Data.Sudoku: sudokuborders :: Int -> [Edge N]
+ Data.Sudoku: sudokubordersg :: Grid C a -> [Edge N]
+ Data.Util: loops :: Eq a => [(a, a)] -> Maybe [[a]]
+ Data.Util: paths :: Eq a => [(a, a)] -> Maybe [[a]]
+ Draw.CmdLine: RenderOpts :: FilePath -> SizeSpec V2 Double -> RenderOpts
+ Draw.CmdLine: [_file] :: RenderOpts -> FilePath
+ Draw.CmdLine: [_size] :: RenderOpts -> SizeSpec V2 Double
+ Draw.CmdLine: checkFormat :: String -> IO ()
+ Draw.CmdLine: data RenderOpts
+ Draw.CmdLine: formats :: [String]
+ Draw.CmdLine: renderToFile :: RenderOpts -> Diagram B -> IO ()
+ Draw.CmdLine: type B = Rasterific
+ Draw.Code: CodeDiagrams :: a -> a -> a -> CodeDiagrams a
+ Draw.Code: [_cdLeft] :: CodeDiagrams a -> a
+ Draw.Code: [_cdOver] :: CodeDiagrams a -> a
+ Draw.Code: [_cdTop] :: CodeDiagrams a -> a
+ Draw.Code: data CodeDiagrams a
+ Draw.Code: drawCode :: Backend' b => Code -> CodeDiagrams (Diagram b)
+ Draw.Code: instance GHC.Base.Monoid a => GHC.Base.Monoid (Draw.Code.CodeDiagrams a)
+ Draw.Code: instance GHC.Base.Semigroup a => GHC.Base.Semigroup (Draw.Code.CodeDiagrams a)
+ Draw.Draw: DrawExample :: OutputChoice
+ Draw.Draw: DrawPuzzle :: OutputChoice
+ Draw.Draw: DrawSolution :: OutputChoice
+ Draw.Draw: Drawers :: p -> Config -> Diagram b -> (p, s) -> Config -> Diagram b -> Drawers b p s
+ Draw.Draw: Pixels :: Unit
+ Draw.Draw: Points :: Unit
+ Draw.Draw: [puzzle] :: Drawers b p s -> p -> Config -> Diagram b
+ Draw.Draw: [solution] :: Drawers b p s -> (p, s) -> Config -> Diagram b
+ Draw.Draw: data Drawers b p s
+ Draw.Draw: data OutputChoice
+ Draw.Draw: data Unit
+ Draw.Draw: diagramSize :: Backend' b => Diagram b -> (Double, Double)
+ Draw.Draw: drawers :: (p -> Diagram b) -> ((p, s) -> Diagram b) -> Drawers b p s
+ Draw.Draw: instance GHC.Show.Show Draw.Draw.OutputChoice
+ Draw.Draw: render :: Backend' b => Maybe (CodeDiagrams (Diagram b)) -> PuzzleSol b -> OutputChoice -> Maybe (Diagram b)
+ Draw.Draw: toOutputWidth :: Unit -> Double -> Double
+ Draw.Draw: type PuzzleSol b = (Diagram b, Maybe (Diagram b))
+ Draw.Elements: DominoC :: C -> DominoC
+ Draw.Elements: cross :: Path V2 Double
+ Draw.Elements: dominoBG :: Colour Double
+ Draw.Elements: dr :: Path V2 Double
+ Draw.Elements: drawAnglePoly :: Backend' b => Int -> Diagram b
+ Draw.Elements: drawBahnhofClue :: Backend' b => BahnhofClue -> Diagram b
+ Draw.Elements: drawBigEnd :: Backend' b => MEnd -> Diagram b
+ Draw.Elements: drawCages :: (Backend' b, Eq a, Ord a) => Grid C a -> Map a (Diagram b) -> Diagram b
+ Draw.Elements: drawChar :: Backend' b => Char -> Diagram b
+ Draw.Elements: drawCharFixed :: Backend' b => Char -> Diagram b
+ Draw.Elements: drawCharOpaque :: Backend' b => Char -> Diagram b
+ Draw.Elements: drawCompassClue :: Backend' b => CompassC -> Diagram b
+ Draw.Elements: drawCross :: Backend' b => Bool -> Diagram b
+ Draw.Elements: drawCrossing :: Backend' b => Crossing -> Diagram b
+ Draw.Elements: drawCurve :: Backend' b => [Edge N] -> Diagram b
+ Draw.Elements: drawDomino :: Backend' b => (Int, Int) -> Diagram b
+ Draw.Elements: drawDominos :: Backend' b => DigitRange -> Diagram b
+ Draw.Elements: drawEnd :: Backend' b => MEnd -> Diagram b
+ Draw.Elements: drawFish :: Backend' b => Fish -> Diagram b
+ Draw.Elements: drawFraction :: Backend' b => Fraction -> Diagram b
+ Draw.Elements: drawInt :: Backend' b => Int -> Diagram b
+ Draw.Elements: drawLITS :: Backend' b => Diagram b
+ Draw.Elements: drawLITSO :: Backend' b => Diagram b
+ Draw.Elements: drawMarkedLine :: (ToPoint a, Backend' b) => MarkedLine a -> Diagram b
+ Draw.Elements: drawMarkedLines :: (ToPoint a, Backend' b) => [MarkedLine a] -> Diagram b
+ Draw.Elements: drawMarkedWord :: Backend' b => MarkedWord -> Diagram b
+ Draw.Elements: drawMarkedWords :: Backend' b => [MarkedWord] -> Diagram b
+ Draw.Elements: drawMyopia :: Backend' b => Myopia -> Diagram b
+ Draw.Elements: drawPentominos :: Backend' b => Diagram b
+ Draw.Elements: drawPill :: Backend' b => Int -> Diagram b
+ Draw.Elements: drawPills :: Backend' b => DigitRange -> Diagram b
+ Draw.Elements: drawPrimeDiag :: Backend' b => PrimeDiag -> Diagram b
+ Draw.Elements: drawShadow :: Backend' b => Shade -> Diagram b
+ Draw.Elements: drawSlalomClue :: (Show a, Backend' b) => a -> Diagram b
+ Draw.Elements: drawSlalomDiag :: Backend' b => SlalomDiag -> Diagram b
+ Draw.Elements: drawSlovakClue :: Backend' b => SlovakClue -> Diagram b
+ Draw.Elements: drawStar :: Backend' b => Star -> Diagram b
+ Draw.Elements: drawTapaClue :: Backend' b => TapaClue -> Diagram b
+ Draw.Elements: drawTent :: Backend' b => PlacedTent -> Diagram b
+ Draw.Elements: drawText :: Backend' b => String -> Diagram b
+ Draw.Elements: drawTextFixed :: Backend' b => String -> Diagram b
+ Draw.Elements: drawThermos :: Backend' b => [Thermometer] -> Diagram b
+ Draw.Elements: drawTight :: Backend' b => (a -> Diagram b) -> Tightfit a -> Diagram b
+ Draw.Elements: drawTree :: Backend' b => Tree -> Diagram b
+ Draw.Elements: drawWords :: Backend' b => [String] -> Diagram b
+ Draw.Elements: fish :: Double -> Angle Double -> Trail' Loop V2 Double
+ Draw.Elements: greaterClue :: Backend' b => GreaterClue -> [Diagram b]
+ Draw.Elements: hintTL :: Backend' b => String -> Diagram b
+ Draw.Elements: instance Draw.Grid.ToPoint Draw.Elements.DominoC
+ Draw.Elements: instance GHC.Classes.Eq Draw.Elements.DominoC
+ Draw.Elements: instance GHC.Classes.Ord Draw.Elements.DominoC
+ Draw.Elements: invertMap :: (Eq a, Ord a) => Map k a -> Map a [k]
+ Draw.Elements: kropkiDot :: Backend' b => KropkiDot -> Diagram b
+ Draw.Elements: miniloop :: Backend' b => Diagram b
+ Draw.Elements: newtype DominoC
+ Draw.Elements: note :: Backend' b => Diagram b -> Diagram b
+ Draw.Elements: pearl :: Backend' b => MasyuPearl -> Diagram b
+ Draw.Elements: placeNoteBR :: Backend' b => Size -> Diagram b -> Diagram b
+ Draw.Elements: placeNoteTL :: Backend' b => Size -> Diagram b -> Diagram b
+ Draw.Elements: placeNoteTR :: Backend' b => Size -> Diagram b -> Diagram b
+ Draw.Elements: placeTL :: Backend' b => Diagram b -> Diagram b
+ Draw.Elements: polyominoGrid :: Backend' b => Grid C (Maybe Char) -> Diagram b
+ Draw.Elements: smallPearl :: Backend' b => MasyuPearl -> Diagram b
+ Draw.Elements: stackWords :: Backend' b => [String] -> Diagram b
+ Draw.Elements: stackWordsLeft :: Backend' b => [String] -> Diagram b
+ Draw.Elements: stackWordsRight :: Backend' b => [String] -> Diagram b
+ Draw.Elements: thermo :: Backend' b => [P2 Double] -> Diagram b
+ Draw.Elements: ur :: Path V2 Double
+ Draw.Elements: vertexLoop :: VertexLoop -> Located (Trail' Loop V2 Double)
+ Draw.Grid: (.--.) :: AffineSpace p => p -> p -> Diff p
+ Draw.Grid: bgdashingG :: (Semigroup a, HasStyle a, InSpace V2 Double a) => [Double] -> Double -> AlphaColour Double -> a -> a
+ Draw.Grid: cage :: Backend' b => [C] -> Diagram b
+ Draw.Grid: class ToPoint a
+ Draw.Grid: dashes :: [Double]
+ Draw.Grid: dashoffset :: Double
+ Draw.Grid: distrib :: (Transformable c, Monoid c, InSpace V2 Double c) => V2 Double -> (Int, Int) -> Double -> [c] -> c
+ Draw.Grid: dot :: Backend' b => Diagram b
+ Draw.Grid: drawAreas :: (Backend' b, Eq a) => Grid C a -> Diagram b
+ Draw.Grid: drawAreasGray :: Backend' b => Grid C Char -> Diagram b
+ Draw.Grid: drawEdges :: (ToPoint k, Backend' b) => [Edge k] -> Diagram b
+ Draw.Grid: drawShade :: Backend' b => Grid C Bool -> Diagram b
+ Draw.Grid: drawThinEdges :: (ToPoint k, Backend' b) => [Edge k] -> Diagram b
+ Draw.Grid: edge :: (ToPoint k) => Edge k -> Path V2 Double
+ Draw.Grid: edgeStyle :: (HasStyle a, InSpace V2 Double a) => a -> a
+ Draw.Grid: fillBG :: Backend' b => Colour Double -> Diagram b
+ Draw.Grid: grid :: Backend' b => GridStyle -> Grid C a -> Diagram b
+ Draw.Grid: gridDashing :: (Semigroup a, HasStyle a, InSpace V2 Double a) => a -> a
+ Draw.Grid: instance Draw.Grid.ToPoint Data.GridShape.C
+ Draw.Grid: instance Draw.Grid.ToPoint Data.GridShape.N
+ Draw.Grid: irregularGridPaths :: Grid C a -> (Path V2 Double, Path V2 Double)
+ Draw.Grid: midPoint :: (AffineSpace k, Diff k ~ (Int, Int), ToPoint k) => Edge k -> P2 Double
+ Draw.Grid: offsetBorder :: Double -> [C] -> Path V2 Double
+ Draw.Grid: onGrid :: (Transformable a, Monoid a, InSpace V2 Double a) => Double -> Double -> (t -> a) -> [(Coord, t)] -> a
+ Draw.Grid: outLine :: Backend' b => Double -> Path V2 Double -> Diagram b
+ Draw.Grid: placeGrid :: (ToPoint k, HasOrigin a, Monoid a, InSpace V2 Double a) => Grid k a -> a
+ Draw.Grid: placeGrid' :: (HasOrigin a, Monoid a, InSpace V2 Double a) => Grid (P2 Double) a -> a
+ Draw.Grid: shadeGrid :: Backend' b => Grid C (Maybe (Colour Double)) -> Diagram b
+ Draw.Grid: thinEdgeStyle :: (HasStyle a, InSpace V2 Double a) => a -> a
+ Draw.Grid: toPoint :: ToPoint a => a -> P2 Double
+ Draw.Lib: aboveT :: Backend' b => Diagram b -> Diagram b -> Diagram b
+ Draw.Lib: besidesL :: Backend' b => Diagram b -> Diagram b -> Diagram b
+ Draw.Lib: besidesR :: Backend' b => Diagram b -> Diagram b -> Diagram b
+ Draw.Lib: debugPath :: Backend' b => Path V2 Double -> Diagram b
+ Draw.Lib: debugPath' :: Backend' b => Path V2 Double -> Diagram b
+ Draw.Lib: dmid :: (InSpace V2 Double a, Enveloped a) => V2 Double -> a -> Double
+ Draw.Lib: fit :: (Transformable t, Enveloped t, InSpace V2 Double t) => Double -> t -> t
+ Draw.Lib: fontBit :: Font
+ Draw.Lib: fontGenLight :: Font
+ Draw.Lib: hcatsep :: (InSpace V2 Double a, Juxtaposable a, HasOrigin a, Monoid' a) => [a] -> a
+ Draw.Lib: hline :: Backend' b => Double -> Diagram b
+ Draw.Lib: interleave :: [a] -> [a] -> [a]
+ Draw.Lib: magnitude :: V2 Double -> Double
+ Draw.Lib: mirror :: (InSpace V2 Double t, Transformable t) => t -> t
+ Draw.Lib: p2i :: (Int, Int) -> P2 Double
+ Draw.Lib: phantom' :: Backend' b => Diagram b -> Diagram b
+ Draw.Lib: r2i :: (Int, Int) -> V2 Double
+ Draw.Lib: smash :: Backend' b => Diagram b -> Diagram b
+ Draw.Lib: spread :: Backend' b => V2 Double -> [Diagram b] -> Diagram b
+ Draw.Lib: text' :: Backend' b => String -> Diagram b
+ Draw.Lib: text'' :: Backend' b => Font -> String -> Diagram b
+ Draw.Lib: textFixed :: Backend' b => String -> Diagram b
+ Draw.Lib: translatep :: (InSpace V2 Double t, Transformable t) => (Int, Int) -> t -> t
+ Draw.Lib: type Backend' b = (V b ~ V2, N b ~ Double, Renderable (Path V2 Double) b, Backend b V2 Double)
+ Draw.Lib: type Font = PreparedFont Double
+ Draw.Lib: vcatsep :: (InSpace V2 Double a, Juxtaposable a, HasOrigin a, Monoid' a) => [a] -> a
+ Draw.Lib: vline :: Backend' b => Double -> Diagram b
+ Draw.PuzzleGrids: drawCharGrid :: Backend' b => Grid C (Maybe Char) -> Diagram b
+ Draw.PuzzleGrids: drawIntGrid :: Backend' b => Grid C (Maybe Int) -> Diagram b
+ Draw.PuzzleGrids: drawMultiOutsideGrid :: Backend' b => OutsideClues C [String] -> Diagram b
+ Draw.PuzzleGrids: drawMultiOutsideGridN :: Backend' b => OutsideClues N [String] -> Diagram b
+ Draw.PuzzleGrids: drawOutsideGrid :: Backend' b => OutsideClues C (Maybe String) -> Diagram b
+ Draw.PuzzleGrids: drawOutsideGridN :: Backend' b => OutsideClues N (Maybe String) -> Diagram b
+ Draw.PuzzleGrids: drawSlitherGrid :: Backend' b => Grid C (Maybe Int) -> Diagram b
+ Draw.PuzzleGrids: drawTightGrid :: Backend' b => (t -> Diagram b) -> Grid C (Tightfit t) -> Diagram b
+ Draw.PuzzleGrids: drawWordsClues :: Backend' b => Grid C (Maybe [String]) -> Diagram b
+ Draw.PuzzleGrids: outsideIntGrid :: Backend' b => OutsideClues C [Int] -> Diagram b
+ Draw.PuzzleGrids: placeMultiOutside :: (Ord k, FromCoord k, ToPoint k, HasOrigin a, Monoid a, InSpace V2 Double a) => OutsideClues k [a] -> a
+ Draw.PuzzleGrids: sudokugrid :: Backend' b => Grid C a -> Diagram b
+ Draw.PuzzleTypes: abctje :: Backend' b => Drawers b (DigitRange, [(String, Int)]) [(Int, Char)]
+ Draw.PuzzleTypes: afternoonskyscrapers :: Backend' b => Drawers b (Grid C Shade) (Grid C (Maybe Int))
+ Draw.PuzzleTypes: angleLoop :: Backend' b => Drawers b (Grid N (Clue Int)) VertexLoop
+ Draw.PuzzleTypes: anglers :: Backend' b => Drawers b (OutsideClues C (Clue Int), Grid C (Maybe Fish)) [Edge C]
+ Draw.PuzzleTypes: baca :: Backend' b => Drawers b (Grid C (Maybe Char), OutsideClues C [Int], OutsideClues C (Maybe Char)) (Grid C (Either Black Char))
+ Draw.PuzzleTypes: bahnhof :: Backend' b => Drawers b (Grid C (Maybe BahnhofClue)) [Edge C]
+ Draw.PuzzleTypes: blackoutDominos :: Backend' b => Drawers b (Grid C (Clue Int), DigitRange) (Grid C (Clue Int), AreaGrid)
+ Draw.PuzzleTypes: boxof2or3 :: Backend' b => Drawers b (Grid N MasyuPearl, [Edge N]) ()
+ Draw.PuzzleTypes: buchstabensalat :: Backend' b => Drawers b (OutsideClues C (Maybe Char), String) (Grid C (Maybe Char))
+ Draw.PuzzleTypes: cave :: Backend' b => Drawers b (Grid C (Maybe Int)) ShadedGrid
+ Draw.PuzzleTypes: colorakari :: Backend' b => Drawers b (Grid C (Maybe Char)) (Grid C (Maybe Char))
+ Draw.PuzzleTypes: compass :: Backend' b => Drawers b (Grid C (Maybe CompassC)) AreaGrid
+ Draw.PuzzleTypes: coral :: Backend' b => Drawers b (OutsideClues C [String]) ShadedGrid
+ Draw.PuzzleTypes: coralLits :: Backend' b => Drawers b (OutsideClues C [String]) (Grid C (Maybe Char))
+ Draw.PuzzleTypes: coralLitso :: Backend' b => Drawers b (OutsideClues C [String]) (Grid C (Either Black Char))
+ Draw.PuzzleTypes: countryRoad :: Backend' b => Drawers b (AreaGrid, Grid C (Maybe Int)) (Loop C)
+ Draw.PuzzleTypes: curvedata :: Backend' b => Drawers b (Grid C (Maybe [Edge N])) [Edge C]
+ Draw.PuzzleTypes: dominoPills :: Backend' b => Drawers b (Grid C (Clue Int), DigitRange, DigitRange) AreaGrid
+ Draw.PuzzleTypes: dominos :: Backend' b => Drawers b (Grid C (Clue Int), DigitRange) AreaGrid
+ Draw.PuzzleTypes: doppelblock :: Backend' b => Drawers b (OutsideClues C (Maybe Int)) (Grid C (Either Black Int))
+ Draw.PuzzleTypes: doubleback :: Backend' b => Drawers b AreaGrid (Loop C)
+ Draw.PuzzleTypes: fillomino :: Backend' b => Drawers b (Grid C (Maybe Int)) (Grid C Int)
+ Draw.PuzzleTypes: fillominoCheckered :: Backend' b => Drawers b (Grid C (Maybe Int)) (Grid C Int)
+ Draw.PuzzleTypes: fillominoLoop :: Backend' b => Drawers b (Grid C (Maybe Int)) (Grid C Int, Loop C)
+ Draw.PuzzleTypes: friendlysudoku :: Backend' b => Drawers b (Map (Edge N) KropkiDot, Grid C (Maybe Int)) (Grid C Int)
+ Draw.PuzzleTypes: galaxies :: Backend' b => Drawers b (Grid C (), Grid N (), Grid C (), Map (Edge N) ()) AreaGrid
+ Draw.PuzzleTypes: geradeweg :: Backend' b => Drawers b (Grid C (Maybe Int)) (Loop C)
+ Draw.PuzzleTypes: greaterWall :: Backend' b => Drawers b ([GreaterClue], [GreaterClue]) (Grid C Bool)
+ Draw.PuzzleTypes: heyawake :: Backend' b => Drawers b (AreaGrid, Grid C (Maybe Int)) (Grid C Bool)
+ Draw.PuzzleTypes: horseSnake :: Backend' b => Drawers b (Grid C (Maybe (Either MEnd Int))) [Edge C]
+ Draw.PuzzleTypes: illumination :: Backend' b => Drawers b (OutsideClues C (Maybe Fraction)) (Grid N (Maybe PlainNode), [Edge N])
+ Draw.PuzzleTypes: japanesesums :: Backend' b => Drawers b (OutsideClues C [Int], String) (Grid C (Either Black Int))
+ Draw.PuzzleTypes: japsummasyu :: Backend' b => Drawers b (OutsideClues C [String]) ()
+ Draw.PuzzleTypes: killersudoku :: Backend' b => Drawers b (AreaGrid, Map Char Int, Grid C (Maybe Int)) (Grid C Int)
+ Draw.PuzzleTypes: kpyramid :: Backend' b => Drawers b RowKropkiPyramid PyramidSol
+ Draw.PuzzleTypes: kropki :: Backend' b => Drawers b (Map (Edge N) KropkiDot) (Grid C Int)
+ Draw.PuzzleTypes: labyrinth :: Backend' b => Drawers b (Grid C (Maybe Int), [Edge N], String) (Grid C (Maybe Int))
+ Draw.PuzzleTypes: latintapa :: Backend' b => Drawers b (Grid C (Maybe [String])) (Grid C (Maybe Char))
+ Draw.PuzzleTypes: liarslither :: Backend' b => Drawers b (Grid C (Maybe Int)) (Loop N, Grid C Bool)
+ Draw.PuzzleTypes: lits :: Backend' b => Drawers b AreaGrid ShadedGrid
+ Draw.PuzzleTypes: litsplus :: Backend' b => Drawers b AreaGrid ShadedGrid
+ Draw.PuzzleTypes: litssym :: Backend' b => Drawers b AreaGrid ShadedGrid
+ Draw.PuzzleTypes: loopki :: Backend' b => Drawers b (Grid C (Maybe MasyuPearl)) (Loop N)
+ Draw.PuzzleTypes: masyu :: Backend' b => Drawers b (Grid C (Maybe MasyuPearl)) (Loop C)
+ Draw.PuzzleTypes: maximallengths :: Backend' b => Drawers b (OutsideClues C (Maybe Int)) (Loop C)
+ Draw.PuzzleTypes: meanderingnumbers :: Backend' b => Drawers b AreaGrid (Grid C (Maybe Int))
+ Draw.PuzzleTypes: mines :: Backend' b => Drawers b (Grid C (Maybe Int)) ShadedGrid
+ Draw.PuzzleTypes: nanroSignpost :: Backend' b => Drawers b (AreaGrid, Grid C (Maybe Int)) (Grid C Int)
+ Draw.PuzzleTypes: neighbors :: Backend' b => Drawers b (Grid C Bool, Grid C (Maybe Int)) (Grid C Int)
+ Draw.PuzzleTypes: numberlink :: Backend' b => Drawers b (Grid C (Maybe Int)) [Edge C]
+ Draw.PuzzleTypes: nurikabe :: Backend' b => Drawers b (Grid C (Maybe Int)) ShadedGrid
+ Draw.PuzzleTypes: pentominoPipes :: Backend' b => Drawers b (Grid N Char) (Grid N KropkiDot, [Edge N])
+ Draw.PuzzleTypes: pentominoSums :: Backend' b => Drawers b (OutsideClues C [String], String) (Grid C (Either Pentomino Int), [(Char, Int)], OutsideClues C [String])
+ Draw.PuzzleTypes: pentominous :: Backend' b => Drawers b (Grid C (Maybe Char)) (Grid C Char)
+ Draw.PuzzleTypes: pentominousBorders :: Backend' b => Drawers b (Grid C (), [Edge N]) (Grid C Char)
+ Draw.PuzzleTypes: pentopia :: Backend' b => Drawers b (Grid C (Maybe Myopia)) (Grid C Bool)
+ Draw.PuzzleTypes: persistenceOfMemory :: Backend' b => Drawers b (AreaGrid, (Grid C (Maybe MEnd))) (Loop C)
+ Draw.PuzzleTypes: primeplace :: Backend' b => Drawers b (Grid C PrimeDiag) (Grid C Int)
+ Draw.PuzzleTypes: pyramid :: Backend' b => Drawers b Pyramid PyramidSol
+ Draw.PuzzleTypes: scrabble :: Backend' b => Drawers b (Grid C Bool, [String]) (Grid C (Maybe Char))
+ Draw.PuzzleTypes: shikaku :: Backend' b => Drawers b (Grid C (Maybe Int)) AreaGrid
+ Draw.PuzzleTypes: skyscrapers :: Backend' b => Drawers b (OutsideClues C (Maybe Int), String) (Grid C (Maybe Int))
+ Draw.PuzzleTypes: skyscrapersStars :: Backend' b => Drawers b (OutsideClues C (Maybe Int), Int) (Grid C (Either Int Star))
+ Draw.PuzzleTypes: slalom :: Backend' b => Drawers b (Grid N (Maybe Int)) (Grid C SlalomDiag)
+ Draw.PuzzleTypes: slither :: Backend' b => Drawers b (Grid C (Maybe Int)) (Loop N)
+ Draw.PuzzleTypes: slithermulti :: Backend' b => Drawers b (Grid C (Maybe Int), Int) [Edge N]
+ Draw.PuzzleTypes: slovaksums :: Backend' b => Drawers b (Grid C (Maybe SlovakClue), String) (Grid C (Maybe Int))
+ Draw.PuzzleTypes: snake :: Backend' b => Drawers b (OutsideClues C (Maybe Int), Grid C (Maybe MEnd)) (Grid C (Maybe (Either MEnd Black)))
+ Draw.PuzzleTypes: starbattle :: Backend' b => Drawers b (AreaGrid, Int) (Grid C (Maybe Star))
+ Draw.PuzzleTypes: starwars :: Backend' b => Drawers b (AreaGrid, [MarkedLine C]) (Grid C (Maybe Star))
+ Draw.PuzzleTypes: statuepark :: Backend' b => Drawers b (Grid C (Maybe MasyuPearl)) (Grid C Bool)
+ Draw.PuzzleTypes: sudoku :: Backend' b => Drawers b (Grid C (Maybe Int)) (Grid C (Maybe Int))
+ Draw.PuzzleTypes: sudokuDoppelblock :: Backend' b => Drawers b (AreaGrid, OutsideClues C (Maybe Int)) (Grid C (Either Black Int))
+ Draw.PuzzleTypes: summon :: Backend' b => Drawers b (AreaGrid, OutsideClues C (Maybe Int), String) (Grid C (Maybe Int))
+ Draw.PuzzleTypes: tapa :: Backend' b => Drawers b (Grid C (Maybe TapaClue)) ShadedGrid
+ Draw.PuzzleTypes: tents :: Backend' b => Drawers b (OutsideClues C (Maybe Int), Grid C (Maybe Tree)) (Grid C (Maybe PlacedTent))
+ Draw.PuzzleTypes: thermosudoku :: Backend' b => Drawers b (Grid C (Maybe Int), [Thermometer]) (Grid C (Maybe Int))
+ Draw.PuzzleTypes: tightfitskyscrapers :: Backend' b => Drawers b (OutsideClues C (Maybe Int), Grid C (Tightfit ())) (Grid C (Tightfit Int))
+ Draw.PuzzleTypes: tomTom :: Backend' b => Drawers b (AreaGrid, Grid C (Maybe String)) (Grid C Int)
+ Draw.PuzzleTypes: wordloop :: Backend' b => Drawers b (Grid C (Maybe Char), [String]) (Grid C (Maybe Char))
+ Draw.PuzzleTypes: wordsearch :: Backend' b => Drawers b (Grid C (Maybe Char), [String]) (Grid C (Maybe Char), [MarkedWord])
+ Draw.PuzzleTypes: wormhole :: Backend' b => Drawers b (Grid C (Maybe (Either Int Char))) ()
+ Draw.Pyramid: cell :: Backend' b => Bool -> Diagram b
+ Draw.Pyramid: cellc :: Backend' b => Bool -> Maybe Int -> Diagram b
+ Draw.Pyramid: clue :: Backend' b => Maybe Int -> Diagram b
+ Draw.Pyramid: kpyramid :: Backend' b => RowKropkiPyramid -> Diagram b
+ Draw.Pyramid: krow :: Backend' b => KropkiRow -> Diagram b
+ Draw.Pyramid: pgray :: Colour Double
+ Draw.Pyramid: pyramid :: Backend' b => Pyramid -> Diagram b
+ Draw.Pyramid: row :: Backend' b => Row -> Diagram b
+ Draw.Style: FrameStyle :: Double -> Colour Double -> FrameStyle
+ Draw.Style: GridStyle :: LineStyle -> LineStyle -> Maybe FrameStyle -> VertexStyle -> GridStyle
+ Draw.Style: LineDashed :: LineStyle
+ Draw.Style: LineNone :: LineStyle
+ Draw.Style: LineThick :: LineStyle
+ Draw.Style: LineThin :: LineStyle
+ Draw.Style: VertexDot :: VertexStyle
+ Draw.Style: VertexNone :: VertexStyle
+ Draw.Style: [_border] :: GridStyle -> LineStyle
+ Draw.Style: [_fColour] :: FrameStyle -> Colour Double
+ Draw.Style: [_fWidthFactor] :: FrameStyle -> Double
+ Draw.Style: [_frame] :: GridStyle -> Maybe FrameStyle
+ Draw.Style: [_line] :: GridStyle -> LineStyle
+ Draw.Style: [_vertex] :: GridStyle -> VertexStyle
+ Draw.Style: data FrameStyle
+ Draw.Style: data GridStyle
+ Draw.Style: data LineStyle
+ Draw.Style: data VertexStyle
+ Draw.Style: gDashed :: GridStyle
+ Draw.Style: gDashedThick :: GridStyle
+ Draw.Style: gDefault :: GridStyle
+ Draw.Style: gPlain :: GridStyle
+ Draw.Style: gPlainDashed :: GridStyle
+ Draw.Style: gSlither :: GridStyle
+ Draw.Widths: borderwidth :: Double
+ Draw.Widths: edgewidth :: Double
+ Draw.Widths: fourpix :: Double
+ Draw.Widths: framewidthfactor :: Double
+ Draw.Widths: gridres :: Int
+ Draw.Widths: gridresd :: Double
+ Draw.Widths: gridwidth :: Double
+ Draw.Widths: onepix :: Double
+ Draw.Widths: twopix :: Double
+ Parse.Code: parseCode :: Value -> Parser [CodePart]
+ Parse.Parsec: fraction :: GenParser Char st Fraction
+ Parse.Parsec: toParser :: GenParser a () b -> [a] -> Parser b
+ Parse.Parsec: toStringParser :: GenParser Char () b -> Value -> Parser b
+ Parse.Puzzle: TP :: (Maybe String) -> Value -> (Maybe Value) -> (Maybe Value) -> TypedPuzzle
+ Parse.Puzzle: data TypedPuzzle
+ Parse.Puzzle: instance Data.Aeson.Types.FromJSON.FromJSON Parse.Puzzle.TypedPuzzle
+ Parse.Puzzle: instance GHC.Show.Show Parse.Puzzle.TypedPuzzle
+ Parse.Puzzle: parseType :: String -> Parser PuzzleType
+ Parse.Puzzle: type ParsePuzzle a b = (Value -> Parser a, Value -> Parser b)
+ Parse.PuzzleTypes: abctje :: ParsePuzzle (DigitRange, [(String, Int)]) [(Int, Char)]
+ Parse.PuzzleTypes: afternoonskyscrapers :: ParsePuzzle (Grid C Shade) (Grid C (Maybe Int))
+ Parse.PuzzleTypes: angleLoop :: ParsePuzzle (Grid N (Clue Int)) VertexLoop
+ Parse.PuzzleTypes: anglers :: ParsePuzzle (OutsideClues C (Maybe Int), Grid C (Maybe Fish)) [Edge C]
+ Parse.PuzzleTypes: baca :: ParsePuzzle (Grid C (Maybe Char), OutsideClues C [Int], OutsideClues C (Maybe Char)) (Grid C (Either Black Char))
+ Parse.PuzzleTypes: bahnhof :: ParsePuzzle (Grid C (Maybe BahnhofClue)) [Edge C]
+ Parse.PuzzleTypes: blackoutDominos :: ParsePuzzle (Grid C (Clue Int), DigitRange) (Grid C (Clue Int), AreaGrid)
+ Parse.PuzzleTypes: boxof2or3 :: ParsePuzzle (Grid N MasyuPearl, [Edge N]) ()
+ Parse.PuzzleTypes: buchstabensalat :: ParsePuzzle (OutsideClues C (Maybe Char), String) (Grid C (Maybe Char))
+ Parse.PuzzleTypes: cave :: ParsePuzzle (Grid C (Maybe Int)) (Grid C Bool)
+ Parse.PuzzleTypes: colorakari :: ParsePuzzle (Grid C (Maybe Char)) (Grid C (Maybe Char))
+ Parse.PuzzleTypes: compass :: ParsePuzzle (Grid C (Maybe CompassC)) AreaGrid
+ Parse.PuzzleTypes: coral :: ParsePuzzle (OutsideClues C [String]) (Grid C Bool)
+ Parse.PuzzleTypes: coralLits :: ParsePuzzle (OutsideClues C [String]) (Grid C (Maybe Char))
+ Parse.PuzzleTypes: coralLitso :: ParsePuzzle (OutsideClues C [String]) (Grid C (Either Black Char))
+ Parse.PuzzleTypes: countryRoad :: ParsePuzzle (AreaGrid, Grid C (Maybe Int)) (Loop C)
+ Parse.PuzzleTypes: curvedata :: ParsePuzzle (Grid C (Maybe [Edge N])) [Edge C]
+ Parse.PuzzleTypes: dominoPills :: ParsePuzzle (Grid C (Maybe Int), DigitRange, DigitRange) AreaGrid
+ Parse.PuzzleTypes: dominos :: ParsePuzzle (Grid C (Maybe Int), DigitRange) AreaGrid
+ Parse.PuzzleTypes: doppelblock :: ParsePuzzle (OutsideClues C (Maybe Int)) (Grid C (Either Black Int))
+ Parse.PuzzleTypes: doubleback :: ParsePuzzle AreaGrid (Loop C)
+ Parse.PuzzleTypes: fillomino :: ParsePuzzle (Grid C (Maybe Int)) (Grid C Int)
+ Parse.PuzzleTypes: fillominoLoop :: ParsePuzzle (Grid C (Maybe Int)) (Grid C Int, Loop C)
+ Parse.PuzzleTypes: friendlysudoku :: ParsePuzzle (Map (Edge N) KropkiDot, Grid C (Maybe Int)) (Grid C Int)
+ Parse.PuzzleTypes: galaxies :: ParsePuzzle (Grid C (), Grid N (), Grid C (), Map (Edge N) ()) AreaGrid
+ Parse.PuzzleTypes: geradeweg :: ParsePuzzle (Grid C (Maybe Int)) (Loop C)
+ Parse.PuzzleTypes: greaterWall :: ParsePuzzle ([GreaterClue], [GreaterClue]) (Grid C Bool)
+ Parse.PuzzleTypes: heyawake :: ParsePuzzle (AreaGrid, Grid C (Maybe Int)) (Grid C Bool)
+ Parse.PuzzleTypes: horseSnake :: ParsePuzzle (Grid C (Maybe (Either MEnd Int))) [Edge C]
+ Parse.PuzzleTypes: illumination :: ParsePuzzle (OutsideClues C (Maybe Fraction)) (Grid N (Maybe PlainNode), [Edge N])
+ Parse.PuzzleTypes: instance Data.Aeson.Types.FromJSON.FromJSON Parse.PuzzleTypes.Curve
+ Parse.PuzzleTypes: instance Data.Aeson.Types.FromJSON.FromJSON Parse.PuzzleTypes.GridMarked
+ Parse.PuzzleTypes: instance Data.Aeson.Types.FromJSON.FromJSON Parse.PuzzleTypes.GridWords
+ Parse.PuzzleTypes: instance Data.Aeson.Types.FromJSON.FromJSON Parse.PuzzleTypes.LSol
+ Parse.PuzzleTypes: instance Data.Aeson.Types.FromJSON.FromJSON Parse.PuzzleTypes.Myo
+ Parse.PuzzleTypes: japanesesums :: ParsePuzzle (OutsideClues C [Int], String) (Grid C (Either Black Int))
+ Parse.PuzzleTypes: japsummasyu :: ParsePuzzle (OutsideClues C [String]) ()
+ Parse.PuzzleTypes: killersudoku :: ParsePuzzle (AreaGrid, Map Char Int, Grid C (Maybe Int)) (Grid C Int)
+ Parse.PuzzleTypes: kpyramid :: ParsePuzzle RowKropkiPyramid PyramidSol
+ Parse.PuzzleTypes: kropki :: ParsePuzzle (Map (Edge N) KropkiDot) (Grid C Int)
+ Parse.PuzzleTypes: labyrinth :: ParsePuzzle (Grid C (Maybe Int), [Edge N], String) (Grid C (Maybe Int))
+ Parse.PuzzleTypes: latintapa :: ParsePuzzle (Grid C (Maybe [String])) (Grid C (Maybe Char))
+ Parse.PuzzleTypes: liarslither :: ParsePuzzle (Grid C (Maybe Int)) (Loop N, Grid C Bool)
+ Parse.PuzzleTypes: lits :: ParsePuzzle AreaGrid (Grid C Bool)
+ Parse.PuzzleTypes: litsplus :: ParsePuzzle AreaGrid (Grid C Bool)
+ Parse.PuzzleTypes: loopki :: ParsePuzzle (Grid C (Maybe MasyuPearl)) (Loop N)
+ Parse.PuzzleTypes: masyu :: ParsePuzzle (Grid C (Maybe MasyuPearl)) (Loop C)
+ Parse.PuzzleTypes: maximallengths :: ParsePuzzle (OutsideClues C (Maybe Int)) (Loop C)
+ Parse.PuzzleTypes: meanderingnumbers :: ParsePuzzle AreaGrid (Grid C (Maybe Int))
+ Parse.PuzzleTypes: mines :: ParsePuzzle (Grid C (Maybe Int)) (Grid C Bool)
+ Parse.PuzzleTypes: nanroSignpost :: ParsePuzzle (AreaGrid, Grid C (Maybe Int)) (Grid C Int)
+ Parse.PuzzleTypes: neighbors :: ParsePuzzle (Grid C Bool, Grid C (Maybe Int)) (Grid C Int)
+ Parse.PuzzleTypes: numberlink :: ParsePuzzle (Grid C (Maybe Int)) [Edge C]
+ Parse.PuzzleTypes: nurikabe :: ParsePuzzle (Grid C (Maybe Int)) (Grid C Bool)
+ Parse.PuzzleTypes: pentominoPipes :: ParsePuzzle (Grid N Char) (Grid N KropkiDot, [Edge N])
+ Parse.PuzzleTypes: pentominoSums :: ParsePuzzle (OutsideClues C [String], String) (Grid C (Either Pentomino Int), [(Char, Int)], OutsideClues C [String])
+ Parse.PuzzleTypes: pentominous :: ParsePuzzle (Grid C (Maybe Char)) (Grid C Char)
+ Parse.PuzzleTypes: pentominousBorders :: ParsePuzzle (Grid C (), [Edge N]) (Grid C Char)
+ Parse.PuzzleTypes: pentopia :: ParsePuzzle (Grid C (Maybe Myopia)) (Grid C Bool)
+ Parse.PuzzleTypes: persistenceOfMemory :: ParsePuzzle (AreaGrid, Grid C (Maybe MEnd)) (Loop C)
+ Parse.PuzzleTypes: primeplace :: ParsePuzzle (Grid C PrimeDiag) (Grid C Int)
+ Parse.PuzzleTypes: pyramid :: ParsePuzzle Pyramid PyramidSol
+ Parse.PuzzleTypes: scrabble :: ParsePuzzle (Grid C Bool, [String]) (Grid C (Maybe Char))
+ Parse.PuzzleTypes: shikaku :: ParsePuzzle (Grid C (Maybe Int)) AreaGrid
+ Parse.PuzzleTypes: skyscrapers :: ParsePuzzle (OutsideClues C (Maybe Int), String) (Grid C (Maybe Int))
+ Parse.PuzzleTypes: skyscrapersStars :: ParsePuzzle (OutsideClues C (Maybe Int), Int) (Grid C (Either Int Star))
+ Parse.PuzzleTypes: slalom :: ParsePuzzle (Grid N (Maybe Int)) (Grid C SlalomDiag)
+ Parse.PuzzleTypes: slither :: ParsePuzzle (Grid C (Clue Int)) (Loop N)
+ Parse.PuzzleTypes: slithermulti :: ParsePuzzle (Grid C (Clue Int), Int) [Edge N]
+ Parse.PuzzleTypes: slovaksums :: ParsePuzzle (Grid C (Maybe SlovakClue), String) (Grid C (Maybe Int))
+ Parse.PuzzleTypes: snake :: ParsePuzzle (OutsideClues C (Maybe Int), Grid C (Maybe MEnd)) (Grid C (Maybe (Either MEnd Black)))
+ Parse.PuzzleTypes: starbattle :: ParsePuzzle (AreaGrid, Int) (Grid C (Maybe Star))
+ Parse.PuzzleTypes: starwars :: ParsePuzzle (AreaGrid, [MarkedLine C]) (Grid C (Maybe Star))
+ Parse.PuzzleTypes: statuepark :: ParsePuzzle (Grid C (Maybe MasyuPearl)) (Grid C Bool)
+ Parse.PuzzleTypes: sudoku :: ParsePuzzle (Grid C (Maybe Int)) (Grid C (Maybe Int))
+ Parse.PuzzleTypes: sudokuDoppelblock :: ParsePuzzle (AreaGrid, OutsideClues C (Maybe Int)) (Grid C (Either Black Int))
+ Parse.PuzzleTypes: summon :: ParsePuzzle (AreaGrid, OutsideClues C (Maybe Int), String) (Grid C (Maybe Int))
+ Parse.PuzzleTypes: tapa :: ParsePuzzle (Grid C (Maybe TapaClue)) (Grid C Bool)
+ Parse.PuzzleTypes: tents :: ParsePuzzle (OutsideClues C (Maybe Int), Grid C (Maybe Tree)) (Grid C (Maybe PlacedTent))
+ Parse.PuzzleTypes: thermosudoku :: ParsePuzzle (Grid C (Maybe Int), [Thermometer]) (Grid C (Maybe Int))
+ Parse.PuzzleTypes: tightfitskyscrapers :: ParsePuzzle (OutsideClues C (Maybe Int), Grid C (Tightfit ())) (Grid C (Tightfit Int))
+ Parse.PuzzleTypes: tomTom :: ParsePuzzle (AreaGrid, Grid C (Maybe String)) (Grid C Int)
+ Parse.PuzzleTypes: wordloop :: ParsePuzzle (Grid C (Maybe Char), [String]) (Grid C (Maybe Char))
+ Parse.PuzzleTypes: wordsearch :: ParsePuzzle (Grid C (Maybe Char), [String]) (Grid C (Maybe Char), [MarkedWord])
+ Parse.PuzzleTypes: wormhole :: ParsePuzzle (Grid C (Maybe (Either Int Char))) ()
+ Parse.Util: Alpha :: Char -> Alpha
+ Parse.Util: Blank :: Blank
+ Parse.Util: Blank' :: Blank'
+ Parse.Util: Blank'' :: Blank''
+ Parse.Util: Border :: [a] -> [a] -> [a] -> [a] -> Border a
+ Parse.Util: BorderedRect :: !Int -> !Int -> [[a]] -> (Border b) -> BorderedRect a b
+ Parse.Util: Dirs' :: [Dir'] -> Dirs'
+ Parse.Util: Either' :: Either a b -> Either' a b
+ Parse.Util: Empty :: Empty
+ Parse.Util: HalfDirs :: [Dir] -> HalfDirs
+ Parse.Util: IntString :: String -> IntString
+ Parse.Util: MM :: Map k (Maybe a) -> MaybeMap k a
+ Parse.Util: PCC :: CompassC -> PCompassC
+ Parse.Util: PFraction :: Fraction -> PFraction
+ Parse.Util: PML :: MarkedLine a -> PMarkedLine a
+ Parse.Util: PMW :: MarkedWord -> PMarkedWord
+ Parse.Util: PSlovakClue :: SlovakClue -> PSlovakClue
+ Parse.Util: ParseTapaClue :: TapaClue -> ParseTapaClue
+ Parse.Util: Rect :: !Int -> !Int -> [[a]] -> Rect a
+ Parse.Util: RefGrid :: Grid k (Maybe a) -> RefGrid k a
+ Parse.Util: Shaded :: Bool -> Shaded
+ Parse.Util: Space :: Space
+ Parse.Util: SpacedRect :: Rect a -> SpacedRect a
+ Parse.Util: Tight :: Tightfit () -> Tight
+ Parse.Util: [unAlpha] :: Alpha -> Char
+ Parse.Util: [unDirs'] :: Dirs' -> [Dir']
+ Parse.Util: [unEither'] :: Either' a b -> Either a b
+ Parse.Util: [unHalfDirs] :: HalfDirs -> [Dir]
+ Parse.Util: [unIntString] :: IntString -> String
+ Parse.Util: [unMaybeMap] :: MaybeMap k a -> Map k (Maybe a)
+ Parse.Util: [unPCC] :: PCompassC -> CompassC
+ Parse.Util: [unPFraction] :: PFraction -> Fraction
+ Parse.Util: [unPML] :: PMarkedLine a -> MarkedLine a
+ Parse.Util: [unPMW] :: PMarkedWord -> MarkedWord
+ Parse.Util: [unPSlovakClue] :: PSlovakClue -> SlovakClue
+ Parse.Util: [unParseTapaClue] :: ParseTapaClue -> TapaClue
+ Parse.Util: [unRG] :: RefGrid k a -> Grid k (Maybe a)
+ Parse.Util: [unShaded] :: Shaded -> Bool
+ Parse.Util: [unSpaced] :: SpacedRect a -> Rect a
+ Parse.Util: [unTight] :: Tight -> Tightfit ()
+ Parse.Util: blankToMaybe :: Either Blank a -> Maybe a
+ Parse.Util: blankToMaybe' :: Either Blank' a -> Maybe a
+ Parse.Util: blankToMaybe'' :: Either Blank'' a -> Maybe a
+ Parse.Util: char :: Char -> Char -> Parser Char
+ Parse.Util: chars :: [Char] -> Char -> Parser Char
+ Parse.Util: class FromChar a
+ Parse.Util: class FromString a
+ Parse.Util: compose :: Ord b => Map a b -> Map b c -> Maybe (Map a c)
+ Parse.Util: compose' :: Ord b => Map a (Maybe b) -> Map b c -> Maybe (Map a (Maybe c))
+ Parse.Util: data Blank
+ Parse.Util: data Blank'
+ Parse.Util: data Blank''
+ Parse.Util: data Border a
+ Parse.Util: data BorderedRect a b
+ Parse.Util: data Empty
+ Parse.Util: data HalfDirs
+ Parse.Util: data Rect a
+ Parse.Util: data Space
+ Parse.Util: field :: Path -> Value -> Parser Value
+ Parse.Util: filterPlainEdges :: Map (Edge k) Char -> [Edge k]
+ Parse.Util: fromCoordEdge :: Key k => Edge Coord -> Edge k
+ Parse.Util: fromCoordEdges :: Key k => [Edge Coord] -> [Edge k]
+ Parse.Util: fromCoordGrid :: Key k => Grid Coord a -> Grid k a
+ Parse.Util: hashmaptomap :: Ord a => HashMap a b -> Map a b
+ Parse.Util: impossible :: a
+ Parse.Util: instance (Data.Aeson.Types.FromJSON.FromJSON a, Data.Aeson.Types.FromJSON.FromJSON b) => Data.Aeson.Types.FromJSON.FromJSON (Parse.Util.Either' a b)
+ Parse.Util: instance (Data.GridShape.Key k, Data.Aeson.Types.FromJSON.FromJSON a) => Data.Aeson.Types.FromJSON.FromJSON (Parse.Util.RefGrid k a)
+ Parse.Util: instance (GHC.Show.Show a, GHC.Show.Show b) => GHC.Show.Show (Parse.Util.BorderedRect a b)
+ Parse.Util: instance (Parse.Util.FromChar a, Parse.Util.FromChar b) => Data.Aeson.Types.FromJSON.FromJSON (Parse.Util.BorderedRect a b)
+ Parse.Util: instance (Parse.Util.FromChar a, Parse.Util.FromChar b) => Parse.Util.FromChar (Data.Either.Either a b)
+ Parse.Util: instance (Parse.Util.FromChar a, Parse.Util.FromChar b) => Parse.Util.FromChar (Parse.Util.Either' a b)
+ Parse.Util: instance (Parse.Util.FromString a, Parse.Util.FromString b) => Parse.Util.FromString (Data.Either.Either a b)
+ Parse.Util: instance Data.Aeson.Types.FromJSON.FromJSON Parse.Util.Blank
+ Parse.Util: instance Data.Aeson.Types.FromJSON.FromJSON Parse.Util.Blank'
+ Parse.Util: instance Data.Aeson.Types.FromJSON.FromJSON Parse.Util.IntString
+ Parse.Util: instance Data.Aeson.Types.FromJSON.FromJSON Parse.Util.PCompassC
+ Parse.Util: instance Data.Aeson.Types.FromJSON.FromJSON Parse.Util.PFraction
+ Parse.Util: instance Data.Aeson.Types.FromJSON.FromJSON Parse.Util.PMarkedWord
+ Parse.Util: instance Data.Aeson.Types.FromJSON.FromJSON Parse.Util.PSlovakClue
+ Parse.Util: instance Data.Aeson.Types.FromJSON.FromJSON Parse.Util.ParseTapaClue
+ Parse.Util: instance Data.Foldable.Foldable (Parse.Util.MaybeMap k)
+ Parse.Util: instance Data.Foldable.Foldable Parse.Util.Border
+ Parse.Util: instance Data.GridShape.FromCoord a => Data.Aeson.Types.FromJSON.FromJSON (Parse.Util.PMarkedLine a)
+ Parse.Util: instance Data.Traversable.Traversable (Parse.Util.MaybeMap k)
+ Parse.Util: instance Data.Traversable.Traversable Parse.Util.Border
+ Parse.Util: instance GHC.Base.Functor (Parse.Util.MaybeMap k)
+ Parse.Util: instance GHC.Base.Functor Parse.Util.Border
+ Parse.Util: instance GHC.Base.Functor Parse.Util.Rect
+ Parse.Util: instance GHC.Classes.Eq Parse.Util.Alpha
+ Parse.Util: instance GHC.Classes.Ord Parse.Util.Alpha
+ Parse.Util: instance GHC.Show.Show Parse.Util.Alpha
+ Parse.Util: instance GHC.Show.Show a => GHC.Show.Show (Parse.Util.Border a)
+ Parse.Util: instance GHC.Show.Show a => GHC.Show.Show (Parse.Util.Rect a)
+ Parse.Util: instance Parse.Util.FromChar ()
+ Parse.Util: instance Parse.Util.FromChar Data.Elements.Black
+ Parse.Util: instance Parse.Util.FromChar Data.Elements.Crossing
+ Parse.Util: instance Parse.Util.FromChar Data.Elements.Fish
+ Parse.Util: instance Parse.Util.FromChar Data.Elements.GalaxyCentre
+ Parse.Util: instance Parse.Util.FromChar Data.Elements.KropkiDot
+ Parse.Util: instance Parse.Util.FromChar Data.Elements.MEnd
+ Parse.Util: instance Parse.Util.FromChar Data.Elements.MasyuPearl
+ Parse.Util: instance Parse.Util.FromChar Data.Elements.Pentomino
+ Parse.Util: instance Parse.Util.FromChar Data.Elements.PlacedTent
+ Parse.Util: instance Parse.Util.FromChar Data.Elements.PlainNode
+ Parse.Util: instance Parse.Util.FromChar Data.Elements.PrimeDiag
+ Parse.Util: instance Parse.Util.FromChar Data.Elements.Relation
+ Parse.Util: instance Parse.Util.FromChar Data.Elements.SlalomDiag
+ Parse.Util: instance Parse.Util.FromChar Data.Elements.Star
+ Parse.Util: instance Parse.Util.FromChar Data.Elements.Tree
+ Parse.Util: instance Parse.Util.FromChar Data.GridShape.Dir'
+ Parse.Util: instance Parse.Util.FromChar GHC.Types.Char
+ Parse.Util: instance Parse.Util.FromChar GHC.Types.Int
+ Parse.Util: instance Parse.Util.FromChar Parse.Util.Alpha
+ Parse.Util: instance Parse.Util.FromChar Parse.Util.Blank
+ Parse.Util: instance Parse.Util.FromChar Parse.Util.Blank'
+ Parse.Util: instance Parse.Util.FromChar Parse.Util.Blank''
+ Parse.Util: instance Parse.Util.FromChar Parse.Util.Dirs'
+ Parse.Util: instance Parse.Util.FromChar Parse.Util.Empty
+ Parse.Util: instance Parse.Util.FromChar Parse.Util.HalfDirs
+ Parse.Util: instance Parse.Util.FromChar Parse.Util.Shaded
+ Parse.Util: instance Parse.Util.FromChar Parse.Util.Space
+ Parse.Util: instance Parse.Util.FromChar Parse.Util.Tight
+ Parse.Util: instance Parse.Util.FromChar a => Data.Aeson.Types.FromJSON.FromJSON (Parse.Util.Rect a)
+ Parse.Util: instance Parse.Util.FromChar a => Parse.Util.FromChar (GHC.Base.Maybe a)
+ Parse.Util: instance Parse.Util.FromChar a => Parse.Util.FromString (Data.Elements.Tightfit a)
+ Parse.Util: instance Parse.Util.FromString Data.Elements.DigitRange
+ Parse.Util: instance Parse.Util.FromString GHC.Types.Char
+ Parse.Util: instance Parse.Util.FromString GHC.Types.Int
+ Parse.Util: instance Parse.Util.FromString Parse.Util.Blank
+ Parse.Util: instance Parse.Util.FromString a => Data.Aeson.Types.FromJSON.FromJSON (Parse.Util.SpacedRect a)
+ Parse.Util: listListToMap :: [[a]] -> Grid Coord a
+ Parse.Util: newtype Alpha
+ Parse.Util: newtype Dirs'
+ Parse.Util: newtype Either' a b
+ Parse.Util: newtype IntString
+ Parse.Util: newtype MaybeMap k a
+ Parse.Util: newtype PCompassC
+ Parse.Util: newtype PFraction
+ Parse.Util: newtype PMarkedLine a
+ Parse.Util: newtype PMarkedWord
+ Parse.Util: newtype PSlovakClue
+ Parse.Util: newtype ParseTapaClue
+ Parse.Util: newtype RefGrid k a
+ Parse.Util: newtype Shaded
+ Parse.Util: newtype SpacedRect a
+ Parse.Util: newtype Tight
+ Parse.Util: parseAfternoonGrid :: Value -> Parser (Grid C Shade)
+ Parse.Util: parseAnnotatedEdges :: (Key k, FromChar a) => Value -> Parser (Map (Edge k) a)
+ Parse.Util: parseBorderedRect :: (Char -> Parser a) -> (Char -> Parser b) -> Value -> Parser (BorderedRect a b)
+ Parse.Util: parseBoxEdges :: Key k => Value -> Parser [Edge k]
+ Parse.Util: parseCellEdges :: FromChar a => Value -> Parser (Grid C a, [Edge N])
+ Parse.Util: parseChar :: FromChar a => Char -> Parser a
+ Parse.Util: parseCharJSON :: FromChar a => Value -> Parser a
+ Parse.Util: parseCharMap :: FromJSON a => Value -> Parser (Map Char a)
+ Parse.Util: parseCharOutside :: FromChar a => Value -> Parser (OutsideClues k a)
+ Parse.Util: parseClueGrid :: (FromChar a, Key k) => Value -> Parser (Grid k (Maybe a))
+ Parse.Util: parseClueGrid' :: (FromChar a, Key k) => Value -> Parser (Grid k (Maybe a))
+ Parse.Util: parseCoordGrid :: (FromChar a) => Value -> Parser (Grid Coord a)
+ Parse.Util: parseCoordLoop :: Value -> Parser VertexLoop
+ Parse.Util: parseDoublePair :: FromString a => Value -> Parser ((a, a), (a, a))
+ Parse.Util: parseEdgeGrid :: (FromChar a, FromChar b, FromChar c) => Value -> Parser (Grid N a, Grid C b, Map (Edge N) c)
+ Parse.Util: parseEdges :: Key k => Value -> Parser [Edge k]
+ Parse.Util: parseEdgesFull :: Key k => Value -> Parser [Edge k]
+ Parse.Util: parseExtClueGrid :: (Key k, FromChar a, FromJSON a) => Value -> Parser (Grid k (Maybe a))
+ Parse.Util: parseExtGrid :: (Key k, FromChar a, FromJSON a) => Value -> Parser (Grid k a)
+ Parse.Util: parseExtGrid' :: (Key k, FromJSON a, FromChar b) => (a -> b) -> Value -> Parser (Grid k b)
+ Parse.Util: parseFrom :: Path -> (Value -> Parser b) -> Value -> Parser b
+ Parse.Util: parseGreaterClue :: [Char] -> Parser GreaterClue
+ Parse.Util: parseGreaterClues :: Value -> Parser [GreaterClue]
+ Parse.Util: parseGrid :: (Key k, FromChar a) => Value -> Parser (Grid k a)
+ Parse.Util: parseGridChars :: FromChar a => Grid k Char -> Parser (Grid k a)
+ Parse.Util: parseGridWith :: Key k => (Char -> Parser a) -> Value -> Parser (Grid k a)
+ Parse.Util: parseIrregGrid :: (Key k, FromChar a) => Value -> Parser (Grid k a)
+ Parse.Util: parseLine :: FromChar a => String -> Parser [a]
+ Parse.Util: parseMarkedLine :: FromCoord a => Value -> Parser (MarkedLine a)
+ Parse.Util: parseMultiOutsideClues :: FromJSON a => Value -> Parser (OutsideClues k [a])
+ Parse.Util: parseNWords :: Int -> String -> Parser [String]
+ Parse.Util: parseNodeEdges :: FromChar a => Value -> Parser (Grid N a, [Edge N])
+ Parse.Util: parseOutside :: FromJSON a => Value -> Parser (OutsideClues k a)
+ Parse.Util: parseOutsideGrid :: Key k => (Char -> Parser a) -> (Char -> Parser b) -> Value -> Parser (OutsideClues k b, Grid k a)
+ Parse.Util: parseOutsideGridMap :: (Key k, FromChar a, FromChar b) => (a -> c) -> (b -> d) -> Value -> Parser (OutsideClues k d, Grid k c)
+ Parse.Util: parsePlainEdgeGrid :: (FromChar a, FromChar b) => Value -> Parser (Grid N a, Grid C b, [Edge N])
+ Parse.Util: parsePlainEdges :: Key k => Value -> Parser [Edge k]
+ Parse.Util: parseShadedGrid :: Key k => Value -> Parser (Grid k Bool)
+ Parse.Util: parseSpacedClueGrid :: (Key k, FromString a) => Value -> Parser (Grid k (Maybe a))
+ Parse.Util: parseSpacedGrid :: (Key k, FromString a) => Value -> Parser (Grid k a)
+ Parse.Util: parseString :: FromString a => String -> Parser a
+ Parse.Util: parseStringJSON :: FromString a => Value -> Parser a
+ Parse.Util: parseThermoGrid :: ThermoRect -> Parser (Grid C (Maybe Int), [Thermometer])
+ Parse.Util: parseThermos :: Grid C Alpha -> Parser [Thermometer]
+ Parse.Util: parseTightOutside :: Value -> Parser (OutsideClues C (Maybe Int), Grid C (Tightfit ()))
+ Parse.Util: parseWithReplacement :: FromChar a => (Char -> Maybe a) -> Char -> Parser a
+ Parse.Util: partitionEithers :: Ord k => Map k (Either a b) -> (Map k a, Map k b)
+ Parse.Util: readEdges :: Grid Coord Char -> Map (Edge Coord) Char
+ Parse.Util: rectToCoordGrid :: Rect a -> Grid Coord a
+ Parse.Util: rectToIrregGrid :: Rect (Either Empty a) -> Grid Coord a
+ Parse.Util: reorientOutside :: OutsideClues k a -> OutsideClues k a
+ Parse.Util: type Path = [String]
+ Parse.Util: type ThermoRect = Rect (Either Blank (Either Int Alpha))

Files

CHANGES.md view
@@ -1,3 +1,11 @@+0.3.0.0: 20180718+-----------------++* Switch backend to diagrams-rasterific, losing the dependency on Cairo.+* Upgrade to diagrams 1.4.+* Various new puzzle types, including Minesweeper, Tents, Snake,+  Country Road, Killer Sudoku.+ 0.2.0.0: 20161211 ----------------- 
README.md view
@@ -29,13 +29,19 @@   3.3..3 solution:   loop: |-    .┌──┐┌┐-    .│┌─┘││-    .│└──┘│-    ┌┘.┌─┐│-    └┐┌┘.││-    ┌┘│..││-    └─┘..└┘+    o o-o-o-o o-o+     .|. . .|.|.|+    o o o-o-o o o+     .|.|. . .|.|+    o o o-o-o-o o+     .|. . . . .|+    o-o o o-o-o o+    |. . .|. .|.|+    o-o o-o o o o+     .|.|. . .|.|+    o-o o o o o o+    |. .|. . .|.|+    o-o-o o o o-o   liars: |     ...X..     .X....@@ -45,8 +51,7 @@     ..X... ``` -There is a demo web application at-[puzzle-draw-demo.herokuapp.com][demo] that provides some more+There is a [demo web application][demo] that provides some more examples and that can be used to generate SVG images from such puzzle descriptions. @@ -57,24 +62,21 @@ ----------  This is a brief overview of how to install the command line tool.-You'll need to get a Haskell environment, by installing the-[Haskell Platform][platform]. Then install `puzzle-draw` with cabal:+The easiest way right now is to get [stack](https://www.stackage.org/),+clone this repository, and install using  ```-$ cabal install puzzle-draw+$ stack install ``` -This will compile puzzle-draw against the SVG backend, and install-a binary `drawpuzzle` that formats puzzles to SVG. Alternatively,-you can compile against the Cairo backend by passing `-fcairo`:+Alternatively, get a Haskell environment by installing the+[Haskell Platform](https://www.haskell.org/platform/).+Then install `puzzle-draw` with cabal:  ```-$ cabal install -fcairo puzzle-draw+$ cabal install puzzle-draw ``` -This will enable PDF and PNG output. Be warned that getting Cairo-installed can be tricky on non-Linux platforms.- Running ------- @@ -96,9 +98,7 @@ behaviour, e.g., choosing the output format.  [travis]: https://travis-ci.org/robx/puzzle-draw-[cmdline]: https://github.com/robx/puzzle-draw-cmdline [liarslither]: https://maybepuzzles.wordpress.com/types/liar-slither-link/ [twentyfour]: https://maybepuzzles.wordpress.com/2014/03/29/puzzle-set-24-hour-marathon/ [diagrams]: http://projects.haskell.org/diagrams/-[demo]: https://puzzle-draw-demo.herokuapp.com-[platform]: https://www.haskell.org/platform/+[demo]: https://arp.vllmrt.net/puzzles/draw/static/puzzle.html
puzzle-draw.cabal view
@@ -1,145 +1,131 @@-name:                puzzle-draw-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-                     layer on top of Diagrams to help with drawing arbitrary-                     puzzles, and to support several specific puzzles types-                     In addition, it includes functionality for parsing-                     puzzle data from a YAML file format.-license:             MIT-license-file:        LICENSE-author:              Robert Vollmert-maintainer:          rfvollmert@gmail.com-category:            Graphics-build-type:          Simple-extra-source-files:  README.md, CHANGES.md-data-files:          data/fonts/*.svg-cabal-version:       >=1.8+cabal-version: >=1.8+name: puzzle-draw+version: 0.3.0.0+license: MIT+license-file: LICENSE+maintainer: rob@vllmrt.net+author: Robert Vollmert+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+    layer on top of Diagrams to help with drawing arbitrary+    puzzles, and to support several specific puzzles types+    In addition, it includes functionality for parsing+    puzzle data from a YAML file format.+category: Graphics+build-type: Simple+data-files:+    data/fonts/*.svg+extra-source-files:+    README.md+    CHANGES.md  source-repository head-  type:     git-  location: http://github.com/robx/puzzle-draw.git--flag cairo-  description: Build against Cairo backend-  default:     False+    type: git+    location: http://github.com/robx/puzzle-draw.git  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-                       Diagrams.Puzzles.Widths-                       Diagrams.Puzzles.Pyramid-                       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.8 && < 5,-                       diagrams-lib >= 1.3.1,-                       parsec >= 3.1,-                       yaml >= 0.8.4,-                       aeson >= 0.7,-                       unordered-containers >= 0.2,-                       containers >= 0.5,-                       hashable >= 1.2,-                       text >= 1.1,-                       SVGFonts >= 1.4,-                       vector-space >= 0.8,-                       mtl >= 2.1,-                       optparse-applicative >= 0.12 && < 0.13,-                       filepath >= 1.3,-                       linear-  if flag(cairo)-    cpp-options: "-DCAIRO"-    build-depends:     diagrams-cairo >= 1.3-  else-    build-depends:     diagrams-svg >= 1.3-  hs-source-dirs:      src-  ghc-options:         -Wall+    exposed-modules:+        Data.Grid+        Data.GridShape+        Data.Util+        Data.Elements+        Data.Pyramid+        Data.Sudoku+        Data.Compose+        Data.PuzzleTypes+        Data.Code+        Data.CmdLine+        Parse.Util+        Parse.Parsec+        Parse.Puzzle+        Parse.PuzzleTypes+        Parse.Code+        Draw.Style+        Draw.Grid+        Draw.Lib+        Draw.Elements+        Draw.Widths+        Draw.Pyramid+        Draw.PuzzleGrids+        Draw.PuzzleTypes+        Draw.Draw+        Draw.Code+        Draw.CmdLine+    hs-source-dirs: src+    other-modules:+        Paths_puzzle_draw+    ghc-options: -Wall+    build-depends:+        base >=4.8 && <5,+        diagrams-lib >=1.4.2.3 && <1.5,+        parsec ==3.1.*,+        yaml >=0.8.4 && <0.9,+        aeson >=0.7 && <1.4,+        unordered-containers ==0.2.*,+        containers ==0.5.*,+        hashable ==1.2.*,+        text >=1.1 && <1.3,+        SVGFonts ==1.7.*,+        vector-space >=0.8 && <0.14,+        mtl >=2.1 && <2.3,+        optparse-applicative >=0.13 && <0.15,+        filepath >=1.3 && <1.5,+        linear >=1.20.8 && <1.21,+        diagrams-rasterific >=1.4.1 && <1.5  executable drawpuzzle-  main-is:          drawpuzzle.hs-  hs-source-dirs:   src/tools-  build-depends:       base >= 4.8 && < 5,-                       puzzle-draw,-                       diagrams-lib >= 1.3,-                       yaml >= 0.8.4,-                       optparse-applicative >= 0.12,-                       aeson >= 0.7,-                       filepath >= 1.3-  if flag(cairo)-    cpp-options: "-DCAIRO"-    build-depends:     diagrams-cairo >= 1.3-  else-    build-depends:     diagrams-svg >= 1.3-  ghc-options:         -Wall+    main-is: drawpuzzle.hs+    hs-source-dirs: src/tools+    ghc-options: -Wall+    build-depends:+        base >=4.8 && <5,+        puzzle-draw -any,+        diagrams-lib >=1.4.2.3 && <1.5,+        yaml >=0.8.4 && <0.9,+        optparse-applicative >=0.12 && <0.15,+        aeson >=0.7 && <1.4,+        filepath >=1.3 && <1.5,+        diagrams-rasterific >=1.4.1 && <1.5  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+    main-is: checkpuzzle.hs+    hs-source-dirs: src/tools+    ghc-options: -Wall+    build-depends:+        base >=4.8 && <5,+        puzzle-draw -any,+        yaml >=0.8.4 && <0.9,+        containers ==0.5.*,+        optparse-applicative >=0.12 && <0.15,+        aeson >=0.7 && <1.4,+        filepath >=1.3 && <1.5  test-suite test-  type:                exitcode-stdio-1.0-  hs-source-dirs:      tests-  main-is:             tests.hs-  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.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+    type: exitcode-stdio-1.0+    main-is: tests.hs+    hs-source-dirs: tests+    other-modules:+        Data+        Util+        Data.GridSpec+        Data.GridShapeSpec+        Draw.GridSpec+        Parse.PuzzleTypesSpec+        Parse.UtilSpec+    ghc-options: -Wall+    build-depends:+        base >=4.8 && <5,+        tasty >=0.8 && <1.2,+        tasty-hspec >=1.1.5 && <1.2,+        tasty-hunit >=0.8 && <0.11,+        hspec >=2.5.4 && <2.6,+        yaml >=0.8.4 && <0.9,+        text >=1.1 && <1.3,+        deepseq >=1.3 && <1.5,+        containers ==0.5.*,+        blaze-svg ==0.3.*,+        diagrams-lib >=1.4.2.3 && <1.5,+        bytestring ==0.10.*,+        puzzle-draw -any
+ src/Data/CmdLine.hs view
@@ -0,0 +1,27 @@+module Data.CmdLine+    ( checkType+    , exitErr+    , readPuzzle+    ) where++import Parse.Puzzle+import Data.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/Code.hs view
@@ -0,0 +1,19 @@+module Data.Code where++import Data.Map.Strict (Map)++import Data.Grid+import Data.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.+    | LRows'  (Map Char Int) -- ^ Rows of cells, counted from the bottom.+    | LCols   (Map Char Int) -- ^ Cols of cells, counted from the left.+    | LRowsN' (Map Char Int) -- ^ Rows of nodes, counted from the bottom.+    | LColsN  (Map Char Int) -- ^ Cols of nodes, counted from the left.
+ src/Data/Compose.hs view
@@ -0,0 +1,134 @@+{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, RankNTypes #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE TypeFamilies #-}++-- |+-- Helpers to string together parser and renderer by puzzle type.++module Data.Compose (+    PuzzleHandler,+    handle,+    -- * Handlers+    drawPuzzleMaybeSol,+  ) where++import Diagrams.Prelude+import Data.Yaml (Parser, Value)++import Parse.Puzzle+import Draw.Draw+import Draw.Lib+import Data.PuzzleTypes++import qualified Parse.PuzzleTypes as R+import qualified Draw.PuzzleTypes as D++-- | A function to compose an arbitrary matching pair of parser and renderer.+--   In @PuzzleHandler b a@, @b@ is the rendering backend type, while @a@ is+--   the result type of the composition.+type PuzzleHandler b a = forall p q.+                         ParsePuzzle p q -> Drawers b p q -> a++-- | @handle h t@ composes the parser and renderer for the puzzle+--   type @t@ with the handler @h@.+handle :: Backend' b =>+        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+handle f Nurikabe             = f R.nurikabe            D.nurikabe+handle f LatinTapa            = f R.latintapa           D.latintapa+handle f Sudoku               = f R.sudoku              D.sudoku+handle f ThermoSudoku         = f R.thermosudoku        D.thermosudoku+handle f Pyramid              = f R.pyramid             D.pyramid+handle f RowKropkiPyramid     = f R.kpyramid            D.kpyramid+handle f SlitherLink          = f R.slither             D.slither+handle f SlitherLinkLiar      = f R.liarslither         D.liarslither+handle f TightfitSkyscrapers  = f R.tightfitskyscrapers D.tightfitskyscrapers+handle f WordLoop             = f R.wordloop            D.wordloop+handle f WordSearch           = f R.wordsearch          D.wordsearch+handle f CurveData            = f R.curvedata           D.curvedata+handle f DoubleBack           = f R.doubleback          D.doubleback+handle f Slalom               = f R.slalom              D.slalom+handle f Compass              = f R.compass             D.compass+handle f BoxOf2Or3            = f R.boxof2or3           D.boxof2or3+handle f AfternoonSkyscrapers = f R.afternoonskyscrapers D.afternoonskyscrapers+handle f MeanderingNumbers    = f R.meanderingnumbers   D.meanderingnumbers+handle f Tapa                 = f R.tapa                D.tapa+handle f JapaneseSums         = f R.japanesesums        D.japanesesums+handle f Coral                = f R.coral               D.coral+handle f MaximalLengths       = f R.maximallengths      D.maximallengths+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+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+handle f Mines                = f R.mines               D.mines+handle f Tents                = f R.tents               D.tents+handle f PentominoSums        = f R.pentominoSums       D.pentominoSums+handle f CoralLITS            = f R.coralLits           D.coralLits+handle f CoralLITSO           = f R.coralLitso          D.coralLitso+handle f Snake                = f R.snake               D.snake+handle f CountryRoad          = f R.countryRoad         D.countryRoad+handle f KillerSudoku         = f R.killersudoku        D.killersudoku+handle f FriendlySudoku       = f R.friendlysudoku      D.friendlysudoku+handle f JapaneseSumsMasyu    = f R.japsummasyu         D.japsummasyu+++-- | 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, Maybe (Diagram b)))+drawPuzzleMaybeSol (pp, ps) (Drawers 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 <*> pure ())
+ src/Data/Elements.hs view
@@ -0,0 +1,116 @@+-- | Types for a variety of puzzle elements.+module Data.Elements where++import Data.GridShape++type Clue a = Maybe a++data MasyuPearl = MWhite | MBlack+    deriving (Eq, Show)++type MasyuClue = Clue MasyuPearl++type IntClue = Clue Int++-- | A Compass clue, specifiying optional numbers in the+--   four cardinal directions.+data CompassC = CC (Maybe Int) (Maybe Int) (Maybe Int) (Maybe Int)+    deriving Show++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++instance Show a => Show (Tightfit a) where+    show c = "(" ++ show' c ++ ")"+        where show' (Single x) = show x+              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 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 = [C]++-- | A forward or backward diagonal as occurring in the solution+--   of a slalom puzzle.+data SlalomDiag = SlalomForward | SlalomBackward+    deriving Show++-- | Shadow along from the western and southern side, as used for+--   afternoon skyscrapers.+data Shade = Shade Bool Bool+    deriving Show++data KropkiDot = KNone | KBlack | KWhite+    deriving (Show, Eq, Ord)++newtype TapaClue = TapaClue [Int]+    deriving Show++-- | Diagonal marking for Prime Place: forward diag?, backward diag?+newtype PrimeDiag = PrimeDiag (Bool, Bool)++data Black = Black+    deriving Eq++data Fish = Fish+    deriving Eq++data Star = Star+    deriving Eq++data Crossing = Crossing+    deriving Eq++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++data PlacedTent = Tent Dir'++data Tree = Tree++data Pentomino = Pentomino Char+    deriving (Show, Eq)
+ src/Data/Grid.hs view
@@ -0,0 +1,314 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ConstraintKinds #-}++-- | Puzzle grids.+module Data.Grid+    (+      Grid+    , AreaGrid+    , ShadedGrid+    , nodes+    , size+    , sizeGrid+    , clues+    , nodeGrid+    , cellGrid++    , dominoGrid+    , litsGrid+    , litsoGrid+    , pentominoGrid++    , borders+    , skeletons+    , edgesGen+    , colour+    , collectLines+    , rows++    , OutsideClues(..)+    , outsideSize+    , outsideClues+    , multiOutsideClues+    , outsideGrid+    , outsideValues+    ) where++import qualified Data.Map.Strict as Map+import qualified Data.Set as Set+import Data.AffineSpace+import Data.VectorSpace+import Control.Monad.State++import Data.Elements+import Data.GridShape++type Grid k a = Map.Map k a++type AreaGrid = Grid C Char+type ShadedGrid = Grid C Bool++-- | 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++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++nodes :: Grid N a -> Set.Set N+nodes = Map.keysSet++-- | The inner edges of a grid that separate unequal cells.+borders :: Eq a => Grid C a -> [Edge N]+borders = edgesGen (/=) (const False)++corners :: C -> [N]+corners c = map (.+^ (c .-. C 0 0)) [N 0 0, N 1 0, N 0 1, N 1 1]++-- | 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++cellGrid :: Grid N a -> Grid C ()+cellGrid m = Map.fromList+           . map (flip (,) ())+           . filter (all (`Map.member` m) . corners)+           . map cellUpRight+           . Map.keys+           $ m+  where+    cellUpRight :: N -> C+    cellUpRight = fromCoord . toCoord++-- | 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"++    start = fmap (const $ Left [1..]) m+    colour' = mapM_ pickAndFill (Map.keys m)++    -- 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 ()++    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 ()++    -- 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'++colour :: Eq a => Grid C a -> Grid C Int+colour m = colourM edgeNeighbours' m+  where+    edgeNeighbours' p = [ q | q <- edgeNeighbours p+                            , q `Map.member` m ]++-- | 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)++outsideValues :: OutsideClues k a -> [a]+outsideValues (OC l r b t) = l ++ r ++ b ++ t++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)++outsideSize :: OutsideClues k a -> Size+outsideSize (OC l r b t) = (w, h)+  where+    w = max (length t) (length b)+    h = max (length l) (length r)++-- | Create a dummy grid matching the given outside clues in size.+outsideGrid :: (Ord k, FromCoord k) => OutsideClues k a -> Grid k ()+outsideGrid = sizeGrid . outsideSize++-- | 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] ]++data OClue = OClue+    { ocBase :: (Int, Int)+    , _ocDir :: (Int, Int)+    }+  deriving (Show, Eq, Ord)++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++outsideClues :: (Ord k, FromCoord k) => OutsideClues k a -> Map.Map k a+outsideClues = Map.mapKeys (fromCoord . ocBase) . oClues++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++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) ]++collectLines :: (Key k, Eq a) => Grid k (Maybe a) -> [Edge k]+collectLines = dualEdgesP eq+  where+    eq (Just x) (Just y) = x == y+    eq _        _        = False++-- | The skeletons of connected equal cells.+skeletons :: Eq a => Grid C a -> [Edge C]+skeletons = map dualE . edgesGen (==) (const False)++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+    s = y - x++size :: Grid Coord a -> Size+size m = foldr (both max) (0, 0) (Map.keys m) ^+^ (1, 1)+  where+    both f (x, y) (x', y') = (f x x', f y y')++polyominoGrid :: [ ((Int, Int), Char) ] -> [ (Int,Int) ] -> Grid C (Maybe Char)+polyominoGrid ls ps = Map.mapKeys fromCoord . Map.fromList $+    [ (a, Just c) | (a, c) <- ls ] +++    [ (a, Nothing) | a <- ps ]++{-+A grid with (Just p) for the capital letters,+Nothing for the lowercase letters.++    Vvv..N.L..X...Ff.Zz..W+    v...nn.l.xxx.ff..z..ww+    v...n..l..x...f.zz.ww.+    ..T.n.ll..............+    ttt.......U.u..Y...Pp.+    ..t.Iiiii.uuu.yyyy.ppp+-}+pentominoGrid :: Grid C (Maybe Char)+pentominoGrid = polyominoGrid ls ps+  where+    ls = [ ((0,5), 'V')+         , ((5,5), 'N')+         , ((7,5), 'L')+         , ((10,5), 'X')+         , ((14,5), 'F')+         , ((17,5), 'Z')+         , ((21,5), 'W')+         , ((2,2), 'T')+         , ((4,0), 'I')+         , ((10,1), 'U')+         , ((15,1), 'Y')+         , ((19,1), 'P')+         ]+    ps = [ (1,5), (2,5), (0,4), (0,3)+         , (0,1), (1,1), (2,1), (2,0)+         , (5,0), (6,0), (7,0), (8,0)+         , (10,0), (11,0), (12,0), (12,1)+         , (14,0), (15,0), (16,0), (17,0)+         , (19,0), (20,0), (20,1), (21,0)+         , (4,2), (4,3), (4,4), (5,4)+         , (6,2), (7,2), (7,3), (7,4)+         , (9,4), (10,4), (11,4), (10,3)+         , (13,4), (14,4), (14,3), (15,5)+         , (18,5), (17,4), (16,3), (17,3)+         , (19,3), (20,3), (20,4), (21,4)+         ]+++{-+A grid with (Just p) for the capital letters,+Nothing for the lowercase letters.++    L  I+    l  i Ttt  Ss Oo+    ll i  t  ss  oo+       i+-}+litsoGrid :: Grid C (Maybe Char)+litsoGrid = polyominoGrid ls ps+  where+    ls = [ ((0,3), 'L')+         , ((3,3), 'I')+         , ((5,2), 'T')+         , ((10,2), 'S')+         , ((13,2), 'O')+         ]+    ps = [ (0,1), (0,2), (1,1)+         , (3,0), (3,1), (3,2)+         , (6,2), (6,1), (7,2)+         , (9,1), (10,1), (11,2)+         , (13,1), (14,1), (14,2)+         ]++{-+A grid with (Just p) for the capital letters,+Nothing for the lowercase letters.++    L  I+    l  i Ttt  Ss+    ll i  t  ss+       i+-}+litsGrid :: Grid C (Maybe Char)+litsGrid = polyominoGrid ls ps+  where+    ls = [ ((0,3), 'L')+         , ((3,3), 'I')+         , ((5,2), 'T')+         , ((10,2), 'S')+         ]+    ps = [ (0,1), (0,2), (1,1)+         , (3,0), (3,1), (3,2)+         , (6,2), (6,1), (7,2)+         , (9,1), (10,1), (11,2)+         ]
+ src/Data/GridShape.hs view
@@ -0,0 +1,231 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ConstraintKinds #-}++-- | Grid shapes.+module Data.GridShape+    (+      Coord+    , Size+    , Square(..)+    , Dir(..)+    , Edge(..)+    , Dir'(..)+    , Edge'(..)+    , Dual2D(..)+    , Key+    , Dual'+    , C(..)+    , N(..)+    , FromCoord(..)+    , ToCoord(..)++    , edge+    , edge'+    , edgeBetween+    , edgeBetween'+    , orient+    , ends'+    , revEdge+    , edges+    , edgesM+    , ends+    , edgeSize+    , unorient+    , dualE+    , vertexNeighbours+    , edgeNeighbours+    , rows+    ) where++import qualified Data.Foldable as F+import Data.List (partition, groupBy, sortOn)+import qualified Data.Map.Strict as Map+import Data.AffineSpace++type Coord = (Int, Int)++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+    deriving (Show, Eq)++squareNeighbours :: [(Int,Int)] -> C -> [C]+squareNeighbours deltas c = map (c .+^) deltas++vertexNeighbours :: C -> [C]+vertexNeighbours = squareNeighbours [ (dx, dy)+                                    | dx <- [-1..1], dy <- [-1..1]+                                    , 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 = Vert | Horiz+    deriving (Eq, Ord, Show)++-- | An edge in a square grid, going up or right from the given cell+--   centre.+data Edge a = E a Dir+    deriving (Show, Eq, Ord)++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.+data Edge' a = E' a Dir'+    deriving (Eq, Ord, Show)++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 :: (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)] ]+        (ni, no) = partition isc nbrs+        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)++rows :: Map.Map C a -> [[a]]+rows g = map (map snd) $+    grouped byRow (Map.toList g) ++ grouped byCol (Map.toList g)+  where+    byRow (C _ y, _) = y+    byCol (C x _, _) = x+    grouped :: (Ord b, Eq b) => (a -> b) -> [a] -> [[a]]+    grouped f = map (map snd) . groupOn fst . sortOn fst . map (\x -> (f x, x))+    groupOn :: Eq b => (a -> b) -> [a] -> [[a]]+    groupOn f = groupBy (\x y -> f x == f y)
+ src/Data/PuzzleTypes.hs view
@@ -0,0 +1,194 @@+{-# LANGUAGE FlexibleContexts, RankNTypes #-}++-- |+-- List of specific puzzle types.++module Data.PuzzleTypes (+    PuzzleType(..)+  , lookupType+  , typeNames+  ) where++import Data.Tuple (swap)++-- | The list of specific puzzle types we can handle.+data PuzzleType = LITS+                | LITSPlus+                | LITSSym+                | Geradeweg+                | Fillomino+                | Masyu+                | Nurikabe+                | LatinTapa+                | Sudoku+                | ThermoSudoku+                | Pyramid+                | RowKropkiPyramid+                | SlitherLink+                | SlitherLinkLiar+                | TightfitSkyscrapers+                | WordLoop+                | WordSearch+                | CurveData+                | DoubleBack+                | Slalom+                | Compass+                | BoxOf2Or3+                | AfternoonSkyscrapers+                | MeanderingNumbers+                | Tapa+                | JapaneseSums+                | Coral+                | MaximalLengths+                | 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+                | Mines+                | Tents+                | PentominoSums+                | CoralLITS+                | CoralLITSO+                | Snake+                | CountryRoad+                | KillerSudoku+                | FriendlySudoku+                | JapaneseSumsMasyu+    deriving (Show, Eq)++typeNames :: [(PuzzleType, String)]+typeNames = [ (LITS, "lits")+            , (LITSPlus, "litsplus")+            , (LITSSym, "lits-symmetry")+            , (Geradeweg, "geradeweg")+            , (Fillomino, "fillomino")+            , (Masyu, "masyu")+            , (Nurikabe, "nurikabe")+            , (LatinTapa, "latintapa")+            , (Sudoku, "sudoku")+            , (ThermoSudoku, "thermosudoku")+            , (Pyramid, "pyramid")+            , (RowKropkiPyramid, "rowkropkipyramid")+            , (SlitherLink, "slitherlink")+            , (SlitherLinkLiar, "slitherlinkliar")+            , (TightfitSkyscrapers, "skyscrapers-tightfit")+            , (WordLoop, "wordloop")+            , (WordSearch, "wordsearch")+            , (CurveData, "curvedata")+            , (DoubleBack, "doubleback")+            , (Slalom, "slalom")+            , (Compass, "compass")+            , (BoxOf2Or3, "boxof2or3")+            , (AfternoonSkyscrapers, "afternoonskyscrapers")+            , (MeanderingNumbers, "meanderingnumbers")+            , (Tapa, "tapa")+            , (JapaneseSums, "japanesesums")+            , (Coral, "coral")+            , (MaximalLengths, "maximallengths")+            , (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")+            , (Mines, "mines")+            , (Tents, "tents")+            , (PentominoSums, "pentomino-sums")+            , (CoralLITS, "coral+lits")+            , (CoralLITSO, "coral+litso")+            , (Snake, "snake")+            , (CountryRoad, "country-road")+            , (KillerSudoku, "killersudoku")+            , (FriendlySudoku, "friendly-sudoku")+            , (JapaneseSumsMasyu, "japanesesums-masyu")+            ]++-- | Look up a puzzle type by name.+lookupType :: String -> Maybe PuzzleType+lookupType t = lookup t (map swap typeNames)
− src/Data/Puzzles/CmdLine.hs
@@ -1,27 +0,0 @@-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
@@ -1,13 +0,0 @@-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
@@ -1,123 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, RankNTypes #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE TypeFamilies #-}---- |--- Helpers to string together parser and renderer by puzzle type.--module Data.Puzzles.Compose (-    PuzzleHandler,-    handle,-    -- * Handlers-    drawPuzzleMaybeSol,-  ) where--import Diagrams.Prelude-import Data.Yaml (Parser, Value)--import Text.Puzzles.Puzzle-import Diagrams.Puzzles.Draw-import Diagrams.Puzzles.Lib-import Data.Puzzles.PuzzleTypes--import qualified Text.Puzzles.PuzzleTypes as R-import qualified Diagrams.Puzzles.PuzzleTypes as D---- | A function to compose an arbitrary matching pair of parser and renderer.---   In @PuzzleHandler b a@, @b@ is the rendering backend type, while @a@ is---   the result type of the composition.-type PuzzleHandler b a = forall p q.-                         ParsePuzzle p q -> RenderPuzzle b p q -> a---- | @handle h t@ composes the parser and renderer for the puzzle---   type @t@ with the handler @h@.-handle :: Backend' b =>-        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-handle f Nurikabe             = f R.nurikabe            D.nurikabe-handle f LatinTapa            = f R.latintapa           D.latintapa-handle f Sudoku               = f R.sudoku              D.sudoku-handle f ThermoSudoku         = f R.thermosudoku        D.thermosudoku-handle f Pyramid              = f R.pyramid             D.pyramid-handle f RowKropkiPyramid     = f R.kpyramid            D.kpyramid-handle f SlitherLink          = f R.slither             D.slither-handle f SlitherLinkLiar      = f R.liarslither         D.liarslither-handle f TightfitSkyscrapers  = f R.tightfitskyscrapers D.tightfitskyscrapers-handle f WordLoop             = f R.wordloop            D.wordloop-handle f WordSearch           = f R.wordsearch          D.wordsearch-handle f CurveData            = f R.curvedata           D.curvedata-handle f DoubleBack           = f R.doubleback          D.doubleback-handle f Slalom               = f R.slalom              D.slalom-handle f Compass              = f R.compass             D.compass-handle f BoxOf2Or3            = f R.boxof2or3           D.boxof2or3-handle f AfternoonSkyscrapers = f R.afternoonskyscrapers D.afternoonskyscrapers-handle f MeanderingNumbers    = f R.meanderingnumbers   D.meanderingnumbers-handle f Tapa                 = f R.tapa                D.tapa-handle f JapaneseSums         = f R.japanesesums        D.japanesesums-handle f Coral                = f R.coral               D.coral-handle f MaximalLengths       = f R.maximallengths      D.maximallengths-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-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, 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)
− src/Data/Puzzles/Elements.hs
@@ -1,102 +0,0 @@--- | Types for a variety of puzzle elements.-module Data.Puzzles.Elements where--import Data.Puzzles.GridShape--type Clue a = Maybe a--data MasyuPearl = MWhite | MBlack-    deriving (Eq, Show)--type MasyuClue = Clue MasyuPearl--type IntClue = Clue Int---- | A Compass clue, specifiying optional numbers in the---   four cardinal directions.-data CompassC = CC (Maybe Int) (Maybe Int) (Maybe Int) (Maybe Int)-    deriving Show--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--instance Show a => Show (Tightfit a) where-    show c = "(" ++ show' c ++ ")"-        where show' (Single x) = show x-              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 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 = [C]---- | A forward or backward diagonal as occurring in the solution---   of a slalom puzzle.-data SlalomDiag = SlalomForward | SlalomBackward-    deriving Show---- | Shadow along from the western and southern side, as used for---   afternoon skyscrapers.-data Shade = Shade Bool Bool-    deriving Show--data KropkiDot = KNone | KBlack | KWhite-    deriving Show--newtype TapaClue = TapaClue [Int]-    deriving Show---- | Diagonal marking for Prime Place: forward diag?, backward diag?-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
@@ -1,207 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ConstraintKinds #-}---- | Puzzle grids.-module Data.Puzzles.Grid-    (-      Grid-    , AreaGrid-    , ShadedGrid-    , nodes-    , size-    , sizeGrid-    , clues-    , nodeGrid-    , cellGrid--    , dominoGrid--    , borders-    , edgesGen-    , colour-    , collectLines--    , OutsideClues(..)-    , outsideSize-    , outsideClues-    , multiOutsideClues-    , outsideGrid-    ) where--import qualified Data.Map as Map-import qualified Data.Set as Set-import Data.AffineSpace-import Data.VectorSpace-import Control.Monad.State--import Data.Puzzles.Elements-import Data.Puzzles.GridShape--type Grid k a = Map.Map k a--type AreaGrid = Grid C Char-type ShadedGrid = Grid C Bool---- | 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--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---nodes :: Grid N a -> Set.Set N-nodes = Map.keysSet---- | The inner edges of a grid that separate unequal cells.-borders :: Eq a => Grid C a -> [Edge N]-borders = edgesGen (/=) (const False)--corners :: C -> [N]-corners c = map (.+^ (c .-. C 0 0)) [N 0 0, N 1 0, N 0 1, N 1 1]---- | 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--cellGrid :: Grid N a -> Grid C ()-cellGrid m = Map.fromList-           . map (flip (,) ())-           . filter (all (`Map.member` m) . corners)-           . map cellUpRight-           . Map.keys-           $ m-  where-    cellUpRight :: N -> C-    cellUpRight = fromCoord . toCoord---- | 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"--    start = fmap (const $ Left [1..]) m-    colour' = mapM_ pickAndFill (Map.keys m)--    -- 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 ()--    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 ()--    -- 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'--colour :: Eq a => Grid C a -> Grid C Int-colour m = colourM edgeNeighbours' m-  where-    edgeNeighbours' p = [ q | q <- edgeNeighbours p-                            , q `Map.member` m ]---- | 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)--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)--outsideSize :: OutsideClues k a -> Size-outsideSize (OC l r b t) = (w, h)-  where-    w = max (length t) (length b)-    h = max (length l) (length r)---- | Create a dummy grid matching the given outside clues in size.-outsideGrid :: (Ord k, FromCoord k) => OutsideClues k a -> Grid k ()-outsideGrid = sizeGrid . outsideSize---- | 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] ]--data OClue = OClue-    { ocBase :: (Int, Int)-    , _ocDir :: (Int, Int)-    }-  deriving (Show, Eq, Ord)--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--outsideClues :: (Ord k, FromCoord k) => OutsideClues k a -> Map.Map k a-outsideClues = Map.mapKeys (fromCoord . ocBase) . oClues--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--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) ]--collectLines :: (Key k, Eq a) => Grid k (Maybe a) -> [Edge k]-collectLines = dualEdgesP eq-  where-    eq (Just x) (Just y) = x == y-    eq _        _        = False--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-    s = y - x--size :: Grid Coord a -> Size-size m = foldr (both max) (0, 0) (Map.keys m) ^+^ (1, 1)-  where-    both f (x, y) (x', y') = (f x x', f y y')
− src/Data/Puzzles/GridShape.hs
@@ -1,219 +0,0 @@-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ConstraintKinds #-}---- | Grid shapes.-module Data.Puzzles.GridShape-    (-      Coord-    , Size-    , Square(..)-    , Dir(..)-    , Edge(..)-    , Dir'(..)-    , Edge'(..)-    , Dual2D(..)-    , Key-    , Dual'-    , C(..)-    , N(..)-    , FromCoord(..)-    , ToCoord(..)--    , 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 qualified Data.Map as Map-import Data.AffineSpace--type Coord = (Int, Int)--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-    deriving (Show, Eq)--squareNeighbours :: [(Int,Int)] -> C -> [C]-squareNeighbours deltas c = map (c .+^) deltas--vertexNeighbours :: C -> [C]-vertexNeighbours = squareNeighbours [ (dx, dy)-                                    | dx <- [-1..1], dy <- [-1..1]-                                    , 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 = Vert | Horiz-    deriving (Eq, Ord, Show)---- | An edge in a square grid, going up or right from the given cell---   centre.-data Edge a = E a Dir-    deriving (Show, Eq, Ord)--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.-data Edge' a = E' a Dir'-    deriving (Eq, Ord, Show)--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 :: (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)] ]-        (ni, no) = partition isc nbrs-        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
@@ -1,174 +0,0 @@-{-# LANGUAGE FlexibleContexts, RankNTypes #-}---- |--- List of specific puzzle types.--module Data.Puzzles.PuzzleTypes (-    PuzzleType(..)-  , lookupType-  , typeNames-  ) where--import Data.Tuple (swap)---- | The list of specific puzzle types we can handle.-data PuzzleType = LITS-                | LITSPlus-                | LITSSym-                | Geradeweg-                | Fillomino-                | Masyu-                | Nurikabe-                | LatinTapa-                | Sudoku-                | ThermoSudoku-                | Pyramid-                | RowKropkiPyramid-                | SlitherLink-                | SlitherLinkLiar-                | TightfitSkyscrapers-                | WordLoop-                | WordSearch-                | CurveData-                | DoubleBack-                | Slalom-                | Compass-                | BoxOf2Or3-                | AfternoonSkyscrapers-                | MeanderingNumbers-                | Tapa-                | JapaneseSums-                | Coral-                | MaximalLengths-                | 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")-            , (Nurikabe, "nurikabe")-            , (LatinTapa, "latintapa")-            , (Sudoku, "sudoku")-            , (ThermoSudoku, "thermosudoku")-            , (Pyramid, "pyramid")-            , (RowKropkiPyramid, "rowkropkipyramid")-            , (SlitherLink, "slitherlink")-            , (SlitherLinkLiar, "slitherlinkliar")-            , (TightfitSkyscrapers, "skyscrapers-tightfit")-            , (WordLoop, "wordloop")-            , (WordSearch, "wordsearch")-            , (CurveData, "curvedata")-            , (DoubleBack, "doubleback")-            , (Slalom, "slalom")-            , (Compass, "compass")-            , (BoxOf2Or3, "boxof2or3")-            , (AfternoonSkyscrapers, "afternoonskyscrapers")-            , (MeanderingNumbers, "meanderingnumbers")-            , (Tapa, "tapa")-            , (JapaneseSums, "japanesesums")-            , (Coral, "coral")-            , (MaximalLengths, "maximallengths")-            , (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.-lookupType :: String -> Maybe PuzzleType-lookupType t = lookup t (map swap typeNames)
− src/Data/Puzzles/Pyramid.hs
@@ -1,129 +0,0 @@--- | Data types and parsing for pyramid puzzles.-module Data.Puzzles.Pyramid (-    Row(..),-    Pyramid(..),-    PyramidSol(..),-    KropkiRow(..),-    RowKropkiPyramid(..),-    mergepyramidsol,-    mergekpyramidsol,-    plainpyramid,-    psize-  ) where--import Data.Char (digitToInt)-import Text.ParserCombinators.Parsec hiding ((<|>), many)-import Control.Monad (liftM2, mplus)-import Data.Yaml hiding (Parser)-import qualified Data.Yaml as Yaml-import qualified Data.Text as T-import Control.Applicative--import Data.Puzzles.Elements--data Row = R { entries :: [Maybe Int]-             , shaded  :: Bool-             }--newtype Pyramid = Pyr {unPyr :: [Row]}--newtype PyramidSol = PyramidSol [[Int]]-    deriving Show---- | The size (number of rows) of a pyramid.-psize :: Pyramid -> Int-psize (Pyr rows) = length rows---- | Merge a solution into a pyramid.-mergepyramidsol :: Pyramid -> PyramidSol -> Pyramid-mergepyramidsol (Pyr rs) (PyramidSol qs)-    | length rs /= length qs  = error "can't merge differently sized pyramids"-    | otherwise               = Pyr (zipWith mergerow rs qs)-    where mergerow (R es s) es' = R (zipWith mplus es (map Just es')) s---- | Merge a solution into a kropki pyramid.-mergekpyramidsol :: RowKropkiPyramid -> PyramidSol -> RowKropkiPyramid-mergekpyramidsol (KP rs) (PyramidSol qs)-    | length rs /= length qs  = error "can't merge differently sized pyramids"-    | otherwise               = KP (zipWith mergerow rs qs)-    where mergerow (KR es s ds) es' =-              KR (zipWith mplus es (map Just es')) s ds--prow :: GenParser Char st Row-prow = do s <- pshaded-          spaces-          es <- pclues-          return (R es s)--pplainrow :: GenParser Char st [Int]-pplainrow = many (spaces >> fmap digitToInt digit)--pshaded :: GenParser Char st Bool-pshaded = (char 'G' >> return True) <|> (char 'W' >> return False)--pclues :: GenParser Char st [Maybe Int]-pclues = do c <- pclue-            cs <- many (spaces >> pclue)-            return (c:cs)--pclue :: GenParser Char st (Maybe Int)-pclue = fmap (Just . digitToInt) digit-        <|> (char '.' >> return Nothing)--showClues :: [Maybe Int] -> String-showClues = map showClue-    where showClue = maybe '.' (head . show)--instance Show Row where-    show (R c True) = 'G' : showClues c-    show (R c False) = 'W' : showClues c--instance Show Pyramid where-    show = unlines . map show . unPyr--data KropkiRow = KR { entriesk :: [Maybe Int]-                    , shadedk  :: Bool-                    , dotsk    :: [KropkiDot]-                    }-    deriving Show--newtype RowKropkiPyramid = KP {unKP :: [KropkiRow]}-    deriving Show---- | Forget the kropki dots.-plainpyramid :: RowKropkiPyramid -> Pyramid-plainpyramid (KP rows) = Pyr (map r rows)-    where r x = R (entriesk x) (shadedk x)--pkropkirow :: GenParser Char st KropkiRow-pkropkirow = do s <- pshaded-                spaces-                (ks, cs) <- pkropkiclues-                return (KR cs s ks)--pkropkiclues :: GenParser Char st ([KropkiDot], [Maybe Int])-pkropkiclues = do c <- pclue-                  kcs <- many (liftM2 (,) pkropki pclue)-                  let (ks, cs) = unzip kcs in return (ks, c:cs)--pkropki :: GenParser Char st KropkiDot-pkropki = (char '*' >> return KBlack)-          <|> (char 'o' >> return KWhite)-          <|> (char ' ' >> return KNone)--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--instance FromJSON Pyramid where-    parseJSON (String t) = Pyr <$> mapM (toParser prow . T.unpack) (T.lines t)-    parseJSON _          = empty--instance FromJSON RowKropkiPyramid where-    parseJSON (String t) = KP <$> mapM (toParser pkropkirow . T.unpack) (T.lines t)-    parseJSON _          = empty--instance FromJSON PyramidSol where-    parseJSON (String t) = PyramidSol <$>-                             mapM (toParser pplainrow . T.unpack) (T.lines t)-    parseJSON _          = empty
− src/Data/Puzzles/Sudoku.hs
@@ -1,36 +0,0 @@-module Data.Puzzles.Sudoku (-    sudokuborders,-    sudokubordersg-  ) where--import qualified Data.Map as Map--import Data.Puzzles.Grid-import Data.Puzzles.GridShape--msqrt :: Integral a => a -> Maybe a-msqrt x = if r ^ (2 :: Int) == x then Just r else Nothing-    where r = round . (sqrt :: Double -> Double) . fromIntegral $ x--mhalf :: Integral a => a -> Maybe a-mhalf x = if even x then Just (x `div` 2) else Nothing---- | Determine the internal borders of a standard sudoku of the---   given size.-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 (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 :: Grid C a -> [Edge N]-sudokubordersg = sudokuborders-               . fst . size . Map.mapKeys toCoord
− src/Data/Puzzles/Util.hs
@@ -1,32 +0,0 @@-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/Data/Pyramid.hs view
@@ -0,0 +1,129 @@+-- | Data types and parsing for pyramid puzzles.+module Data.Pyramid (+    Row(..),+    Pyramid(..),+    PyramidSol(..),+    KropkiRow(..),+    RowKropkiPyramid(..),+    mergepyramidsol,+    mergekpyramidsol,+    plainpyramid,+    psize+  ) where++import Data.Char (digitToInt)+import Text.ParserCombinators.Parsec hiding ((<|>), many)+import Control.Monad (liftM2, mplus)+import Data.Yaml hiding (Parser)+import qualified Data.Yaml as Yaml+import qualified Data.Text as T+import Control.Applicative++import Data.Elements++data Row = R { entries :: [Maybe Int]+             , shaded  :: Bool+             }++newtype Pyramid = Pyr {unPyr :: [Row]}++newtype PyramidSol = PyramidSol [[Int]]+    deriving Show++-- | The size (number of rows) of a pyramid.+psize :: Pyramid -> Int+psize (Pyr rows) = length rows++-- | Merge a solution into a pyramid.+mergepyramidsol :: Pyramid -> PyramidSol -> Pyramid+mergepyramidsol (Pyr rs) (PyramidSol qs)+    | length rs /= length qs  = error "can't merge differently sized pyramids"+    | otherwise               = Pyr (zipWith mergerow rs qs)+    where mergerow (R es s) es' = R (zipWith mplus es (map Just es')) s++-- | Merge a solution into a kropki pyramid.+mergekpyramidsol :: RowKropkiPyramid -> PyramidSol -> RowKropkiPyramid+mergekpyramidsol (KP rs) (PyramidSol qs)+    | length rs /= length qs  = error "can't merge differently sized pyramids"+    | otherwise               = KP (zipWith mergerow rs qs)+    where mergerow (KR es s ds) es' =+              KR (zipWith mplus es (map Just es')) s ds++prow :: GenParser Char st Row+prow = do s <- pshaded+          spaces+          es <- pclues+          return (R es s)++pplainrow :: GenParser Char st [Int]+pplainrow = many (spaces >> fmap digitToInt digit)++pshaded :: GenParser Char st Bool+pshaded = (char 'G' >> return True) <|> (char 'W' >> return False)++pclues :: GenParser Char st [Maybe Int]+pclues = do c <- pclue+            cs <- many (spaces >> pclue)+            return (c:cs)++pclue :: GenParser Char st (Maybe Int)+pclue = fmap (Just . digitToInt) digit+        <|> (char '.' >> return Nothing)++showClues :: [Maybe Int] -> String+showClues = map showClue+    where showClue = maybe '.' (head . show)++instance Show Row where+    show (R c True) = 'G' : showClues c+    show (R c False) = 'W' : showClues c++instance Show Pyramid where+    show = unlines . map show . unPyr++data KropkiRow = KR { entriesk :: [Maybe Int]+                    , shadedk  :: Bool+                    , dotsk    :: [KropkiDot]+                    }+    deriving Show++newtype RowKropkiPyramid = KP {unKP :: [KropkiRow]}+    deriving Show++-- | Forget the kropki dots.+plainpyramid :: RowKropkiPyramid -> Pyramid+plainpyramid (KP rows) = Pyr (map r rows)+    where r x = R (entriesk x) (shadedk x)++pkropkirow :: GenParser Char st KropkiRow+pkropkirow = do s <- pshaded+                spaces+                (ks, cs) <- pkropkiclues+                return (KR cs s ks)++pkropkiclues :: GenParser Char st ([KropkiDot], [Maybe Int])+pkropkiclues = do c <- pclue+                  kcs <- many (liftM2 (,) pkropki pclue)+                  let (ks, cs) = unzip kcs in return (ks, c:cs)++pkropki :: GenParser Char st KropkiDot+pkropki = (char '*' >> return KBlack)+          <|> (char 'o' >> return KWhite)+          <|> (char ' ' >> return KNone)++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++instance FromJSON Pyramid where+    parseJSON (String t) = Pyr <$> mapM (toParser prow . T.unpack) (T.lines t)+    parseJSON _          = empty++instance FromJSON RowKropkiPyramid where+    parseJSON (String t) = KP <$> mapM (toParser pkropkirow . T.unpack) (T.lines t)+    parseJSON _          = empty++instance FromJSON PyramidSol where+    parseJSON (String t) = PyramidSol <$>+                             mapM (toParser pplainrow . T.unpack) (T.lines t)+    parseJSON _          = empty
+ src/Data/Sudoku.hs view
@@ -0,0 +1,36 @@+module Data.Sudoku (+    sudokuborders,+    sudokubordersg+  ) where++import qualified Data.Map.Strict as Map++import Data.Grid+import Data.GridShape++msqrt :: Integral a => a -> Maybe a+msqrt x = if r ^ (2 :: Int) == x then Just r else Nothing+    where r = round . (sqrt :: Double -> Double) . fromIntegral $ x++mhalf :: Integral a => a -> Maybe a+mhalf x = if even x then Just (x `div` 2) else Nothing++-- | Determine the internal borders of a standard sudoku of the+--   given size.+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 (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 :: Grid C a -> [Edge N]+sudokubordersg = sudokuborders+               . fst . size . Map.mapKeys toCoord
+ src/Data/Util.hs view
@@ -0,0 +1,32 @@+module Data.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
@@ -1,45 +0,0 @@-{-# LANGUAGE FlexibleContexts, CPP #-}--module Diagrams.Puzzles.CmdLine -    ( B-    , renderToFile-    , RenderOpts(..)-    , formats-    , checkFormat-    )-  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)-#else-import Diagrams.Backend.SVG (B, renderSVG)-#endif--data RenderOpts = RenderOpts { _file :: FilePath, _w :: Double }--renderB :: FilePath -> SizeSpec V2 Double -> Diagram B -> IO ()-renderB =-#ifdef CAIRO-    renderCairo-#else-    renderSVG-#endif--renderToFile :: RenderOpts -> Diagram B -> IO ()-renderToFile ropts = renderB (_file ropts) (mkWidth $ _w ropts)--formats :: [String]-#ifdef CAIRO-formats = ["png", "svg", "ps", "pdf"]-#else-formats = ["svg"]-#endif--checkFormat :: String -> IO ()-checkFormat f = unless (f `elem` formats) $-                    exitErr $ "unknown format: " ++ f
− src/Diagrams/Puzzles/Code.hs
@@ -1,53 +0,0 @@-{-# 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
@@ -1,79 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ConstraintKinds #-}--module Diagrams.Puzzles.Draw (-    PuzzleSol,-    RenderPuzzle,-    OutputChoice(..),-    draw,-    Unit(..),-    diagramWidth,-    toOutputWidth,-  ) where--import Diagrams.Prelude--import Diagrams.Puzzles.Lib-import Diagrams.Puzzles.Widths-import Diagrams.Puzzles.Code--type RenderPuzzle b p s = (p -> Diagram b, (p, s) -> Diagram b)--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-     => Maybe (CodeDiagrams (Diagram b))-     -> PuzzleSol b -> OutputChoice -> Maybe (Diagram b)-draw mc (p, ms) = fmap (bg white) . d-  where-    fixup = alignPixel . border borderwidth-    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 :: Backend' b => Diagram b -> Double-diagramWidth = fst . unr2 . boxExtents . boundingBox--toOutputWidth :: Unit -> Double -> Double-toOutputWidth u w = case u of Pixels -> fromIntegral wpix-                              Points -> wpt-  where-    wpix = round (gridresd * w) :: Int  -- grid square size 40px-    wpt = cmtopoint w     -- grid square size 1.0cm--alignPixel :: Backend' b => Diagram b -> Diagram b-alignPixel = scale (1/gridresd) . align' . scale gridresd-  where-    align' d = maybe id grow (getCorners $ boundingBox d) d-    grow (bl, tr) = mappend $ phantoml (nudge bl False) (nudge tr True)-    nudge p dir = let (px, py) = unp2 p in p2 (nudge' px dir, nudge' py dir)-    nudge' x True  = fromIntegral (ceiling (x - 0.5) :: Int) + 0.5-    nudge' x False = fromIntegral (floor   (x + 0.5) :: Int) - 0.5-    phantoml p q = phantom' $ p ~~ q---- | Add a phantom border of the given width around a diagram.-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
@@ -1,350 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ConstraintKinds #-}---- | Module: Diagrams.TwoD.Puzzles.Elements------ Tools to draw individual puzzle components. In particular--- contents and decorations for individual cells.--module Diagrams.Puzzles.Elements where--import Diagrams.Prelude hiding (N)-import Diagrams.TwoD.Offset--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-pearl m = circle 0.35 # lwG 0.05 # fc (c m)-  where-    c MWhite = white-    c MBlack = black--smallPearl :: Backend' b =>-              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 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 V2 Double-dr = fromVertices [p2 (1/2,-1/2), p2 (-1/2,1/2)]---- | Both diagonals of a centered unit square.-cross :: Path V2 Double-cross = ur <> dr---- | Draw a cross.-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-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)-          texts = mconcat . zipWith tx [n, e, s, w] $-                  [(0,f), (f,0), (0,-f), (-f,0)]-          f = 3/10--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-          col = lc gr . fc gr-          gr = blend 0.6 white black-thermo [] = error "invalid empty thermometer"---- | Draw a list of thermometers, given as lists of @(Int, Int)@ cell--- coordinates.-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) -> 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))-                       <> d y # scale s # translate (r2 (t,-t))-    where t = 1/5-          s = 2/3-drawTight d (DR x y) = stroke dr # lwG onepix-                       <> d x # scale s # translate (r2 (-t,-t))-                       <> d y # scale s # translate (r2 (t,t))-    where t = 1/5-          s = 2/3---- | Stack the given words, left-justified.-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 -> 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] -> 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-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 -> Diagram b-drawText = text'--drawTextFixed :: Backend' b => String -> Diagram b-drawTextFixed = textFixed---- | Draw an @Int@.-drawInt :: Backend' b =>-           Int -> Diagram b-drawInt s = drawText (show s)---- | Draw a character.-drawChar :: Backend' b =>-            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-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 N] -> Diagram b-drawCurve = lwG onepix . fit 0.6 . centerXY . mconcat . map (stroke . edge)---- | Draw a shadow in the style of Afternoon Skyscrapers.-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)) (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-drawTapaClue (TapaClue [x]) = drawInt x-drawTapaClue (TapaClue xs)  = fit 0.8-                            . atPoints (p (length xs))-                            . map drawInt-                            $ xs-  where-    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-    p' 1 = error "singleton clues handled separately"-    p' _ = error "invalid tapa clue"--drawPrimeDiag :: Backend' b =>-                 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-                  (True,  False) -> ur-                  (False, True)  -> dr-                  (True,  True)  -> ur <> dr--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-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
@@ -1,182 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, TypeFamilies #-}-{-# LANGUAGE ConstraintKinds #-}--module Diagrams.Puzzles.Grid where--import Data.Char (isUpper)-import qualified Data.Map as M-import qualified Data.Set as S--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 (edge)--import Diagrams.Puzzles.Style-import Diagrams.Puzzles.Lib-import Diagrams.Puzzles.Widths--(.--.) :: AS.AffineSpace p => p -> p -> AS.Diff p-(.--.) = (AS..-.)--class ToPoint a where-    toPoint :: a -> P2 Double--instance ToPoint C where-    toPoint c = p2 (1/2, 1/2) .+^ r2i (c .--. C 0 0)--instance ToPoint N where-    toPoint c = origin .+^ r2i (c .--. N 0 0)---- | Draw a small black dot with no envelope.-dot :: Backend' b => Diagram b-dot = circle 0.05 # fc black # smash--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--    (outer, inner) = irregularGridPaths g--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, 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]--dashoffset :: Double-dashoffset = 2.5 / 40--gridDashing :: (Semigroup a, HasStyle a, InSpace V2 Double a) => a -> a-gridDashing = bgdashingG dashes dashoffset white'-  where-    white' = black `withOpacity` (0.05 :: Double)---- | `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)-    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--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 = S.fromList . mconcat . pathVertices $ pouter-    inner = S.fromList . mconcat . pathVertices $ pinner--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)--placeGrid :: (ToPoint k, HasOrigin a, Monoid a, InSpace V2 Double a)-          => Grid k a -> a-placeGrid = M.foldMapWithKey (moveTo . toPoint)--placeGrid' :: (HasOrigin a, Monoid a, InSpace V2 Double a)-          => Grid (P2 Double) a -> a-placeGrid' = M.foldMapWithKey moveTo--edge :: (ToPoint k) => Edge k -> Path V2 Double-edge (E c d) = rule d # translate (toPoint c .-. origin)-  where-    rule Vert = vrule 1.0 # alignB-    rule Horiz = hrule 1.0 # alignL--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, InSpace V2 Double a) => a -> a-edgeStyle = lineCap LineCapSquare . lwG edgewidth--thinEdgeStyle :: (HasStyle a, InSpace V2 Double a) => a -> a-thinEdgeStyle = lineCap LineCapSquare . lwG onepix--drawEdges :: (ToPoint k, Backend' b) => [Edge k] -> Diagram b-drawEdges = edgeStyle . stroke . mconcat . map edge--drawThinEdges :: (ToPoint k, Backend' b) => [Edge k] -> Diagram b-drawThinEdges = thinEdgeStyle . stroke . mconcat . map edge--drawAreas :: (Backend' b, Eq a) =>-             Grid C a -> Diagram b-drawAreas = drawEdges . borders--fillBG :: Backend' b => Colour Double -> Diagram b-fillBG c = square 1 # lwG onepix # fc c # lc c--shadeGrid :: Backend' b =>-             Grid C (Maybe (Colour Double)) -> Diagram b-shadeGrid = placeGrid . fmap fillBG . clues--drawShade :: Backend' b =>-             Grid C Bool -> Diagram b-drawShade = shadeGrid . fmap f-  where-    f True  = Just gray-    f False = Nothing--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---- Place a list of diagrams along a ray, with steps of size--- @f@.-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
− src/Diagrams/Puzzles/Lib.hs
@@ -1,153 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ConstraintKinds #-}--module Diagrams.Puzzles.Lib where--import Diagrams.Path (pathPoints)-import Diagrams.Prelude--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 = (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-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 :: (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 :: (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 => Diagram b -> Diagram b-smash = withEnvelope (pointDiagram origin :: D V2 Double)---- | Helper to translate by a point given as @(Int, Int)@.-translatep :: (InSpace V2 Double t, Transformable t)-           => (Int, Int) -> t -> t-translatep = translate . r2i---- | Convert pair of @Int@ to vector.-r2i :: (Int, Int) -> V2 Double-r2i = r2 . (fromIntegral *** fromIntegral)---- | Convert pair of @Int@ to point.-p2i :: (Int, Int) -> P2 Double-p2i = p2 . (fromIntegral *** fromIntegral)--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 => 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 ::  (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 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 =>-            Diagram b -> Diagram b -> Diagram b-besidesL a b = a ||| strutX 0.5 ||| b'-    where b' = b # centerY # translate (dmid unitY a *^ unitY)---- | Variant of 'besidesL' where the origin is that of the right diagram.-besidesR :: Backend' b =>-           Diagram b  -> Diagram b -> Diagram b-besidesR b a =  b' ||| strutX 0.5 ||| a-    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, InSpace V2 Double t) =>-       Double -> t -> t-fit f a = scale (f / m) 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 => 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-    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---textrect t = rect (fromIntegral (length t) * 0.4) 0.7 # lc red---text'' :: Backend' b => String -> Diagram b R2---text'' t = text' t `atop` textrect t---- | Variant of 'phantom' that forces the argument backend type.-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
@@ -1,93 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses     #-}-{-# LANGUAGE FlexibleContexts          #-}-{-# LANGUAGE TypeFamilies              #-}-{-# LANGUAGE ConstraintKinds           #-}--module Diagrams.Puzzles.PuzzleGrids-    (-      drawIntGrid-    , drawCharGrid-    , outsideIntGrid-    , drawSlitherGrid-    , drawTightGrid-    , sudokugrid-    , drawWordsClues-    , drawOutsideGrid-    , drawMultiOutsideGrid-    , drawOutsideGridN-    , drawMultiOutsideGridN-    , placeMultiOutside-    ) where--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.Elements--drawCharGrid :: Backend' b =>-                Grid C (Maybe Char) -> Diagram b-drawCharGrid = placeGrid . fmap drawChar . clues <> grid gDefault--drawIntGrid :: Backend' b =>-               Grid C (Maybe Int) -> Diagram b-drawIntGrid = placeGrid . fmap drawInt . clues  <> grid gDefault--drawSlitherGrid :: Backend' b =>-                   Grid C (Maybe Int) -> Diagram b-drawSlitherGrid = placeGrid . fmap  drawInt . clues <> grid gSlither--sudokugrid :: Backend' b =>-              Grid C a -> Diagram b-sudokugrid = drawEdges . sudokubordersg <> grid gDefault--drawWordsClues :: Backend' b =>-                  Grid C (Maybe [String]) -> Diagram b-drawWordsClues = placeGrid . fmap drawWords . clues--drawTightGrid :: Backend' b =>-                 (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)--placeMultiOutside :: (Ord k, FromCoord k, ToPoint k,-                      HasOrigin a, Monoid a,-                      InSpace V2 Double a)-                  => OutsideClues k [a] -> a-placeMultiOutside = placeGrid . multiOutsideClues--placeOutside :: (Ord k, FromCoord k, ToPoint k,-                 HasOrigin a, Monoid a,-                 InSpace V2 Double a)-             => OutsideClues k (Maybe a) -> a-placeOutside = placeMultiOutside . fmap maybeToList--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--drawMultiOutsideGrid :: Backend' b => OutsideClues C [String] -> Diagram b-drawMultiOutsideGrid = placeMultiOutside . fmap (fmap (scale 0.8 . drawText))-                     <> grid gDefault . outsideGrid--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
@@ -1,757 +0,0 @@--{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ConstraintKinds #-}--module Diagrams.Puzzles.PuzzleTypes (-    lits, litsplus, geradeweg, fillomino, masyu, nurikabe, latintapa,-    sudoku, thermosudoku, pyramid, kpyramid, slither,-    liarslither, tightfitskyscrapers, wordloop, wordsearch,-    curvedata, doubleback, slalom, compass, boxof2or3,-    afternoonskyscrapers, meanderingnumbers, tapa, japanesesums,-    coral, maximallengths, primeplace, labyrinth, bahnhof,-    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, 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-import qualified Diagrams.Puzzles.Pyramid as DPyr-import Diagrams.Puzzles.Elements-import Diagrams.Puzzles.Lib-import Diagrams.Puzzles.Widths--import Data.Puzzles.Grid-import Data.Puzzles.GridShape-import Data.Puzzles.Elements-import qualified Data.Puzzles.Pyramid as Pyr--lits :: Backend' b => RenderPuzzle b AreaGrid ShadedGrid-lits = (,)-    (grid gDefault <> drawAreasGray)-    ((drawAreas <> grid gDefault) . fst <> drawShade . snd)--litsplus :: Backend' b => RenderPuzzle b AreaGrid ShadedGrid-litsplus = lits--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 (Grid C (Maybe Int)) (Loop C)-geradeweg = (,)-    drawIntGrid-    (placeGrid . fmap drawInt . clues . fst-     <> solstyle . drawEdges . snd-     <> grid gDefault . fst)--fillomino :: Backend' b => RenderPuzzle b (Grid C (Maybe Int)) (Grid C Int)-fillomino = (,)-    (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 (Grid C (Maybe MasyuPearl)) (Loop C)-masyu = (,)-    p-    (solstyle . drawEdges . snd <> p . fst)-  where-    p = placeGrid . fmap pearl . clues <> grid gDefault--nurikabe :: Backend' b =>-            RenderPuzzle b (Grid C (Maybe Int)) ShadedGrid-nurikabe = (,)-    drawIntGrid-    (drawIntGrid . fst <> drawShade . snd)--latintapa :: Backend' b =>-             RenderPuzzle b (Grid C (Maybe [String])) (Grid C (Maybe Char))-latintapa = (,)-    l-    (l . fst <> placeGrid . fmap drawChar . clues . snd)-  where-    l = grid gDefault <> drawWordsClues--sudoku :: Backend' b =>-          RenderPuzzle b (Grid C (Maybe Int)) (Grid C (Maybe Int))-sudoku = (,)-    (placeGrid . fmap drawInt . clues <> sudokugrid)-    ((placeGrid . fmap drawInt . clues <> sudokugrid) . snd)--thermosudoku :: Backend' b =>-                RenderPuzzle b (Grid C (Maybe Int), [Thermometer]) (Grid C (Maybe Int))-thermosudoku = (,)-    (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-pyramid = (,)-    DPyr.pyramid-    (DPyr.pyramid . merge)-  where-    merge (p, q) = Pyr.mergepyramidsol p q--kpyramid :: Backend' b =>-    RenderPuzzle b Pyr.RowKropkiPyramid Pyr.PyramidSol-kpyramid = (,)-    DPyr.kpyramid-    (DPyr.kpyramid . merge)-  where-    merge (p, q) = Pyr.mergekpyramidsol p q--slither :: Backend' b =>-           RenderPuzzle b (Grid C (Maybe Int)) (Loop N)-slither = (,)-    drawSlitherGrid-    (drawSlitherGrid . fst <> solstyle . drawEdges . snd)--liarslither :: Backend' b =>-               RenderPuzzle b (Grid C (Maybe Int)) (Loop N, Grid C Bool)-liarslither = (,)-    drawSlitherGrid-    (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 C (Maybe Int), Grid C (Tightfit ()))-                                      (Grid C (Tightfit Int))-tightfitskyscrapers = (,)-    (placeGrid . fmap drawInt . clues . outsideClues . fst-     <> drawTightGrid (const mempty) . snd)-    (placeGrid . fmap drawInt . clues . outsideClues . fst . fst-     <> drawTightGrid drawInt . snd)--wordgrid :: Backend' b =>-            Grid C (Maybe Char) -> [String] -> Diagram b-wordgrid g ws = stackWords ws `besidesR` drawCharGrid g--wordloop :: Backend' b =>-            RenderPuzzle b (Grid C (Maybe Char), [String]) (Grid C (Maybe Char))-wordloop = (,)-    (uncurry wordgrid)-    (drawCharGrid . snd)--wordsearch :: Backend' b =>-              RenderPuzzle b (Grid C (Maybe Char), [String])-                             (Grid C (Maybe Char), [MarkedWord])-wordsearch = (,)-    (uncurry wordgrid) -    (solstyle . drawMarkedWords . snd . snd-     <> drawCharGrid . fst . snd)--curvedata :: Backend' b =>-             RenderPuzzle b (Grid C (Maybe [Edge N])) [Edge C]-curvedata = (,)-    cd-    ((solstyle . drawEdges . snd) <> cd . fst)-  where-    cd = placeGrid . fmap drawCurve . clues <> grid gDefault--doubleback :: Backend' b =>-              RenderPuzzle b AreaGrid (Loop C)-doubleback = (,)-    p-    (solstyle . drawEdges . snd <> p . fst)-  where-    p = grid gDefault <> drawAreasGray--slalom :: Backend' b =>-          RenderPuzzle b (Grid N (Maybe Int)) (Grid C SlalomDiag)-slalom = (,)-    p-    (p . fst <> placeGrid . fmap (solstyle . drawSlalomDiag) . snd)-  where-    p = placeGrid . fmap drawSlalomClue . clues-        <> grid gDefault . cellGrid--compass :: Backend' b =>-           RenderPuzzle b (Grid C (Maybe CompassC)) AreaGrid-compass = (,)-    (placeGrid . fmap drawCompassClue . clues <> grid gDashed)-    (placeGrid . fmap drawCompassClue . clues . fst-     <> (grid gDashed <> drawAreasGray) . snd)--boxof2or3 :: Backend' b =>-             RenderPuzzle b (Grid N MasyuPearl, [Edge N]) ()-boxof2or3 = (,)-    (placeGrid . fmap smallPearl . fst-     <> drawThinEdges . snd)-    (error "boxof2or3 solution not implemented")--afternoonskyscrapers :: Backend' b =>-                        RenderPuzzle b (Grid C Shade) (Grid C (Maybe Int))-afternoonskyscrapers = (,)-    (grid gDefault <> placeGrid . fmap drawShadow)-    (drawIntGrid . snd <> placeGrid . fmap drawShadow . fst)--meanderingnumbers :: Backend' b =>-                        RenderPuzzle b AreaGrid (Grid C (Maybe Int))-meanderingnumbers = (,)-    (grid gDefault <> drawAreas)-    (drawIntGrid . snd <> drawAreas . fst)--tapa :: Backend' b =>-        RenderPuzzle b (Grid C (Maybe TapaClue)) ShadedGrid-tapa = (,)-    tapaGrid-    (tapaGrid . fst <> drawShade . snd)-  where-    tapaGrid = placeGrid . fmap drawTapaClue . clues <> grid gDefault--japanesesums :: Backend' b =>-                RenderPuzzle b (OutsideClues C [Int], String)-                               (Grid C (Either Black Int))-japanesesums = (,)-    (outsideIntGrid . fst <> n)-    (outsideIntGrid . fst . fst <> japcells . snd)-  where-    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 C [String]) ShadedGrid-coral = (,)-    drawMultiOutsideGrid-    (drawMultiOutsideGrid . fst <> drawShade . snd)--maximallengths :: Backend' b =>-                  RenderPuzzle b (OutsideClues C (Maybe Int)) (Loop C)-maximallengths = (,)-    g-    (solstyle . drawEdges . snd <> g . fst)-  where-    g = placeGrid . fmap drawInt . clues . outsideClues-        <> grid gDefault . outsideGrid--primeplace :: Backend' b =>-              RenderPuzzle b (Grid C PrimeDiag) (Grid C Int)-primeplace = (,)-    g-    (placeGrid . fmap drawInt . snd <> g . fst)-  where-    g = grid gStyle-        <> placeGrid . fmap drawPrimeDiag-    gStyle = GridStyle LineThin LineThick Nothing VertexNone--labyrinth :: Backend' b =>-             RenderPuzzle b (Grid C (Maybe Int), [Edge N]) (Grid C (Maybe Int))-labyrinth = (,)-    (placeGrid . fmap drawInt . clues . fst <> g)-    (placeGrid . fmap drawInt . clues . snd <> g . fst)-  where-    g = drawEdges . snd <> grid gPlain . fst--bahnhof :: Backend' b =>-            RenderPuzzle b (Grid C (Maybe BahnhofClue)) [Edge C]-bahnhof = (,)-    (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 =>-    RenderPuzzle b (Grid C (Maybe Int)) ShadedGrid-cave = (,)-    (grid gDashDash <> placeGrid . fmap drawInt . clues)-    (drawEdges . edgesGen (/=) not . snd-     <> placeGrid . fmap drawInt . clues . fst-     <> drawShade . snd-     <> grid gStyle . fst)-  where-    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
@@ -1,41 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE TypeFamilies #-}--module Diagrams.Puzzles.Pyramid where--import Diagrams.Prelude--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-cell s = square 1 # lwG onepix # if s then fc pgray else id--clue :: Backend' b => Maybe Int -> Diagram b-clue Nothing = mempty-clue (Just c) = text' (show c)--cellc :: Backend' b => Bool -> Maybe Int -> Diagram b-cellc s c = clue c `atop` cell s--row :: Backend' b => Row -> Diagram b-row (R cs s) = centerX . hcat . map (cellc s) $ cs--pyramid :: Backend' b => Pyramid -> Diagram b-pyramid = alignBL . vcat . map row . unPyr--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 kropkiDot ks)--kpyramid :: Backend' b => RowKropkiPyramid -> Diagram b-kpyramid = alignBL . vcat . map krow . unKP
− src/Diagrams/Puzzles/Style.hs
@@ -1,50 +0,0 @@-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
@@ -1,26 +0,0 @@-module Diagrams.Puzzles.Widths where--gridres :: Int-gridres = 40--gridresd :: Double-gridresd = fromIntegral gridres--onepix :: Double-onepix = 1 / fromIntegral gridres--twopix, fourpix :: Double-twopix = 2 * onepix-fourpix = 4 * onepix--gridwidth :: Double-gridwidth = onepix--framewidthfactor :: Double-framewidthfactor = 4--edgewidth :: Double-edgewidth = 3 * onepix--borderwidth :: Double-borderwidth = 1 / 2
+ src/Draw/CmdLine.hs view
@@ -0,0 +1,32 @@+{-# LANGUAGE FlexibleContexts #-}++module Draw.CmdLine +    ( B+    , renderToFile+    , RenderOpts(..)+    , formats+    , checkFormat+    )+  where++import Data.CmdLine (exitErr)++import Diagrams.Prelude hiding (value, option, (<>), Result)+import Control.Monad (unless)++import Diagrams.Backend.Rasterific (B, renderRasterific)++data RenderOpts = RenderOpts+  { _file :: FilePath+  , _size :: SizeSpec V2 Double+  }++renderToFile :: RenderOpts -> Diagram B -> IO ()+renderToFile ropts = renderRasterific (_file ropts) (_size ropts)++formats :: [String]+formats = ["png", "ps", "pdf"]++checkFormat :: String -> IO ()+checkFormat f = unless (f `elem` formats) $+                    exitErr $ "unknown format: " ++ f
+ src/Draw/Code.hs view
@@ -0,0 +1,75 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}++module Draw.Code+    (+      CodeDiagrams (..)+    , drawCode+    ) where++import Data.Code+import Data.GridShape+import Data.Grid+import Draw.Lib+import Draw.Grid+import Draw.Elements++import Diagrams.Prelude++import qualified Data.Map.Strict as Map++data CodeDiagrams a = CodeDiagrams { _cdLeft :: a, _cdTop :: a, _cdOver :: a }++instance Semigroup a => Semigroup (CodeDiagrams a) where+    (CodeDiagrams x y z) <> (CodeDiagrams x' y' z') =+        CodeDiagrams (x <> x') (y <> y') (z <> z')++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))+drawCodePart (LRows'  rs) = CodeDiagrams (placeGrid g # centerX) mempty mempty+  where+    g = Map.fromList [ (C 0 r, arrowRightL l) | (l, r) <- Map.toList rs ]+drawCodePart (LCols   cs) = CodeDiagrams mempty (placeGrid g # centerY) mempty+  where+    g = Map.fromList [ (C c 0, arrowDownL l)  | (l, c) <- Map.toList cs ]+drawCodePart (LRowsN' rs) = CodeDiagrams (placeGrid g # centerX) mempty mempty+  where+    g = Map.fromList [ (N 0 r, arrowRightL l) | (l, r) <- Map.toList rs ]+drawCodePart (LColsN  cs) = CodeDiagrams mempty (placeGrid g # centerY) mempty+  where+    g = Map.fromList [ (N c 0, arrowDownL l)  | (l, c) <- Map.toList cs ]++arrowDown :: Backend' b => Diagram b+arrowDown = triangle 0.5 # lwG 0 # fc black # rotateBy (1/2)++arrowDownL :: Backend' b => Char -> Diagram b+arrowDownL c = drawChar c # fc white # scale 0.5 <> arrowDown # scale 1.2++arrowRight :: Backend' b => Diagram b+arrowRight = arrowDown # rotateBy (1/4)++arrowRightL :: Backend' b => Char -> Diagram b+arrowRightL c = drawChar c # fc white # scale 0.5 # translate (r2 (-0.05,0)) <> arrowRight # scale 1.2
+ src/Draw/Draw.hs view
@@ -0,0 +1,89 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ConstraintKinds #-}++module Draw.Draw (+    PuzzleSol,+    Drawers(..),+    drawers,+    OutputChoice(..),+    render,+    Unit(..),+    diagramSize,+    toOutputWidth,+  ) where++import Diagrams.Prelude hiding (render)++import Draw.Lib+import Draw.Widths+import Draw.Code++type Config = ()++data Drawers b p s =+    Drawers+        { puzzle :: p -> Config -> Diagram b+        , solution :: (p, s) -> Config -> Diagram b+        }++drawers :: (p -> Diagram b) -> ((p, s) -> Diagram b) -> Drawers b p s+drawers p s = Drawers (const . p) (const . s)++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.+render :: Backend' b+     => Maybe (CodeDiagrams (Diagram b))+     -> PuzzleSol b -> OutputChoice -> Maybe (Diagram b)+render mc (p, ms) = fmap (bg white) . d+  where+    fixup = alignPixel . border borderwidth+    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)++diagramSize :: Backend' b => Diagram b -> (Double, Double)+diagramSize = unr2 . boxExtents . boundingBox++toOutputWidth :: Unit -> Double -> Double+toOutputWidth u w = case u of Pixels -> fromIntegral wpix+                              Points -> wpt+  where+    wpix = round (gridresd * w) :: Int  -- grid square size 40px+    wpt = cmtopoint w     -- grid square size 1.0cm++alignPixel :: Backend' b => Diagram b -> Diagram b+alignPixel = scale (1/gridresd) . align' . scale gridresd+  where+    align' d = maybe id grow (getCorners $ boundingBox d) d+    grow (bl, tr) = mappend $ phantoml (nudge bl False) (nudge tr True)+    nudge p dir = let (px, py) = unp2 p in p2 (nudge' px dir, nudge' py dir)+    nudge' x True  = fromIntegral (ceiling (x - 0.5) :: Int) + 0.5+    nudge' x False = fromIntegral (floor   (x + 0.5) :: Int) - 0.5+    phantoml p q = phantom' $ p ~~ q++-- | Add a phantom border of the given width around a diagram.+border :: Backend' b => Double -> Diagram b -> Diagram b+border w = extrudeEnvelope (w *^ unitX) . extrudeEnvelope (-w *^ unitX)+         . extrudeEnvelope (w *^ unitY) . extrudeEnvelope (-w *^ unitY)
+ src/Draw/Elements.hs view
@@ -0,0 +1,424 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ConstraintKinds #-}++-- | Module: Diagrams.TwoD.Puzzles.Elements+--+-- Tools to draw individual puzzle components. In particular+-- contents and decorations for individual cells.++module Draw.Elements where++import Diagrams.Prelude hiding (N)+import Diagrams.TwoD.Offset++import qualified Data.Map.Strict as Map+import Data.List (groupBy, sortOn)++import Data.Grid+import Data.Elements hiding (Loop)+import Data.GridShape hiding (edge)++import Draw.Lib+import Draw.Style+import Draw.Widths+import Draw.Grid++pearl :: Backend' b =>+         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+smallPearl = scale 0.4 . pearl++drawEnd :: Backend' b =>+       MEnd -> Diagram b+drawEnd MEnd = smallPearl MBlack++drawBigEnd :: Backend' b =>+       MEnd -> Diagram b+drawBigEnd MEnd = pearl MBlack++-- | The up-right diagonal of a centered unit square.+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 V2 Double+dr = fromVertices [p2 (1/2,-1/2), p2 (-1/2,1/2)]++-- | Both diagonals of a centered unit square.+cross :: Path V2 Double+cross = ur <> dr++-- | Draw a cross.+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+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)+          texts = mconcat . zipWith tx [n, e, s, w] $+                  [(0,f), (f,0), (0,-f), (-f,0)]+          f = 3/10++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+          col = lc gr . fc gr+          gr = blend 0.6 white black+thermo [] = error "invalid empty thermometer"++-- | Draw a list of thermometers, given as lists of @(Int, Int)@ cell+-- coordinates.+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) -> 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))+                       <> d y # scale s # translate (r2 (t,-t))+    where t = 1/5+          s = 2/3+drawTight d (DR x y) = stroke dr # lwG onepix+                       <> d x # scale s # translate (r2 (-t,-t))+                       <> d y # scale s # translate (r2 (t,t))+    where t = 1/5+          s = 2/3++-- | Stack the given words, left-justified.+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 -> 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] -> 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+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 -> Diagram b+drawText = text'++drawTextFixed :: Backend' b => String -> Diagram b+drawTextFixed = textFixed++-- | Draw an @Int@.+drawInt :: Backend' b =>+           Int -> Diagram b+drawInt s = drawText (show s)++-- | Draw a character.+drawChar :: Backend' b =>+            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++placeTL :: Backend' b => Diagram b -> Diagram b+placeTL = moveTo (p2 (-0.4,0.4)) . scale 0.5 . alignTL++hintTL :: Backend' b => String -> Diagram b+hintTL = placeTL . 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+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 N] -> Diagram b+drawCurve = lwG onepix . fit 0.6 . centerXY . mconcat . map (stroke . edge)++-- | Draw a shadow in the style of Afternoon Skyscrapers.+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)) (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+drawTapaClue (TapaClue [x]) = drawInt x+drawTapaClue (TapaClue xs)  = fit 0.8+                            . atPoints (p (length xs))+                            . map drawInt+                            $ xs+  where+    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+    p' 1 = error "singleton clues handled separately"+    p' _ = error "invalid tapa clue"++drawPrimeDiag :: Backend' b =>+                 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+                  (True,  False) -> ur+                  (False, True)  -> dr+                  (True,  True)  -> ur <> dr++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 = ((180 @@ deg) ^-^ acosA off ^-^ startAngle)++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++drawTree :: Backend' b => Tree -> Diagram b+drawTree Tree =+    fit 0.5 $ centerXY $ scaleY 0.5 $ fc black $ mconcat+        [ rect 0.1 0.6 # moveTo (p2 (0.5, 0.7))+        , circle 0.1   # moveTo (p2 (0.4, 0.9))+        , circle 0.2   # moveTo (p2 (0.6, 1.0))+        , circle 0.2   # moveTo (p2 (0.4, 1.2))+        , circle 0.16  # moveTo (p2 (0.6, 1.3))+        , circle 0.15  # moveTo (p2 (0.45, 1.45))+        , circle 0.1   # moveTo (p2 (0.7, 1.4))+        ]++drawTent :: Backend' b => PlacedTent -> Diagram b+drawTent (Tent d) = tent <> lwG gridwidth (stroke conn)+  where+    conn :: Path V2 Double+    conn = p2 (0, 0) ~~ p2 (case d of+        U -> (0, 1)+        R -> (1, 0)+        D -> (0, -1)+        L -> (-1, 0))++    tent = fit 0.7 $ centerXY $ lwG 0 $ mconcat+        [ rect 10 (1/4) # fc black+        , shape [(-2, 0), (0, 4), (2, 0), (-2, 0)] # fc white+        , shape [(-4, 0), (0, 8), (4, 0), (-4, 0)] # fc black+        , shape [(0, 8), (-1/2, 8 + 1), (-1, 8 + 1 - 1/4), (0, 8 + 1 - 1/4 - 2), (0, 8) ] # fc black+        , shape [(0, 8), (1/2, 8 + 1), (1, 8 + 1 - 1/4), (0, 8 + 1 - 1/4 - 2), (0, 8) ] # fc black+        ]+    shape = strokeLocLoop . fromVertices . map p2++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)++placeNoteTR :: Backend' b =>+             Size -> Diagram b -> Diagram b+placeNoteTR 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]++polyominoGrid :: Backend' b => Grid C (Maybe Char) -> Diagram b+polyominoGrid = placeGrid . fmap (scale 0.8) . fmap+    (\x -> case x of+        Nothing -> fillBG black+        Just c -> (drawText [c] # fc white # lc white) <> fillBG black)++drawPentominos :: Backend' b => Diagram b+drawPentominos = centerXY . scale 0.5 . polyominoGrid $ pentominoGrid++drawLITS :: Backend' b => Diagram b+drawLITS = centerXY . scale 0.5 . polyominoGrid $ litsGrid++drawLITSO :: Backend' b => Diagram b+drawLITSO = centerXY . scale 0.5 . polyominoGrid $ litsoGrid++drawCrossing :: Backend' b => Crossing -> Diagram b+drawCrossing = const $ drawChar '+'++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++drawCages :: (Backend' b, Eq a, Ord a) =>+             Grid C a -> Map.Map a (Diagram b) -> Diagram b+drawCages g m =+    hints <> (mconcat . map cage . Map.elems) byChar+  where+    hints = placeGrid . fmap (bgFrame 0.05 white . placeTL) . clues+          . fmap (flip Map.lookup m . head) . invertMap . fmap tl $ byChar+    tl = head . sortOn (\ (C x y) -> (-y, x))+    byChar = invertMap g++invertMap :: (Eq a, Ord a) => Map.Map k a -> Map.Map a [k]+invertMap+        = Map.fromList+        . map (\ l -> (fst (head l), map snd l))+        . groupBy (\ x y -> fst x == fst y)+        . sortOn fst+        . map (\ (x,y) -> (y,x))+        . Map.toList+
+ src/Draw/Grid.hs view
@@ -0,0 +1,206 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ConstraintKinds #-}++module Draw.Grid where++import Data.Maybe (catMaybes)+import Data.Char (isUpper)+import qualified Data.Map.Strict as Map++import Diagrams.Prelude hiding (size, E, N, dot, outer)+import Diagrams.TwoD.Offset (offsetPath)++import qualified Data.AffineSpace as AS++import Data.Util+import Data.Grid+import Data.GridShape hiding (edge)++import Draw.Style+import Draw.Lib+import Draw.Widths++(.--.) :: AS.AffineSpace p => p -> p -> AS.Diff p+(.--.) = (AS..-.)++class ToPoint a where+    toPoint :: a -> P2 Double++instance ToPoint C where+    toPoint c = p2 (1/2, 1/2) .+^ r2i (c .--. C 0 0)++instance ToPoint N where+    toPoint c = origin .+^ r2i (c .--. N 0 0)++-- | Draw a small black dot with no envelope.+dot :: Backend' b => Diagram b+dot = circle 0.05 # fc black # smash++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++    (outer, inner) = irregularGridPaths g++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, 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]++dashoffset :: Double+dashoffset = 2.5 / 40++gridDashing :: (Semigroup a, HasStyle a, InSpace V2 Double a) => a -> a+gridDashing = bgdashingG dashes dashoffset white'+  where+    white' = black `withOpacity` (0.05 :: Double)++-- | `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 (Map.keysSet m) (`Map.member` m)+    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++offsetBorder :: Double -> [C] -> Path V2 Double+offsetBorder off cs =+    pathFromLoopVertices . map offsetCorner . corners . map toPoint $ loop+  where+    pathFromLoopVertices = pathFromLocTrail+                         . mapLoc (wrapLoop . closeLine)+                         . fromVertices+    outer :: [Edge' N]+    (outer, _) = edges cs (`elem` cs)+    loop :: [N]+    loop = case loops (map ends' outer) of+        Just [l] -> tail l+        _        -> error "broken cage"+    corners :: [P2 Double] -> [(P2 Double, P2 Double, P2 Double)]+    corners vs = catMaybes $ zipWith3+        (\ a b c -> if b .-. a == c .-. b then Nothing else Just (a, b, c))+        vs (tail vs ++ vs) (tail (tail vs) ++ vs)+    offsetCorner :: (P2 Double, P2 Double, P2 Double) -> P2 Double+    offsetCorner (a, b, c) =+      let+        dir = perp (normalize (b .-. a)) ^+^ perp (normalize (c .-. b))+      in+        b .+^ (off *^ dir)++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)++placeGrid :: (ToPoint k, HasOrigin a, Monoid a, InSpace V2 Double a)+          => Grid k a -> a+placeGrid = Map.foldMapWithKey (moveTo . toPoint)++placeGrid' :: (HasOrigin a, Monoid a, InSpace V2 Double a)+          => Grid (P2 Double) a -> a+placeGrid' = Map.foldMapWithKey moveTo++edge :: (ToPoint k) => Edge k -> Path V2 Double+edge (E c d) = rule d # translate (toPoint c .-. origin)+  where+    rule Vert = vrule 1.0 # alignB+    rule Horiz = hrule 1.0 # alignL++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, InSpace V2 Double a) => a -> a+edgeStyle = lineCap LineCapSquare . lwG edgewidth++thinEdgeStyle :: (HasStyle a, InSpace V2 Double a) => a -> a+thinEdgeStyle = lineCap LineCapSquare . lwG onepix++drawEdges :: (ToPoint k, Backend' b) => [Edge k] -> Diagram b+drawEdges = edgeStyle . stroke . mconcat . map edge++drawThinEdges :: (ToPoint k, Backend' b) => [Edge k] -> Diagram b+drawThinEdges = thinEdgeStyle . stroke . mconcat . map edge++drawAreas :: (Backend' b, Eq a) =>+             Grid C a -> Diagram b+drawAreas = drawEdges . borders++cage :: Backend' b => [C] -> Diagram b+cage cs = dashingG dashes dashoffset+        . lwG onepix . stroke . offsetBorder (-4 * onepix) $ cs++fillBG :: Backend' b => Colour Double -> Diagram b+fillBG c = square 1 # lwG onepix # fc c # lc c++shadeGrid :: Backend' b =>+             Grid C (Maybe (Colour Double)) -> Diagram b+shadeGrid = placeGrid . fmap fillBG . clues++drawShade :: Backend' b =>+             Grid C Bool -> Diagram b+drawShade = shadeGrid . fmap f+  where+    f True  = Just gray+    f False = Nothing++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++-- Place a list of diagrams along a ray, with steps of size+-- @f@.+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
+ src/Draw/Lib.hs view
@@ -0,0 +1,161 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ConstraintKinds #-}++module Draw.Lib where++import Diagrams.Path (pathPoints)+import Diagrams.Prelude++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 = (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+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 :: (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 :: (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 => Diagram b -> Diagram b+smash = withEnvelope (pointDiagram origin :: D V2 Double)++-- | Helper to translate by a point given as @(Int, Int)@.+translatep :: (InSpace V2 Double t, Transformable t)+           => (Int, Int) -> t -> t+translatep = translate . r2i++-- | Convert pair of @Int@ to vector.+r2i :: (Int, Int) -> V2 Double+r2i = r2 . (fromIntegral *** fromIntegral)++-- | Convert pair of @Int@ to point.+p2i :: (Int, Int) -> P2 Double+p2i = p2 . (fromIntegral *** fromIntegral)++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 => 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 ::  (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 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 =>+            Diagram b -> Diagram b -> Diagram b+besidesL a b = a ||| strutX 0.5 ||| b'+    where b' = b # centerY # translate (dmid unitY a *^ unitY)++-- | Variant of 'besidesL' where the origin is that of the right diagram.+besidesR :: Backend' b =>+           Diagram b  -> Diagram b -> Diagram b+besidesR b a =  b' ||| strutX 0.5 ||| a+    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, InSpace V2 Double t) =>+       Double -> t -> t+fit f a = scale (f / m) 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 => 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+    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 = unsafePerformIO $ 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+--textrect t = rect (fromIntegral (length t) * 0.4) 0.7 # lc red+--text'' :: Backend' b => String -> Diagram b R2+--text'' t = text' t `atop` textrect t++-- | Variant of 'phantom' that forces the argument backend type.+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++{-+opaque :: Backend' b => Diagram b -> Diagram b+opaque x = x <> bRect x # stroke # scale 1.1 # lwG 0 # fc white+ where+    bRect :: Backend' b => Diagram b -> Path V2 Double+    bRect = boundingRect+-}
+ src/Draw/PuzzleGrids.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE MultiParamTypeClasses     #-}+{-# LANGUAGE FlexibleContexts          #-}+{-# LANGUAGE TypeFamilies              #-}+{-# LANGUAGE ConstraintKinds           #-}++module Draw.PuzzleGrids+    (+      drawIntGrid+    , drawCharGrid+    , outsideIntGrid+    , drawSlitherGrid+    , drawTightGrid+    , sudokugrid+    , drawWordsClues+    , drawOutsideGrid+    , drawMultiOutsideGrid+    , drawOutsideGridN+    , drawMultiOutsideGridN+    , placeMultiOutside+    ) where++import Diagrams.Prelude hiding (size, N)++import qualified Data.Map.Strict as Map+import Data.Maybe (maybeToList)++import Data.Grid+import Data.GridShape+import Data.Elements+import Data.Sudoku++import Draw.Lib+import Draw.Style+import Draw.Grid+import Draw.Elements++drawCharGrid :: Backend' b =>+                Grid C (Maybe Char) -> Diagram b+drawCharGrid = placeGrid . fmap drawChar . clues <> grid gDefault++drawIntGrid :: Backend' b =>+               Grid C (Maybe Int) -> Diagram b+drawIntGrid = placeGrid . fmap drawInt . clues  <> grid gDefault++drawSlitherGrid :: Backend' b =>+                   Grid C (Maybe Int) -> Diagram b+drawSlitherGrid = placeGrid . fmap  drawInt . clues <> grid gSlither++sudokugrid :: Backend' b =>+              Grid C a -> Diagram b+sudokugrid = drawEdges . sudokubordersg <> grid gDefault++drawWordsClues :: Backend' b =>+                  Grid C (Maybe [String]) -> Diagram b+drawWordsClues = placeGrid . fmap drawWords . clues++drawTightGrid :: Backend' b =>+                 (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)++placeMultiOutside :: (Ord k, FromCoord k, ToPoint k,+                      HasOrigin a, Monoid a,+                      InSpace V2 Double a)+                  => OutsideClues k [a] -> a+placeMultiOutside = placeGrid . multiOutsideClues++placeOutside :: (Ord k, FromCoord k, ToPoint k,+                 HasOrigin a, Monoid a,+                 InSpace V2 Double a)+             => OutsideClues k (Maybe a) -> a+placeOutside = placeMultiOutside . fmap maybeToList++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++drawMultiOutsideGrid :: Backend' b => OutsideClues C [String] -> Diagram b+drawMultiOutsideGrid = placeMultiOutside . fmap (fmap (scale 0.8 . drawText))+                     <> grid gDefault . outsideGrid++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/Draw/PuzzleTypes.hs view
@@ -0,0 +1,910 @@++{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ConstraintKinds #-}++module Draw.PuzzleTypes (+    lits, litsplus, geradeweg, fillomino, masyu, nurikabe, latintapa,+    sudoku, thermosudoku, pyramid, kpyramid, slither,+    liarslither, tightfitskyscrapers, wordloop, wordsearch,+    curvedata, doubleback, slalom, compass, boxof2or3,+    afternoonskyscrapers, meanderingnumbers, tapa, japanesesums,+    coral, maximallengths, primeplace, labyrinth, bahnhof,+    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, mines, tents,+    pentominoSums, coralLits, coralLitso, snake, countryRoad,+    killersudoku, friendlysudoku, japsummasyu+  ) where++import Diagrams.Prelude hiding (Loop, N, coral, size)++import Data.Char (isUpper)+import Data.List (nub, sort, sortOn)+import qualified Data.Map.Strict as Map++import Draw.Style+import Draw.PuzzleGrids+import Draw.Draw+import Draw.Grid+import qualified Draw.Pyramid as DPyr+import Draw.Elements+import Draw.Lib+import Draw.Widths++import Data.Grid+import Data.GridShape+import Data.Elements+import qualified Data.Pyramid as Pyr++unimplemented :: String -> a+unimplemented x = error (x ++ " unimplemented")++lits :: Backend' b => Drawers b AreaGrid ShadedGrid+lits = drawers+    (grid gDefault <> drawAreasGray)+    ((drawAreas <> grid gDefault) . fst <> drawShade . snd)++litsplus :: Backend' b => Drawers b AreaGrid ShadedGrid+litsplus = lits++litssym :: Backend' b => Drawers b AreaGrid ShadedGrid+litssym = drawers+    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 => Drawers b (Grid C (Maybe Int)) (Loop C)+geradeweg = drawers+    drawIntGrid+    (placeGrid . fmap drawInt . clues . fst+     <> solstyle . drawEdges . snd+     <> grid gDefault . fst)++fillomino :: Backend' b => Drawers b (Grid C (Maybe Int)) (Grid C Int)+fillomino = drawers+    (placeGrid . fmap drawInt . clues <> grid gDashed)+    ((placeGrid . fmap drawInt <> drawEdges . borders <> grid gDashed) . snd)++fillominoCheckered :: Backend' b => Drawers b (Grid C (Maybe Int)) (Grid C Int)+fillominoCheckered = drawers+    (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 => Drawers b (Grid C (Maybe Int))+                                              (Grid C Int, Loop C)+fillominoLoop = drawers+    (placeGrid . fmap drawInt . clues <> grid gDashed)+    ((placeGrid . fmap drawInt . fst+      <> solstyle . drawEdges . snd+      <> drawEdges . borders . fst+      <> grid gDashed . fst) . snd)++masyu :: Backend' b =>+         Drawers b (Grid C (Maybe MasyuPearl)) (Loop C)+masyu = drawers+    p+    (solstyle . drawEdges . snd <> p . fst)+  where+    p = placeGrid . fmap pearl . clues <> grid gDefault++nurikabe :: Backend' b =>+            Drawers b (Grid C (Maybe Int)) ShadedGrid+nurikabe = drawers+    drawIntGrid+    (drawIntGrid . fst <> drawShade . snd)++latintapa :: Backend' b =>+             Drawers b (Grid C (Maybe [String])) (Grid C (Maybe Char))+latintapa = drawers+    l+    (l . fst <> placeGrid . fmap drawChar . clues . snd)+  where+    l = grid gDefault <> drawWordsClues++sudoku :: Backend' b =>+          Drawers b (Grid C (Maybe Int)) (Grid C (Maybe Int))+sudoku = drawers+    (placeGrid . fmap drawInt . clues <> sudokugrid)+    ((placeGrid . fmap drawInt . clues <> sudokugrid) . snd)++thermosudoku :: Backend' b =>+                Drawers b (Grid C (Maybe Int), [Thermometer]) (Grid C (Maybe Int))+thermosudoku = drawers+    (placeGrid . fmap drawInt . clues . fst <> sudokugrid . fst <> drawThermos . snd)+    (placeGrid . fmap drawInt . clues . snd <> sudokugrid . snd <> drawThermos . snd . fst)++killersudoku :: Backend' b =>+                Drawers b (AreaGrid, Map.Map Char Int, Grid C (Maybe Int)) (Grid C Int)+killersudoku = drawers+    (p <> placeGrid . fmap drawInt . clues . trd3)+    (placeGrid . fmap drawInt . snd <> p . fst)+  where+    fst3 (x,_,_) = x+    trd3 (_,_,z) = z+    p = cages <> sudokugrid . fst3+    cages (g, m, _) = drawCages (Map.filter (/= '.') g) (Map.map drawInt m)++pyramid :: Backend' b =>+    Drawers b Pyr.Pyramid Pyr.PyramidSol+pyramid = drawers+    DPyr.pyramid+    (DPyr.pyramid . merge)+  where+    merge (p, q) = Pyr.mergepyramidsol p q++kpyramid :: Backend' b =>+    Drawers b Pyr.RowKropkiPyramid Pyr.PyramidSol+kpyramid = drawers+    DPyr.kpyramid+    (DPyr.kpyramid . merge)+  where+    merge (p, q) = Pyr.mergekpyramidsol p q++slither :: Backend' b =>+           Drawers b (Grid C (Maybe Int)) (Loop N)+slither = drawers+    drawSlitherGrid+    (drawSlitherGrid . fst <> solstyle . drawEdges . snd)++liarslither :: Backend' b =>+               Drawers b (Grid C (Maybe Int)) (Loop N, Grid C Bool)+liarslither = drawers+    drawSlitherGrid+    (placeGrid . fmap (solstyle . drawCross) . snd . snd+     <> drawSlitherGrid . fst+     <> solstyle . drawEdges . fst . snd)++slithermulti :: Backend' b =>+                Drawers b (Grid C (Maybe Int), Int) [Edge N]+slithermulti = drawers+    (drawSlitherGrid . fst <> n)+    (drawSlitherGrid . fst . fst <> solstyle . drawEdges . snd)+  where+    n (g, l) = placeNoteTR (size' g) (drawInt l ||| strutX 0.2 ||| miniloop)+    size' = size . Map.mapKeys toCoord++tightfitskyscrapers :: Backend' b =>+                       Drawers b (OutsideClues C (Maybe Int), Grid C (Tightfit ()))+                                      (Grid C (Tightfit Int))+tightfitskyscrapers = drawers+    (placeGrid . fmap drawInt . clues . outsideClues . fst+     <> drawTightGrid (const mempty) . snd)+    (placeGrid . fmap drawInt . clues . outsideClues . fst . fst+     <> drawTightGrid drawInt . snd)++wordgrid :: Backend' b =>+            Grid C (Maybe Char) -> [String] -> Diagram b+wordgrid g ws = stackWords ws `besidesR` drawCharGrid g++wordloop :: Backend' b =>+            Drawers b (Grid C (Maybe Char), [String]) (Grid C (Maybe Char))+wordloop = drawers+    (uncurry wordgrid)+    (drawCharGrid . snd)++wordsearch :: Backend' b =>+              Drawers b (Grid C (Maybe Char), [String])+                             (Grid C (Maybe Char), [MarkedWord])+wordsearch = drawers+    (uncurry wordgrid) +    (solstyle . drawMarkedWords . snd . snd+     <> drawCharGrid . fst . snd)++curvedata :: Backend' b =>+             Drawers b (Grid C (Maybe [Edge N])) [Edge C]+curvedata = drawers+    (placeGrid . fmap drawCurve . clues+     <> grid gDefault)+    (placeGrid . fmap drawCurve . clues . fst+     <> solstyle . drawEdges . snd+     <> grid gDefault . fst)++doubleback :: Backend' b =>+              Drawers b AreaGrid (Loop C)+doubleback = drawers+    p+    (solstyle . drawEdges . snd <> p . fst)+  where+    p = grid gDefault <> drawAreasGray++slalom :: Backend' b =>+          Drawers b (Grid N (Maybe Int)) (Grid C SlalomDiag)+slalom = drawers+    p+    (p . fst <> placeGrid . fmap (solstyle . drawSlalomDiag) . snd)+  where+    p = placeGrid . fmap drawSlalomClue . clues+        <> grid gDefault . cellGrid++compass :: Backend' b =>+           Drawers b (Grid C (Maybe CompassC)) AreaGrid+compass = drawers+    (placeGrid . fmap drawCompassClue . clues <> grid gDashed)+    (placeGrid . fmap drawCompassClue . clues . fst+     <> (grid gDashed <> drawAreasGray) . snd)++boxof2or3 :: Backend' b =>+             Drawers b (Grid N MasyuPearl, [Edge N]) ()+boxof2or3 = drawers+    (placeGrid . fmap smallPearl . fst+     <> drawThinEdges . snd)+    (unimplemented "boxof2or3 solution")++afternoonskyscrapers :: Backend' b =>+                        Drawers b (Grid C Shade) (Grid C (Maybe Int))+afternoonskyscrapers = drawers+    (grid gDefault <> placeGrid . fmap drawShadow)+    (drawIntGrid . snd <> placeGrid . fmap drawShadow . fst)++meanderingnumbers :: Backend' b =>+                        Drawers b AreaGrid (Grid C (Maybe Int))+meanderingnumbers = drawers+    (grid gDefault <> drawAreas)+    (drawIntGrid . snd <> drawAreas . fst)++tapa :: Backend' b =>+        Drawers b (Grid C (Maybe TapaClue)) ShadedGrid+tapa = drawers+    tapaGrid+    (tapaGrid . fst <> drawShade . snd)+  where+    tapaGrid = placeGrid . fmap drawTapaClue . clues <> grid gDefault++japanesesums :: Backend' b =>+                Drawers b (OutsideClues C [Int], String)+                               (Grid C (Either Black Int))+japanesesums = drawers+    (outsideIntGrid . fst <> n)+    (outsideIntGrid . fst . fst <> japcells . snd)+  where+    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 =>+          Drawers b (OutsideClues C [String]) ShadedGrid+coral = drawers+    drawMultiOutsideGrid+    (drawMultiOutsideGrid . fst <> drawShade . snd)++maximallengths :: Backend' b =>+                  Drawers b (OutsideClues C (Maybe Int)) (Loop C)+maximallengths = drawers+    g+    (solstyle . drawEdges . snd <> g . fst)+  where+    g = placeGrid . fmap drawInt . clues . outsideClues+        <> grid gDefault . outsideGrid++primeplace :: Backend' b =>+              Drawers b (Grid C PrimeDiag) (Grid C Int)+primeplace = drawers+    g+    (placeGrid . fmap drawInt . snd <> g . fst)+  where+    g = grid gStyle+        <> placeGrid . fmap drawPrimeDiag+    gStyle = GridStyle LineThin LineThick Nothing VertexNone++labyrinth :: Backend' b =>+             Drawers b (Grid C (Maybe Int), [Edge N], String) (Grid C (Maybe Int))+labyrinth = drawers+    (placeGrid . fmap drawInt . clues . fst3 <> p <> n)+    (placeGrid . fmap drawInt . clues . snd <> p . fst)+  where+    p (g, e, _) = drawEdges e <> grid gPlain g+    n (g, _, ds) = placeNoteTR (size' g) (drawText ds # scale 0.8)+    size' = size . Map.mapKeys toCoord+    fst3 (x,_,_) = x++bahnhof :: Backend' b =>+            Drawers b (Grid C (Maybe BahnhofClue)) [Edge C]+bahnhof = drawers+    (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 =>+                   Drawers b (Grid C (Clue Int), DigitRange)+                                  (Grid C (Clue Int), AreaGrid)+blackoutDominos = drawers+    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 =>+    Drawers b (Grid N (Clue Int)) VertexLoop+angleLoop = drawers+    (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 =>+    Drawers b (OutsideClues C (Clue Int), Grid C (Maybe Fish)) [Edge C]+anglers = drawers+    (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 =>+    Drawers b (Grid C (Maybe Int)) ShadedGrid+cave = drawers+    (grid gDashDash <> placeGrid . fmap drawInt . clues)+    (drawEdges . edgesGen (/=) not . snd+     <> placeGrid . fmap drawInt . clues . fst+     <> drawShade . snd+     <> grid gStyle . fst)+  where+    gDashDash = GridStyle LineDashed LineDashed Nothing VertexNone+    gStyle = GridStyle LineDashed LineNone (Just $ FrameStyle 8 gray)+                       VertexNone++skyscrapers ::+    Backend' b =>+    Drawers b (OutsideClues C (Maybe Int), String) (Grid C (Maybe Int))+skyscrapers = drawers+    (g . fst <> n)+    (g . fst . fst <> placeGrid . fmap drawInt . clues . snd)+  where+    g = placeGrid . fmap drawInt . clues . outsideClues+        <> grid gDefault . outsideGrid+    n (oc, s) = placeNoteTR (outsideSize oc) (drawText s)++shikaku :: Backend' b => Drawers b (Grid C (Maybe Int)) AreaGrid+shikaku = drawers+    p+    (drawAreas . snd <> p . fst)+  where+    p = placeGrid . fmap drawInt . clues <> grid gDashed++slovaksums :: Backend' b => Drawers b (Grid C (Maybe SlovakClue), String) (Grid C (Maybe Int))+slovaksums = drawers+    (p . fst <> n)+    (placeGrid . fmap drawInt . clues . snd <> p . fst . fst)+  where+    n (g, ds) = placeNoteTR (size' g) (drawText ds # scale 0.8)+    p = grid gDefault <> placeGrid . fmap drawSlovakClue . clues+    size' = size . Map.mapKeys toCoord++skyscrapersStars ::+    Backend' b =>+    Drawers b (OutsideClues C (Maybe Int), Int)+                   (Grid C (Either Int Star))+skyscrapersStars = drawers+    (g <> n)+    (g . fst <> placeGrid . fmap (either drawInt drawStar) . snd)+  where+    g = (placeGrid . fmap drawInt . clues . outsideClues+         <> grid gDefault . outsideGrid) . fst+    n (oc, s) = placeNoteTR (outsideSize oc)+                          (drawInt s ||| strutX 0.2 ||| drawStar Star)++summon ::+    Backend' b =>+    Drawers b (AreaGrid, OutsideClues C (Maybe Int), String) (Grid C (Maybe Int))+summon = drawers+    (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 =>+    Drawers b (Grid C (Maybe Char),+                    OutsideClues C [Int],+                    OutsideClues C (Maybe Char))+                   (Grid C (Either Black Char))+baca = drawers+    (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 =>+    Drawers b (OutsideClues C (Maybe Char), String) (Grid C (Maybe Char))+buchstabensalat = drawers+    (p <> n)+    (p . fst <> placeGrid . fmap drawChar . clues . snd)+  where+    p = (placeGrid . fmap drawChar . clues . outsideClues+         <> grid gDefault . outsideGrid) . fst+    n (ocs, ls) = placeNoteTR (outsideSize ocs) (drawText ls # scale 0.8)++doppelblock ::+    Backend' b =>+    Drawers b (OutsideClues C (Maybe Int))+              (Grid C (Either Black Int))+doppelblock = drawers+    (p <> n)+    (p . fst <> placeGrid . fmap drawVal . snd)+  where+    p = placeGrid . fmap (scale 0.8 . drawInt) . clues . outsideClues+        <> grid gDefault . outsideGrid+    n ocs = placeNoteTL (0, h) (drawText ds # scale 0.8)+      where+        h = snd (outsideSize ocs)+        ds = "1-" ++ show (h - 2)+    drawVal (Right c) = drawInt c+    drawVal (Left _) = fillBG gray++sudokuDoppelblock ::+    Backend' b =>+    Drawers b (AreaGrid, OutsideClues C (Maybe Int))+                   (Grid C (Either Black Int))+sudokuDoppelblock = drawers+    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 =>+    Drawers b (Grid C (Clue Int), DigitRange) AreaGrid+dominos = drawers+    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 =>+    Drawers b (Grid C (Clue Int), DigitRange, DigitRange) AreaGrid+dominoPills = drawers+    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 =>+    Drawers b (Grid C (Maybe Int)) [Edge C]+numberlink = drawers+    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 =>+          Drawers b (Grid C (Maybe MasyuPearl)) (Loop N)+loopki = drawers+    p+    (solstyle . drawEdges . snd <> p . fst)+  where+    p = placeGrid . fmap (scale 0.5 . pearl) . clues <> grid gSlither++scrabble :: Backend' b =>+            Drawers b (Grid C Bool, [String]) (Grid C (Maybe Char))+scrabble = drawers+    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 =>+             Drawers b (Grid C Bool, Grid C (Maybe Int)) (Grid C Int)+neighbors = drawers+    (placeGrid . fmap drawInt . clues . snd <> (grid gDefault <> drawShade) . fst)+    (placeGrid . fmap drawInt . snd <> (grid gDefault <> drawShade) . fst . fst)++starwars :: Backend' b =>+            Drawers b (AreaGrid, [MarkedLine C]) (Grid C (Maybe Star))+starwars = drawers+    p+    (p . fst <> placeGrid . fmap drawStar . clues . snd)+  where+    p = ((drawAreas <> grid gDefault) . fst <> drawMarkedLines . snd)++starbattle :: Backend' b =>+              Drawers b (AreaGrid, Int) (Grid C (Maybe Star))+starbattle = drawers+    (p <> n)+    ((p <> n) . fst <> placeGrid . fmap drawStar . clues . snd)+  where+    p = (drawAreas <> grid gDefault) . fst+    n (g, k) = placeNoteTR (size' g)+                         (drawInt k ||| strutX 0.2 ||| drawStar Star)+    size' = size . Map.mapKeys toCoord++heyawake :: Backend' b =>+            Drawers b (AreaGrid, Grid C (Maybe Int)) (Grid C Bool)+heyawake = drawers+    (as <> cs)+    (as . fst <> drawShade . snd <> cs . fst)+  where+    as = (drawAreas <> grid gDefault) . fst+    cs = placeGrid . fmap drawInt . clues . snd++wormhole :: Backend' b =>+            Drawers b (Grid C (Maybe (Either Int Char))) ()+wormhole = drawers+    (placeGrid . fmap (either drawInt drawChar) . clues <> grid gDashed)+    mempty++pentominous ::+    Backend' b =>+    Drawers b (Grid C (Maybe Char)) (Grid C Char)+pentominous = drawers+    (placeGrid . fmap drawChar . clues <> grid gDashed)+    (placeGrid . fmap drawChar . clues . fst <>+     (drawAreas <> grid gDashed) . snd)++colorakari ::+    Backend' b =>+    Drawers b (Grid C (Maybe Char)) (Grid C (Maybe Char))+colorakari = drawers+    (placeGrid . fmap drawColorClue . clues <> grid gDefault)+    (unimplemented "color akari solution")+  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 =>+    Drawers b (AreaGrid, (Grid C (Maybe MEnd))) (Loop C)+persistenceOfMemory = drawers+    (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++mappingTable :: Backend' b => [(String, String)] -> Diagram b+mappingTable = b . g+  where+    b = placeGrid . fmap drawText <> grid gPlain+    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++abctje ::+    Backend' b =>+    Drawers b (DigitRange, [(String, Int)]) [(Int, Char)]+abctje = drawers+    p+    ((mappingTable . h  ||| const (strutX 1.0) ||| mappingTable . h') . snd)+  where+    p (ds, cs) = (digNote ds `aboveT` (stackWordsLeft ws ||| strutX 1.0 ||| stackWordsRight ns))+                 `besidesR` (strutX 2.0 ||| mappingTable ps ||| strutX 1.0 ||| mappingTable 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+    h = sortOn fst . map (\(x, y) -> (y:[], show x))+    h' = map (\(x, y) -> (show x, y:[]))++kropki ::+    Backend' b =>+    Drawers b (Map.Map (Edge N) KropkiDot) (Grid C Int)+kropki = drawers+    (p <> n)+    (placeGrid . fmap drawInt . snd <> p . fst)+  where+    p = placeGrid' . Map.mapKeys midPoint . fmap kropkiDot <> grid gDefault . sizeGrid . sz+    n g = placeNoteTR (w, h) (drawText ds # scale 0.8)+      where+        (w, h) = sz g+        ds = "1-" ++ show h+    sz m = edgeSize (Map.keys m)++statuepark ::+    Backend' b =>+    Drawers b (Grid C (Maybe MasyuPearl)) (Grid C Bool)+statuepark = drawers+    p+    (p . fst <> drawShade . snd)+  where+    p = placeGrid . fmap pearl . clues <> grid gDashed++pentominousBorders ::+    Backend' b =>+    Drawers b (Grid C (), [Edge N]) (Grid C Char)+pentominousBorders = drawers+    (drawEdges . snd <> grid gDashed . fst)+    ((drawAreas <> grid gDashed) . snd)++smallHintRooms ::+    Backend' b =>+    (AreaGrid, Grid C (Maybe Int)) -> Diagram b+smallHintRooms = ((drawAreas <> grid gDashed) . fst <> placeGrid . fmap hintTL . fmap show . clues . snd)++nanroSignpost ::+    Backend' b =>+    Drawers b (AreaGrid, Grid C (Maybe Int)) (Grid C Int)+nanroSignpost = drawers+    smallHintRooms+    (placeGrid . fmap drawInt . snd <> smallHintRooms . fst)++tomTom ::+    Backend' b =>+    Drawers b (AreaGrid, Grid C (Maybe String)) (Grid C Int)+tomTom = drawers+    p+    (placeGrid . fmap drawInt . snd <> p . fst)+  where+    p = ((drawAreas <> grid gDashed) . fst <> placeGrid . fmap hintTL . clues . snd)++horseSnake ::+    Backend' b =>+    Drawers b (Grid C (Maybe (Either MEnd Int))) [Edge C]+horseSnake = drawers+    p+    (solstyle . drawEdges . snd <> p . fst)+  where+    p = (placeGrid . fmap (either drawBigEnd drawInt) . clues <> grid gDashed)++illumination ::+    Backend' b =>+    Drawers b (OutsideClues C (Maybe Fraction)) (Grid N (Maybe PlainNode), [Edge N])+illumination = drawers+    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 =>+    Drawers b (Grid C (Maybe Myopia)) (Grid C Bool)+pentopia = drawers+    p+    (p . fst <> drawShade . snd)+  where+    p = placeGrid . fmap drawMyopia . clues <> grid gDefault++pentominoPipes ::+    Backend' b =>+    Drawers b (Grid N Char) (Grid N KropkiDot, [Edge N])+pentominoPipes = drawers+    (placeGrid . fmap drawCharOpaque <> grid gSlither . cellGrid)+    ((placeGrid . fmap kropkiDot . fst+      <> drawEdges . snd) . snd+     <> grid gSlither . cellGrid . fst)++greaterWall ::+    Backend' b =>+    Drawers b ([GreaterClue], [GreaterClue]) (Grid C Bool)+greaterWall = drawers+    ((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 =>+    Drawers b (Grid C (), Grid N (), Grid C (), Map.Map (Edge N) ()) AreaGrid+galaxies = drawers+    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++mines ::+    Backend' b =>+    Drawers b (Grid C (Maybe Int)) ShadedGrid+mines = drawers+    p+    (p . fst <> placeGrid . fmap (const (pearl MBlack)) . Map.filter id . snd)+  where+    p = grid gDefault <> placeGrid . fmap (\i -> drawInt i <> fillBG lightgray) . clues++tents ::+    Backend' b =>+    Drawers b (OutsideClues C (Maybe Int), Grid C (Maybe Tree)) (Grid C (Maybe PlacedTent))+tents = drawers+    p+    (p . fst <> placeGrid . fmap drawTent . clues . snd)+  where+    p = placeGrid . fmap drawInt . clues . outsideClues . fst+        <> placeGrid . fmap drawTree . clues . snd+        <> grid gDashed . snd++pentominoSums :: Backend' b => Drawers b (OutsideClues C [String], String)+                               (Grid C (Either Pentomino Int), [(Char, Int)], OutsideClues C [String])+pentominoSums = drawers+    p+    (solgrid ||| const (strutX 1.0) ||| table)+  where+    p (ocs, ds) =+        (((drawMultiOutsideGrid ocs <> n (ocs, ds)) ||| strutX 1.0 ||| emptyTable ocs)+        `aboveT` drawPentominos)+    n (ocs, ds) = placeNoteTL (0, h ocs) (drawText ds # scale 0.8)+    h = snd . outsideSize+    emptyTable = mappingTable . emptys+    emptys = map (\k -> (k, "")) . nub . sort . concat . outsideValues+    solgrid =+        skel . fst3 . snd+        <> drawMultiOutsideGrid . trd3 . snd+        <> cells . fst3 . snd+    fst3 (x,_,_) = x+    trd3 (_,_,z) = z+    skel = skeletonStyle . drawEdges . skeletons . lefts+    skeletonStyle = lc white . lwG (3 * onepix)+    lefts = clues . fmap (either Just (const Nothing))+    cells = placeGrid . fmap (\v -> case v of+        Left _  -> fillBG gray+        Right x -> drawInt x)+    table ((cs, _), (_, m, _)) = mappingTable m'+      where+        m' = Map.toList (Map.union (Map.fromList a) (Map.fromList (emptys cs)))+        a = map (\(k, v) -> ([k], show v)) m++coralLits ::+    Backend' b =>+    Drawers b (OutsideClues C [String]) (Grid C (Maybe Char))+coralLits = drawers+    (\ocs -> drawMultiOutsideGrid ocs `aboveT` drawLITS)+    (skeletonStyle . drawEdges . skeletons . clues . snd+     <> drawMultiOutsideGrid . fst+     <> placeGrid . fmap (const (fillBG gray)) . clues . snd)+  where+    skeletonStyle = lc white . lwG (3 * onepix)++coralLitso ::+    Backend' b =>+    Drawers b (OutsideClues C [String]) (Grid C (Either Black Char))+coralLitso = drawers+    (\ocs -> drawMultiOutsideGrid ocs `aboveT` drawLITSO)+    (drawMultiOutsideGrid . fst+     <> skeletonStyle . drawEdges . skeletons . rights . snd+     <> placeGrid . fmap (const (fillBG gray)) . lefts . snd)+  where+    skeletonStyle = solstyle+    lefts = clues . fmap (either Just (const Nothing))+    rights = clues . fmap (either (const Nothing) Just)++snake ::+    Backend' b =>+    Drawers b (OutsideClues C (Maybe Int), Grid C (Maybe MEnd))+                   (Grid C (Maybe (Either MEnd Black)))+snake = drawers p s+  where+    cs = placeGrid . fmap drawInt . clues . outsideClues . fst+    p = cs+        <> placeGrid . fmap drawBigEnd . clues . snd+        <> grid gDefault . snd+    s = cs . fst+        <> grid gDefault . snd+        <> placeGrid . fmap (either (drawBigEnd <> gr) gr) . clues . snd+    gr = const (fillBG gray)++countryRoad ::+    Backend' b =>+    Drawers b (AreaGrid, Grid C (Maybe Int)) (Loop C)+countryRoad = drawers+    smallHintRooms+    (solstyle . drawEdges . snd <> smallHintRooms . fst)++friendlysudoku ::+    Backend' b =>+    Drawers b (Map.Map (Edge N) KropkiDot, Grid C (Maybe Int)) (Grid C Int)+friendlysudoku = drawers+    p+    (placeGrid . fmap drawInt . snd <> p . fst)+  where+    p = placeGrid' . Map.mapKeys midPoint . fmap kropkiDot . fst+      <> placeGrid . fmap drawInt . clues . snd+      <> sudokugrid . snd++japsummasyu :: Backend' b =>+          Drawers b (OutsideClues C [String]) ()+japsummasyu = drawers+    (placeMultiOutside . fmap (fmap (scale 0.8 . drawText))+                     <> grid gDashDash . outsideGrid)+    (error "japsummasyu solution not implemented")+  where+    gDashDash = GridStyle LineDashed LineDashed Nothing VertexNone
+ src/Draw/Pyramid.hs view
@@ -0,0 +1,41 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE TypeFamilies #-}++module Draw.Pyramid where++import Diagrams.Prelude++import Data.Pyramid+import Draw.Elements+import Draw.Lib+import Draw.Widths++pgray :: Colour Double+pgray = blend 0.6 white black++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+clue Nothing = mempty+clue (Just c) = text' (show c)++cellc :: Backend' b => Bool -> Maybe Int -> Diagram b+cellc s c = clue c `atop` cell s++row :: Backend' b => Row -> Diagram b+row (R cs s) = centerX . hcat . map (cellc s) $ cs++pyramid :: Backend' b => Pyramid -> Diagram b+pyramid = alignBL . vcat . map row . unPyr++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 kropkiDot ks)++kpyramid :: Backend' b => RowKropkiPyramid -> Diagram b+kpyramid = alignBL . vcat . map krow . unKP
+ src/Draw/Style.hs view
@@ -0,0 +1,50 @@+module Draw.Style+    ( LineStyle (..)+    , FrameStyle (..)+    , VertexStyle (..)+    , GridStyle (..)++    , gDefault+    , gDashed+    , gDashedThick+    , gPlain+    , gPlainDashed+    , gSlither+    ) where++import Draw.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/Draw/Widths.hs view
@@ -0,0 +1,26 @@+module Draw.Widths where++gridres :: Int+gridres = 40++gridresd :: Double+gridresd = fromIntegral gridres++onepix :: Double+onepix = 1 / fromIntegral gridres++twopix, fourpix :: Double+twopix = 2 * onepix+fourpix = 4 * onepix++gridwidth :: Double+gridwidth = onepix++framewidthfactor :: Double+framewidthfactor = 4++edgewidth :: Double+edgewidth = 3 * onepix++borderwidth :: Double+borderwidth = 1 / 4
+ src/Parse/Code.hs view
@@ -0,0 +1,26 @@+{-# LANGUAGE OverloadedStrings #-}++module Parse.Code where++import Data.Code+import Parse.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'))+    , fmap LRows'   <$> (v .:? "cell_rows_bottom_labeled" >>= traverse parseCharMap)+    , fmap LCols    <$> (v .:? "cell_cols_labeled" >>= traverse parseCharMap)+    , fmap LRowsN'  <$> (v .:? "node_rows_bottom_labeled" >>= traverse parseCharMap)+    , fmap LColsN   <$> (v .:? "node_cols_labeled" >>= traverse parseCharMap)+    ] +parseCode _          = fail "expected object"
+ src/Parse/Parsec.hs view
@@ -0,0 +1,30 @@+-- | Parsec helper for puzzle file parsing.+module Parse.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.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/Parse/Puzzle.hs view
@@ -0,0 +1,28 @@+{-# LANGUAGE OverloadedStrings #-}++module Parse.Puzzle where++import Data.Yaml+import Control.Applicative++import Data.PuzzleTypes++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 .:? "code"+    parseJSON _          = empty++-- | A pair of parsers for a puzzle type.+-- First parses the puzzle, second the solution.+type ParsePuzzle a b = (Value -> Parser a, Value -> Parser b)++parseType :: String -> Parser PuzzleType+parseType t = case lookupType t of+                  Nothing -> fail $ "unknown puzzle type: " ++ t+                  Just p  -> return p
+ src/Parse/PuzzleTypes.hs view
@@ -0,0 +1,507 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeFamilies #-}++module Parse.PuzzleTypes (+    lits, litsplus, geradeweg, fillomino, masyu, nurikabe, latintapa,+    sudoku, thermosudoku, pyramid, kpyramid, slither,+    liarslither, tightfitskyscrapers, wordloop, wordsearch,+    curvedata, doubleback, slalom, compass, boxof2or3,+    afternoonskyscrapers, meanderingnumbers, tapa, japanesesums, coral,+    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, mines, tents,+    pentominoSums, coralLits, coralLitso, snake, countryRoad,+    killersudoku, friendlysudoku, japsummasyu+  ) where++import Control.Applicative+import Control.Monad++import qualified Data.Map.Strict as Map+import Data.Map.Strict (Map)+import Data.Yaml++import Parse.Util+import Parse.Puzzle+import Data.Grid+import Data.GridShape+import qualified Data.Pyramid as Pyr+import Data.Elements++lits :: ParsePuzzle AreaGrid (Grid C Bool)+lits = (parseGrid, parseShadedGrid)++litsplus :: ParsePuzzle AreaGrid (Grid C Bool)+litsplus = lits++geradeweg :: ParsePuzzle (Grid C (Maybe Int)) (Loop C)+geradeweg = (parseClueGrid, parseEdges)++fillomino :: ParsePuzzle (Grid C (Maybe Int)) (Grid C Int)+fillomino = (parseExtClueGrid, parseExtGrid)++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 (Grid C (Maybe Int)) (Grid C Bool)+nurikabe = (parseExtClueGrid, parseShadedGrid)++latintapa :: ParsePuzzle (Grid C (Maybe [String])) (Grid C (Maybe Char))+latintapa = ((unRG <$>) . parseJSON,+             fmap (fmap (fmap unAlpha)) . parseClueGrid')++sudoku :: ParsePuzzle (Grid C (Maybe Int)) (Grid C (Maybe Int))+sudoku = (parseClueGrid, parseClueGrid)++thermosudoku :: ParsePuzzle (Grid C (Maybe Int), [Thermometer])+                            (Grid C (Maybe Int))+thermosudoku = ((parseThermoGrid =<<) . parseJSON, parseClueGrid)++killersudoku :: ParsePuzzle (AreaGrid, Map Char Int, Grid C (Maybe Int)) (Grid C Int)+killersudoku = (,)+    (\v -> (,,)+         <$> parseFrom ["cages"] parseGrid v+         <*> parseFrom ["clues"] parseCharMap v+         <*> (parseFrom ["grid"] parseClueGrid v <|> pure Map.empty))+    parseGrid++pyramid :: ParsePuzzle Pyr.Pyramid Pyr.PyramidSol+pyramid = (parseJSON, parseJSON)++kpyramid :: ParsePuzzle Pyr.RowKropkiPyramid Pyr.PyramidSol+kpyramid = (parseJSON, parseJSON)++slither :: ParsePuzzle (Grid C (Clue Int)) (Loop N)+slither = (parseClueGrid, parseEdges)++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 (Grid C (Maybe Int)) (Loop N, Grid C Bool)+liarslither = (parseClueGrid, (unLSol <$>) . parseJSON)++tightfitskyscrapers :: ParsePuzzle+                       (OutsideClues C (Maybe Int), Grid C (Tightfit ()))+                       (Grid C (Tightfit Int))+tightfitskyscrapers = (parseTightOutside, parseSpacedGrid)++newtype GridWords = GW { unGW :: (Grid C (Maybe Char), [String]) }++instance FromJSON GridWords where+    parseJSON (Object v) = GW <$> ((,) <$>+                                   (parseClueGrid =<< v .: "grid") <*>+                                   v .: "words")+    parseJSON _ = empty++wordloop :: ParsePuzzle (Grid C (Maybe Char), [String]) (Grid C (Maybe Char))+wordloop = ((unGW <$>) . parseJSON, parseClueGrid)++newtype GridMarked = GM { unGM :: (Grid C (Maybe Char), [MarkedWord]) }++instance FromJSON GridMarked where+    parseJSON (Object v) = GM <$> ((,) <$>+                                   (parseClueGrid =<< v .: "grid") <*>+                                   (map unPMW <$> v .: "words"))+    parseJSON _          = mzero++wordsearch :: ParsePuzzle (Grid C (Maybe Char), [String])+                          (Grid C (Maybe Char), [MarkedWord])+wordsearch = ((unGW <$>) . parseJSON, (unGM <$>) . parseJSON)++newtype Curve = Curve { unCurve :: [Edge N] }++instance FromJSON Curve where+    parseJSON v = Curve <$> parsePlainEdges v++curvedata :: ParsePuzzle (Grid C (Maybe [Edge N])) [Edge C]+curvedata = ((fmap (fmap unCurve) . unRG <$>) . parseJSON, parsePlainEdges)++doubleback :: ParsePuzzle AreaGrid (Loop C)+doubleback = (parseGrid, parseEdges)++slalom :: ParsePuzzle (Grid N (Maybe Int)) (Grid C SlalomDiag)+slalom = (parseClueGrid, parseGrid)++compass :: ParsePuzzle (Grid C (Maybe CompassC)) AreaGrid+compass = ((fmap (fmap unPCC) . unRG <$>) . parseJSON, parseGrid)++boxof2or3 :: ParsePuzzle (Grid N MasyuPearl, [Edge N]) ()+boxof2or3 = (parseNodeEdges, error "boxof2or3 parsing not implemented")++afternoonskyscrapers :: ParsePuzzle (Grid C Shade) (Grid C (Maybe Int))+afternoonskyscrapers = (parseAfternoonGrid, parseGrid)++-- this should be changed to support clue numbers+meanderingnumbers :: ParsePuzzle AreaGrid (Grid C (Maybe Int))+meanderingnumbers = (parseGrid, parseGrid)++tapa :: ParsePuzzle (Grid C (Maybe TapaClue)) (Grid C Bool)+tapa = (\v -> fmap (fmap unParseTapaClue) . unRG <$> parseJSON v,+        parseShadedGrid)++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 C [String]) (Grid C Bool)+coral = (,)+    (fmap (fmap (map unIntString)) . parseMultiOutsideClues)+    parseShadedGrid++maximallengths :: ParsePuzzle (OutsideClues C (Maybe Int)) (Loop C)+maximallengths = (\v -> fmap blankToMaybe <$> parseCharOutside v,+                  parseEdges)++primeplace :: ParsePuzzle (Grid C PrimeDiag) (Grid C Int)+primeplace = (parseIrregGrid, parseIrregGrid)++labyrinth :: ParsePuzzle (Grid C (Maybe Int), [Edge N], String) (Grid C (Maybe Int))+labyrinth = (p, parseClueGrid')+  where+    p v@(Object o) = tup <$> parseFrom ["grid"] parseCellEdges v <*> o .: "digits"+    p _            = mempty+    tup (x,y) z = (x,y,z)++bahnhof :: ParsePuzzle (Grid C (Maybe BahnhofClue)) [Edge C]+bahnhof = (parseClueGrid, parseEdges)++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)++{-+  parsing the mappings in order, from something like++  - 1: A+  - 3: B+  - 2: X+-}+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 :: Map a b -> Parser (a, b)+    pair m = if Map.size m == 1 then (return . head . Map.toList $ m) else empty++kropki :: ParsePuzzle (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 (), 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 . Map.mapMaybe id . fmap blankToMaybe''++mines :: ParsePuzzle (Grid C (Maybe Int)) (Grid C Bool)+mines = (parseIrregGrid, parseShadedGrid)++tents :: ParsePuzzle (OutsideClues C (Maybe Int), Grid C (Maybe Tree)) (Grid C (Maybe PlacedTent))+tents =+    ( p+    , fmap (fmap fromTentOrTree) . parseClueGrid+    )+  where+    fromTentOrTree :: Maybe (Either Tree PlacedTent) -> Maybe PlacedTent+    fromTentOrTree = maybe Nothing (either (const Nothing) Just)++    p v = (,)+        <$> parseFrom ["clues"] parseOut v+        <*> parseFrom ["grid"] parseClueGrid v++pentominoSums :: ParsePuzzle (OutsideClues C [String], String)+                             (Grid C (Either Pentomino Int), [(Char, Int)], OutsideClues C [String])+pentominoSums = (p, s)+  where+    p v@(Object o) = (,) <$> (fst coral) v <*> o .: "digits"+    p _ = empty+    s v = (,,) <$> parseFrom ["grid"] parseGrid v+               <*> parseFrom ["values"] values v+               <*> fst coral v+    values v = parseJSON v >>= sequence . map parseKey . Map.toList+    parseKey (k, v) = (,) <$> parseString k <*> pure v++coralLits :: ParsePuzzle (OutsideClues C [String]) (Grid C (Maybe Char))+coralLits = (,)+    (fst coral)+    (fmap (fmap (fmap unAlpha)) . parseClueGrid)++coralLitso :: ParsePuzzle (OutsideClues C [String]) (Grid C (Either Black Char))+coralLitso = (,)+    (fst coral)+    (fmap (fmap (fmap unAlpha)) . parseGrid)++snake :: ParsePuzzle (OutsideClues C (Maybe Int), Grid C (Maybe MEnd)) (Grid C (Maybe (Either MEnd Black)))+snake = (p, parseClueGrid)+  where+    p v = (,)+        <$> parseFrom ["clues"] parseOut v+        <*> parseFrom ["grid"] parseClueGrid v++countryRoad :: ParsePuzzle (AreaGrid, Grid C (Maybe Int)) (Loop C)+countryRoad = (,) (fst nanroSignpost) parseEdges++friendlysudoku :: ParsePuzzle (Map (Edge N) KropkiDot, Grid C (Maybe Int)) (Grid C Int)+friendlysudoku = (,) p parseGrid+  where+    p v = (\(_,c,e) -> (e,c)) <$> pp v+    pp :: Value -> Parser (Grid N (), Grid C (Maybe Int), Map.Map (Edge N) KropkiDot)+    pp = parseEdgeGrid++japsummasyu :: ParsePuzzle (OutsideClues C [String]) ()+japsummasyu = (,)+    (fmap (fmap (map unIntString)) . parseMultiOutsideClues)+    (error "japsummasyu solution not implemented")
+ src/Parse/Util.hs view
@@ -0,0 +1,783 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE TypeFamilies #-}++module Parse.Util where++import Prelude hiding (mapM)++import Control.Applicative+import Control.Arrow+import Control.Monad hiding (mapM)++import Data.List (sortBy, intersect)+import Data.Maybe (catMaybes, fromMaybe, isJust, fromJust)+import Data.Ord (comparing)+import Data.Either (isRight)+import qualified Data.Map.Strict as Map+import qualified Data.Set as Set+import qualified Data.HashMap.Strict as HMap+import Data.Traversable (mapM)+import Data.Monoid ((<>))++import Data.Char (digitToInt, isAlpha, isDigit)+import Text.Read (readMaybe)+import qualified Data.Text as T++import Data.Yaml++import Data.Grid+import Data.GridShape+import Data.Elements++import Parse.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' (f:fs) (Object v) = v .: f >>= field' fs+    field' (f:_)  _          = fail $ "expected field '" ++ T.unpack f ++ "'"++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++instance FromChar Char where+    parseChar = pure++class FromString a where+    parseString :: String -> Parser a++instance FromString Char where+    parseString [c] = pure c+    parseString s = fail $ "expected a single character, got `" ++ s ++ "`"++parseStringJSON :: FromString a => Value -> Parser a+parseStringJSON v = parseJSON v >>= parseString++parseLine :: FromChar a => String -> Parser [a]+parseLine = mapM parseChar++instance FromChar Int where+    parseChar c+        | isDigit c  = digitToInt <$> parseChar c+        | otherwise  = fail $ "expected a digit, got '" ++ [c] ++ "'"++newtype Alpha = Alpha { unAlpha :: Char }+    deriving (Show, Ord, Eq)++instance FromChar Alpha where+    parseChar c+        | isAlpha c  = Alpha <$> parseChar c+        | otherwise  = fail $ "expected a letter"++-- | Helper to parse strings from number-formatted YAML fields.+--   Somewhat dodgy.+newtype IntString = IntString { unIntString :: String }++instance FromJSON IntString where+    parseJSON v@(Number _) = IntString . (show :: Int -> String) <$> parseJSON v+    parseJSON v            = IntString <$> parseJSON v++-- | A rectangle. Each row has length `w`.+data Rect a = Rect !Int !Int [[a]]+    deriving Show++instance Functor Rect where+    fmap f (Rect w h ls) = Rect w h (map (map f) ls)++instance FromChar a => FromJSON (Rect a) where+    parseJSON (String t) = Rect w h <$> filled+      where+        ls = map T.stripEnd . T.lines $ t+        w = maximum . map T.length $ ls+        h = length ls+        filledc = map (T.unpack . T.justifyLeft w ' ') ls+        filled = mapM (mapM parseChar) filledc+    parseJSON _          = fail "expected string"++data Border a = Border [a] [a] [a] [a]+    deriving Show++-- | This instance might be a lie.+instance Foldable Border where+    foldMap f (Border l r b t) = foldMap f l <> foldMap f r+                              <> foldMap f b <> foldMap f t++instance Traversable Border where+    sequenceA (Border l r b t) = Border <$> sequenceA l+                                        <*> sequenceA r+                                        <*> sequenceA b+                                        <*> sequenceA t++instance Functor Border where+    f `fmap` (Border l r b t) = Border (f <$> l) (f <$> r) (f <$> b) (f <$> t)++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 = parseBorderedRect parseChar parseChar++newtype SpacedRect a = SpacedRect { unSpaced :: Rect a }++instance FromString a => FromJSON (SpacedRect a) where+    parseJSON (String t) = if w == wmin then SpacedRect . Rect w h <$> p+                                        else empty+      where+        ls = map T.words . T.lines $ t+        w = maximum . map length $ ls+        wmin = minimum . map length $ ls+        h = length ls+        p = mapM (mapM (parseString . T.unpack)) ls+    parseJSON _          = empty++instance FromChar () where+    parseChar = fmap (const ()) . chars ['.', ' ']++data Space = Space++instance FromChar Space where+    parseChar = fmap (const Space) . char ' '++data Blank = Blank+data Blank' = Blank'+data Blank'' = Blank''+data Empty = Empty++instance FromChar Blank where+    parseChar = fmap (const Blank) . char '.'++parseCharJSON :: FromChar a => Value -> Parser a+parseCharJSON v = do+    [c] <- parseJSON v+    parseChar c++instance FromJSON Blank where+   parseJSON = parseCharJSON++instance FromChar Blank' where+    parseChar = fmap (const Blank') . chars ['.', '-']++instance FromJSON Blank' where+    parseJSON (String ".") = pure Blank'+    parseJSON (String "-") = pure Blank'+    parseJSON _            = fail "expected '.-'"++instance FromChar Blank'' where+    parseChar = fmap (const Blank'') . chars ['.', ' ', '-', '|']++instance FromChar Empty where+    parseChar = fmap (const Empty) . char ' '++instance FromString Blank where+    parseString "." = pure Blank+    parseString _   = fail "expected '.'"++instance FromChar PlainNode where+    parseChar = fmap (const PlainNode) . char 'o'++instance FromChar MasyuPearl where+    parseChar = fmap f . chars ['*', 'o']+      where+        f '*' = MBlack+        f 'o' = MWhite+        f _   = impossible++instance FromChar SlalomDiag where+    parseChar '/'  = pure SlalomForward+    parseChar '\\' = pure SlalomBackward+    parseChar _    = empty++instance FromChar Black where+    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 Tree where+    parseChar = fmap (const Tree) . char '*'++instance FromChar PlacedTent where+    parseChar = fmap Tent . parseChar++instance FromChar Pentomino where+    parseChar c =+        if c `elem` ("FILNPTUVWXYZ" :: String) then+            pure (Pentomino c)+        else+            fail "expected FILNPTUVWXYZ"++instance (FromChar a, FromChar b) => FromChar (Either a b) where+    parseChar c = Left <$> parseChar c <|> Right <$> parseChar c++instance (FromString a, FromString b) => FromString (Either a b) where+    parseString c = Left <$> parseString c <|> Right <$> parseString c++newtype Either' a b = Either' { unEither' :: Either a b }++instance (FromChar a, FromChar b) => FromChar (Either' a b) where+    parseChar c = Either' <$> parseChar c++instance (FromJSON a, FromJSON b) => FromJSON (Either' a b) where+    parseJSON v = Either' <$>+                      (Left <$> parseJSON v <|> Right <$> parseJSON v)++instance FromChar a => FromChar (Maybe a) where+    parseChar = optional . parseChar++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++rectToCoordGrid :: Rect a -> Grid Coord a+rectToCoordGrid (Rect _ _ ls) = listListToMap ls++blankToMaybe :: Either Blank a -> Maybe a+blankToMaybe = either (const Nothing) Just++blankToMaybe' :: Either Blank' a -> Maybe a+blankToMaybe' = either (const Nothing) Just++blankToMaybe'' :: Either Blank'' a -> Maybe a+blankToMaybe'' = either (const Nothing) Just++rectToIrregGrid :: Rect (Either Empty a) -> Grid Coord a+rectToIrregGrid = fmap fromRight . Map.filter isRight . rectToCoordGrid+  where+    fromRight (Right r) = r+    fromRight _         = impossible++newtype Shaded = Shaded { unShaded :: Bool }++instance FromChar Shaded where+    parseChar 'x'  = pure . Shaded $ True+    parseChar 'X'  = pure . Shaded $ True+    parseChar _    = pure . Shaded $ False++parseShadedGrid :: Key k => Value -> Parser (Grid k Bool)+parseShadedGrid v = fmap unShaded <$> parseGrid v++parseCoordGrid :: (FromChar a)+               => Value -> Parser (Grid Coord a)+parseCoordGrid v = rectToCoordGrid <$> parseJSON v++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' :: (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++parseExtGrid :: (Key k, FromChar a, FromJSON a) => Value -> Parser (Grid k a)+parseExtGrid = parseExtGrid' id++parseExtClueGrid :: (Key k, FromChar a, FromJSON a) => Value -> Parser (Grid k (Maybe a))+parseExtClueGrid v = fmap blankToMaybe <$> parseExtGrid' Right v++fromCoordGrid :: Key k => Grid Coord a -> Grid k a+fromCoordGrid = Map.mapKeys fromCoord++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 :: Key k => Value -> Parser [Edge k]+parsePlainEdges v = filterPlainEdges <$> parseAnnotatedEdges v++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++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.+--+-- E.g. o-*-*-o+--      |1|2 3+--      *-o+-- 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 <*>+                  parseAnnotatedEdges v+  where+    parseBoth = do+        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 m =+        (fromCoordGrid . divkeys $ mnode, fromCoordGrid . divkeys $ mcell)+      where+        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+--      | |+--      *-o+-- to a grid of masyu pearls and some edges.+parseNodeEdges :: FromChar a =>+                  Value -> Parser (Grid N a, [Edge N])+parseNodeEdges v = proj13 <$> parsePlainEdgeGrid v+  where+    proj13 :: (Grid N a, Grid C Char, [Edge N])+              -> (Grid N a, [Edge N])+    proj13 (x,_,z) = (x,z)++parseCellEdges :: FromChar a =>+                  Value -> Parser (Grid C a, [Edge N])+parseCellEdges v = proj23 <$> parsePlainEdgeGrid v+  where+    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` ("└┴├┼" :: String) = pure . HalfDirs $ [Vert, Horiz]+                | c `elem` ("│┘┤"  :: String) = pure . HalfDirs $ [Vert]+                | c `elem` ("─┌┬"  :: String) = pure . HalfDirs $ [Horiz]+                | otherwise                   = pure . HalfDirs $ []++-- parses a string like+--  ┌┐┌─┐+--  ││└┐│+--  │└─┘│+--  └──┐│+--     └┘+parseBoxEdges :: Key k => Value -> Parser [Edge k]+parseBoxEdges v = do+    m <- fmap unHalfDirs <$> parseGrid v+    return [ E p d | (p, ds) <- Map.toList m, d <- ds ]++parseEdges :: Key k => Value -> Parser [Edge k]+parseEdges v = parsePlainEdges v <|> parseBoxEdges v++instance FromChar Dir' where+    parseChar 'u' = pure U+    parseChar 'U' = pure U+    parseChar 'd' = pure D+    parseChar 'D' = pure D+    parseChar 'r' = pure R+    parseChar 'R' = pure R+    parseChar 'l' = pure L+    parseChar 'L' = pure L+    parseChar _   = fail "expected 'uUdDrRlL'"++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 :: Grid C Alpha -> Parser [Thermometer]+parseThermos m = catMaybes <$> mapM parseThermo (Map.keys m')+  where+    m' = fmap unAlpha m+    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' :: C -> Parser Thermometer+    parseThermo' p = do+        q <- next p+        maybe (fail "no succ for thermo bulb") (fmap (p:) . parseThermo'') q+    parseThermo'' :: C -> Parser Thermometer+    parseThermo'' p = do+        q <- next p+        maybe (pure [p]) (fmap (p:) . parseThermo'') q+    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 $ 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 $ p+      where+        disjointSucc q = null $ intersect (succs p) (succs' q)+        succs' q = maybe [] (const $ succs q) (Map.lookup q m')++parseThermoGrid :: ThermoRect -> Parser (Grid C (Maybe Int), [Thermometer])+parseThermoGrid (Rect _ _ ls) = (,) ints+                              <$> parseThermos alphas+  where+    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+    parseChar '.'  = pure . Tight $ Single ()+    parseChar '/'  = pure . Tight $ UR () ()+    parseChar '\\' = pure . Tight $ DR () ()+    parseChar _    = empty++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+    parseString [c, '/',d]    = UR <$> parseChar c <*> parseChar d+    parseString [c,'\\',d]    = DR <$> parseChar c <*> parseChar d+    parseString _             = empty++newtype PMarkedWord = PMW {unPMW :: MarkedWord}++parseNWords :: Int -> String -> Parser [String]+parseNWords n s | length ws == n  = pure ws+                | otherwise       = empty+  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 . 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+    parseJSON (String t) = comp . map T.unpack . T.words $ t+        where c "." = pure Nothing+              c x   = Just <$> parseString x+              comp [n, e, s, w] = PCC <$> (CC <$> c n <*> c e <*> c s <*> c w)+              comp _            = empty+    parseJSON _          = empty++newtype PSlovakClue = PSlovakClue {unPSlovakClue :: SlovakClue}++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"++newtype RefGrid k a = RefGrid { unRG :: Grid k (Maybe a) }++hashmaptomap :: Ord a => HMap.HashMap a b -> Map.Map a b+hashmaptomap = Map.fromList . HMap.toList++compose :: Ord b => Map.Map a b -> Map.Map b c -> Maybe (Map.Map a c)+compose m1 m2 = mapM (`Map.lookup` m2) m1++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 m'++parseAfternoonGrid :: Value -> Parser (Grid C Shade)+parseAfternoonGrid v = do+    (_, g, es) <- parsePlainEdgeGrid v+                  :: Parser (Grid N Char, Grid C Char, [Edge N])+    return $ toMap g es+  where+    toShade Vert  = Shade False True+    toShade Horiz = Shade True  False+    merge (Shade a b) (Shade c d) = Shade (a || c) (b || d)+    toMap g es = Map.fromListWith+        merge+        ([(fromCoord . toCoord $ p, toShade d) | E p d <- es]+         ++ [(p, Shade False False) | p <- Map.keys g])++newtype ParseTapaClue = ParseTapaClue { unParseTapaClue :: TapaClue }++instance FromJSON ParseTapaClue where+    parseJSON v = do xs <- parseJSON v+                     guard $ length xs > 0 && length xs <= 4+                     return . ParseTapaClue . TapaClue $ xs++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 k a)+parseCharOutside (Object v) = reorientOutside <$>+                              (OC <$>+                               pfield "left" <*> pfield "right" <*>+                               pfield "bottom" <*> pfield "top" )+  where+    pfield f = parseLine . fromMaybe [] =<< v .:? f+parseCharOutside _          = empty++parseOutside :: FromJSON a => Value -> Parser (OutsideClues k a)+parseOutside (Object v) = reorientOutside <$>+                              (OC <$>+                               pfield "left" <*> pfield "right" <*>+                               pfield "bottom" <*> pfield "top" )+  where+    pfield f = pure . fromMaybe [] =<< v .:? f+parseOutside _          = empty++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"+    v' `ml` k = fromMaybe [] <$> v' .:? k+    rev (OC l r b t) = reorientOutside $+                       OC (map reverse l) r b (map reverse t)+parseMultiOutsideClues _ = empty++instance FromChar PrimeDiag where+    parseChar '.'  = pure $ PrimeDiag (False, False)+    parseChar '/'  = pure $ PrimeDiag (True,  False)+    parseChar '\\' = pure $ PrimeDiag (False, True)+    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/Text/Puzzles/Code.hs
@@ -1,22 +0,0 @@-{-# 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
@@ -1,30 +0,0 @@--- | 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
@@ -1,28 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Text.Puzzles.Puzzle where--import Data.Yaml-import Control.Applicative--import Data.Puzzles.PuzzleTypes--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 .:? "code"-    parseJSON _          = empty---- | A pair of parsers for a puzzle type.--- First parses the puzzle, second the solution.-type ParsePuzzle a b = (Value -> Parser a, Value -> Parser b)--parseType :: String -> Parser PuzzleType-parseType t = case lookupType t of-                  Nothing -> fail $ "unknown puzzle type: " ++ t-                  Just p  -> return p
− src/Text/Puzzles/PuzzleTypes.hs
@@ -1,425 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeFamilies #-}--module Text.Puzzles.PuzzleTypes (-    lits, litsplus, geradeweg, fillomino, masyu, nurikabe, latintapa,-    sudoku, thermosudoku, pyramid, kpyramid, slither,-    liarslither, tightfitskyscrapers, wordloop, wordsearch,-    curvedata, doubleback, slalom, compass, boxof2or3,-    afternoonskyscrapers, meanderingnumbers, tapa, japanesesums, coral,-    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-import qualified Data.Puzzles.Pyramid as Pyr-import Data.Puzzles.Elements--lits :: ParsePuzzle AreaGrid (Grid C Bool)-lits = (parseGrid, parseShadedGrid)--litsplus :: ParsePuzzle AreaGrid (Grid C Bool)-litsplus = lits--geradeweg :: ParsePuzzle (Grid C (Maybe Int)) (Loop C)-geradeweg = (parseClueGrid, parseEdges)--fillomino :: ParsePuzzle (Grid C (Maybe Int)) (Grid C Int)-fillomino = (parseExtClueGrid, parseExtGrid)--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 (Grid C (Maybe Int)) (Grid C Bool)-nurikabe = (parseExtClueGrid, parseShadedGrid)--latintapa :: ParsePuzzle (Grid C (Maybe [String])) (Grid C (Maybe Char))-latintapa = ((unRG <$>) . parseJSON,-             fmap (fmap (fmap unAlpha)) . parseClueGrid')--sudoku :: ParsePuzzle (Grid C (Maybe Int)) (Grid C (Maybe Int))-sudoku = (parseClueGrid, parseClueGrid)--thermosudoku :: ParsePuzzle (Grid C (Maybe Int), [Thermometer])-                            (Grid C (Maybe Int))-thermosudoku = ((parseThermoGrid =<<) . parseJSON, parseClueGrid)--pyramid :: ParsePuzzle Pyr.Pyramid Pyr.PyramidSol-pyramid = (parseJSON, parseJSON)--kpyramid :: ParsePuzzle Pyr.RowKropkiPyramid Pyr.PyramidSol-kpyramid = (parseJSON, parseJSON)--slither :: ParsePuzzle (Grid C (Clue Int)) (Loop N)-slither = (parseClueGrid, parseEdges)--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 (Grid C (Maybe Int)) (Loop N, Grid C Bool)-liarslither = (parseClueGrid, (unLSol <$>) . parseJSON)--tightfitskyscrapers :: ParsePuzzle-                       (OutsideClues C (Maybe Int), Grid C (Tightfit ()))-                       (Grid C (Tightfit Int))-tightfitskyscrapers = (parseTightOutside, parseSpacedGrid)--newtype GridWords = GW { unGW :: (Grid C (Maybe Char), [String]) }--instance FromJSON GridWords where-    parseJSON (Object v) = GW <$> ((,) <$>-                                   (parseClueGrid =<< v .: "grid") <*>-                                   v .: "words")-    parseJSON _ = empty--wordloop :: ParsePuzzle (Grid C (Maybe Char), [String]) (Grid C (Maybe Char))-wordloop = ((unGW <$>) . parseJSON, parseClueGrid)--newtype GridMarked = GM { unGM :: (Grid C (Maybe Char), [MarkedWord]) }--instance FromJSON GridMarked where-    parseJSON (Object v) = GM <$> ((,) <$>-                                   (parseClueGrid =<< v .: "grid") <*>-                                   (map unPMW <$> v .: "words"))-    parseJSON _          = mzero--wordsearch :: ParsePuzzle (Grid C (Maybe Char), [String])-                          (Grid C (Maybe Char), [MarkedWord])-wordsearch = ((unGW <$>) . parseJSON, (unGM <$>) . parseJSON)--newtype Curve = Curve { unCurve :: [Edge N] }--instance FromJSON Curve where-    parseJSON v = Curve <$> parsePlainEdges v--curvedata :: ParsePuzzle (Grid C (Maybe [Edge N])) [Edge C]-curvedata = ((fmap (fmap unCurve) . unRG <$>) . parseJSON, parsePlainEdges)--doubleback :: ParsePuzzle AreaGrid (Loop C)-doubleback = (parseGrid, parseEdges)--slalom :: ParsePuzzle (Grid N (Maybe Int)) (Grid C SlalomDiag)-slalom = (parseClueGrid, parseGrid)--compass :: ParsePuzzle (Grid C (Maybe CompassC)) AreaGrid-compass = ((fmap (fmap unPCC) . unRG <$>) . parseJSON, parseGrid)--boxof2or3 :: ParsePuzzle (Grid N MasyuPearl, [Edge N]) ()-boxof2or3 = (parseNodeEdges, error "boxof2or3 parsing not implemented")--afternoonskyscrapers :: ParsePuzzle (Grid C Shade) (Grid C (Maybe Int))-afternoonskyscrapers = (parseAfternoonGrid, parseGrid)---- this should be changed to support clue numbers-meanderingnumbers :: ParsePuzzle AreaGrid (Grid C (Maybe Int))-meanderingnumbers = (parseGrid, parseGrid)--tapa :: ParsePuzzle (Grid C (Maybe TapaClue)) (Grid C Bool)-tapa = (\v -> fmap (fmap unParseTapaClue) . unRG <$> parseJSON v,-        parseShadedGrid)--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 C [String]) (Grid C Bool)-coral = (,)-    (fmap (fmap (map unIntString)) . parseMultiOutsideClues)-    parseShadedGrid--maximallengths :: ParsePuzzle (OutsideClues C (Maybe Int)) (Loop C)-maximallengths = (\v -> fmap blankToMaybe <$> parseCharOutside v,-                  parseEdges)--primeplace :: ParsePuzzle (Grid C PrimeDiag) (Grid C Int)-primeplace = (parseIrregGrid, parseIrregGrid)--labyrinth :: ParsePuzzle (Grid C (Maybe Int), [Edge N]) (Grid C (Maybe Int))-labyrinth = (parseCellEdges, parseClueGrid')--bahnhof :: ParsePuzzle (Grid C (Maybe BahnhofClue)) [Edge C]-bahnhof = (parseClueGrid, parseEdges)--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
@@ -1,760 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE TypeFamilies #-}--module Text.Puzzles.Util where--import Prelude hiding (mapM)--import Control.Applicative-import Control.Arrow-import Control.Monad hiding (mapM)--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 (mapM)-import Data.Monoid ((<>))--import Data.Char (digitToInt, isAlpha, isDigit)-import Text.Read (readMaybe)-import qualified Data.Text as T--import Data.Yaml--import Data.Puzzles.Grid-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' (f:fs) (Object v) = v .: f >>= field' fs-    field' (f:_)  _          = fail $ "expected field '" ++ T.unpack f ++ "'"--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--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--instance FromChar Int where-    parseChar c-        | isDigit c  = digitToInt <$> parseChar c-        | otherwise  = fail $ "expected a digit, got '" ++ [c] ++ "'"--newtype Alpha = Alpha { unAlpha :: Char }-    deriving (Show, Ord, Eq)--instance FromChar Alpha where-    parseChar c-        | isAlpha c  = Alpha <$> parseChar c-        | otherwise  = empty---- | Helper to parse strings from number-formatted YAML fields.---   Somewhat dodgy.-newtype IntString = IntString { unIntString :: String }--instance FromJSON IntString where-    parseJSON v@(Number _) = IntString . (show :: Int -> String) <$> parseJSON v-    parseJSON v            = IntString <$> parseJSON v---- | A rectangle. Each row has length `w`.-data Rect a = Rect !Int !Int [[a]]-    deriving Show--instance Functor Rect where-    fmap f (Rect w h ls) = Rect w h (map (map f) ls)--instance FromChar a => FromJSON (Rect a) where-    parseJSON (String t) = Rect w h <$> filled-      where-        ls = map T.stripEnd . T.lines $ t-        w = maximum . map T.length $ ls-        h = length ls-        filledc = map (T.unpack . T.justifyLeft w ' ') ls-        filled = mapM (mapM parseChar) filledc-    parseJSON _          = fail "expected string"--data Border a = Border [a] [a] [a] [a]-    deriving Show---- | This instance might be a lie.-instance Foldable Border where-    foldMap f (Border l r b t) = foldMap f l <> foldMap f r-                              <> foldMap f b <> foldMap f t--instance Traversable Border where-    sequenceA (Border l r b t) = Border <$> sequenceA l-                                        <*> sequenceA r-                                        <*> sequenceA b-                                        <*> sequenceA t--instance Functor Border where-    f `fmap` (Border l r b t) = Border (f <$> l) (f <$> r) (f <$> b) (f <$> t)--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 = parseBorderedRect parseChar parseChar--newtype SpacedRect a = SpacedRect { unSpaced :: Rect a }--instance FromString a => FromJSON (SpacedRect a) where-    parseJSON (String t) = if w == wmin then SpacedRect . Rect w h <$> p-                                        else empty-      where-        ls = map T.words . T.lines $ t-        w = maximum . map length $ ls-        wmin = minimum . map length $ ls-        h = length ls-        p = mapM (mapM (parseString . T.unpack)) ls-    parseJSON _          = empty--instance FromChar () where-    parseChar = fmap (const ()) . chars ['.', ' ']--data Space = Space--instance FromChar Space where-    parseChar = fmap (const Space) . char ' '--data Blank = Blank-data Blank' = Blank'-data Blank'' = Blank''-data Empty = Empty--instance FromChar Blank where-    parseChar = fmap (const Blank) . char '.'--parseCharJSON :: FromChar a => Value -> Parser a-parseCharJSON v = do-    [c] <- parseJSON v-    parseChar c--instance FromJSON Blank where-   parseJSON = parseCharJSON--instance FromChar Blank' where-    parseChar = fmap (const Blank') . chars ['.', '-']--instance FromJSON Blank' where-    parseJSON (String ".") = pure Blank'-    parseJSON (String "-") = pure Blank'-    parseJSON _            = fail "expected '.-'"--instance FromChar Blank'' where-    parseChar = fmap (const Blank'') . chars ['.', ' ', '-', '|']--instance FromChar Empty where-    parseChar = fmap (const Empty) . char ' '--instance FromString Blank where-    parseString "." = pure Blank-    parseString _   = fail "expected '.'"--instance FromChar PlainNode where-    parseChar = fmap (const PlainNode) . char 'o'--instance FromChar MasyuPearl where-    parseChar = fmap f . chars ['*', 'o']-      where-        f '*' = MBlack-        f 'o' = MWhite-        f _   = impossible--instance FromChar SlalomDiag where-    parseChar '/'  = pure SlalomForward-    parseChar '\\' = pure SlalomBackward-    parseChar _    = empty--instance FromChar Black where-    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--instance (FromString a, FromString b) => FromString (Either a b) where-    parseString c = Left <$> parseString c <|> Right <$> parseString c--newtype Either' a b = Either' { unEither' :: Either a b }--instance (FromChar a, FromChar b) => FromChar (Either' a b) where-    parseChar c = Either' <$> parseChar c--instance (FromJSON a, FromJSON b) => FromJSON (Either' a b) where-    parseJSON v = Either' <$>-                      (Left <$> parseJSON v <|> Right <$> parseJSON v)--instance FromChar a => FromChar (Maybe a) where-    parseChar = optional . parseChar--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--rectToCoordGrid :: Rect a -> Grid Coord a-rectToCoordGrid (Rect _ _ ls) = listListToMap ls--blankToMaybe :: Either Blank a -> Maybe a-blankToMaybe = either (const Nothing) Just--blankToMaybe' :: Either Blank' a -> Maybe a-blankToMaybe' = either (const Nothing) Just--blankToMaybe'' :: Either Blank'' a -> Maybe a-blankToMaybe'' = either (const Nothing) Just--rectToIrregGrid :: Rect (Either Empty a) -> Grid Coord a-rectToIrregGrid = fmap fromRight . Map.filter isRight . rectToCoordGrid-  where-    fromRight (Right r) = r-    fromRight _         = impossible--newtype Shaded = Shaded { unShaded :: Bool }--instance FromChar Shaded where-    parseChar 'x'  = pure . Shaded $ True-    parseChar 'X'  = pure . Shaded $ True-    parseChar _    = pure . Shaded $ False--parseShadedGrid :: Key k => Value -> Parser (Grid k Bool)-parseShadedGrid v = fmap unShaded <$> parseGrid v--parseCoordGrid :: (FromChar a)-               => Value -> Parser (Grid Coord a)-parseCoordGrid v = rectToCoordGrid <$> parseJSON v--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' :: (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--parseExtGrid :: (Key k, FromChar a, FromJSON a) => Value -> Parser (Grid k a)-parseExtGrid = parseExtGrid' id--parseExtClueGrid :: (Key k, FromChar a, FromJSON a) => Value -> Parser (Grid k (Maybe a))-parseExtClueGrid v = fmap blankToMaybe <$> parseExtGrid' Right v--fromCoordGrid :: Key k => Grid Coord a -> Grid k a-fromCoordGrid = Map.mapKeys fromCoord--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 :: Key k => Value -> Parser [Edge k]-parsePlainEdges v = filterPlainEdges <$> parseAnnotatedEdges v--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--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.------ E.g. o-*-*-o---      |1|2 3---      *-o--- 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 <*>-                  parseAnnotatedEdges v-  where-    parseBoth = do-        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 m =-        (fromCoordGrid . divkeys $ mnode, fromCoordGrid . divkeys $ mcell)-      where-        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---      | |---      *-o--- to a grid of masyu pearls and some edges.-parseNodeEdges :: FromChar a =>-                  Value -> Parser (Grid N a, [Edge N])-parseNodeEdges v = proj13 <$> parsePlainEdgeGrid v-  where-    proj13 :: (Grid N a, Grid C Char, [Edge N])-              -> (Grid N a, [Edge N])-    proj13 (x,_,z) = (x,z)--parseCellEdges :: FromChar a =>-                  Value -> Parser (Grid C a, [Edge N])-parseCellEdges v = proj23 <$> parsePlainEdgeGrid v-  where-    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` ("└┴├┼" :: String) = pure . HalfDirs $ [Vert, Horiz]-                | c `elem` ("│┘┤"  :: String) = pure . HalfDirs $ [Vert]-                | c `elem` ("─┌┬"  :: String) = pure . HalfDirs $ [Horiz]-                | otherwise                   = pure . HalfDirs $ []---- parses a string like---  ┌┐┌─┐---  ││└┐│---  │└─┘│---  └──┐│---     └┘-parseEdges :: Key k => Value -> Parser [Edge k]-parseEdges v = do-    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 :: Grid C Alpha -> Parser [Thermometer]-parseThermos m = catMaybes <$> mapM parseThermo (Map.keys m')-  where-    m' = fmap unAlpha m-    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' :: C -> Parser Thermometer-    parseThermo' p = do-        q <- next p-        maybe (fail "no succ for thermo bulb") (fmap (p:) . parseThermo'') q-    parseThermo'' :: C -> Parser Thermometer-    parseThermo'' p = do-        q <- next p-        maybe (pure [p]) (fmap (p:) . parseThermo'') q-    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 $ 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 $ p-      where-        disjointSucc q = null $ intersect (succs p) (succs' q)-        succs' q = maybe [] (const $ succs q) (Map.lookup q m')--parseThermoGrid :: ThermoRect -> Parser (Grid C (Maybe Int), [Thermometer])-parseThermoGrid (Rect _ _ ls) = (,) ints-                              <$> parseThermos alphas-  where-    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-    parseChar '.'  = pure . Tight $ Single ()-    parseChar '/'  = pure . Tight $ UR () ()-    parseChar '\\' = pure . Tight $ DR () ()-    parseChar _    = empty--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-    parseString [c, '/',d]    = UR <$> parseChar c <*> parseChar d-    parseString [c,'\\',d]    = DR <$> parseChar c <*> parseChar d-    parseString _             = empty--newtype PMarkedWord = PMW {unPMW :: MarkedWord}--parseNWords :: Int -> String -> Parser [String]-parseNWords n s | length ws == n  = pure ws-                | otherwise       = empty-  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 . 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-    parseJSON (String t) = comp . map T.unpack . T.words $ t-        where c "." = pure Nothing-              c x   = Just <$> parseString x-              comp [n, e, s, w] = PCC <$> (CC <$> c n <*> c e <*> c s <*> c w)-              comp _            = empty-    parseJSON _          = empty--newtype PSlovakClue = PSlovakClue {unPSlovakClue :: SlovakClue}--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"--newtype RefGrid k a = RefGrid { unRG :: Grid k (Maybe a) }--hashmaptomap :: Ord a => HMap.HashMap a b -> Map.Map a b-hashmaptomap = Map.fromList . HMap.toList--compose :: Ord b => Map.Map a b -> Map.Map b c -> Maybe (Map.Map a c)-compose m1 m2 = mapM (`Map.lookup` m2) m1--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 m'--parseAfternoonGrid :: Value -> Parser (Grid C Shade)-parseAfternoonGrid v = do-    (_, _, es) <- parsePlainEdgeGrid v-                  :: Parser (Grid N Char, Grid C Char, [Edge N])-    return . toMap $ es-  where-    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-        [(fromCoord . toCoord $ p, toShade d) | E p d <- es]--newtype ParseTapaClue = ParseTapaClue { unParseTapaClue :: TapaClue }--instance FromJSON ParseTapaClue where-    parseJSON v = do xs <- parseJSON v-                     guard $ length xs > 0 && length xs <= 4-                     return . ParseTapaClue . TapaClue $ xs--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 k a)-parseCharOutside (Object v) = reorientOutside <$>-                              (OC <$>-                               pfield "left" <*> pfield "right" <*>-                               pfield "bottom" <*> pfield "top" )-  where-    pfield f = parseLine . fromMaybe [] =<< v .:? f-parseCharOutside _          = empty--parseOutside :: FromJSON a => Value -> Parser (OutsideClues k a)-parseOutside (Object v) = reorientOutside <$>-                              (OC <$>-                               pfield "left" <*> pfield "right" <*>-                               pfield "bottom" <*> pfield "top" )-  where-    pfield f = pure . fromMaybe [] =<< v .:? f-parseOutside _          = empty--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"-    v' `ml` k = fromMaybe [] <$> v' .:? k-    rev (OC l r b t) = reorientOutside $-                       OC (map reverse l) r b (map reverse t)-parseMultiOutsideClues _ = empty--instance FromChar PrimeDiag where-    parseChar '.'  = pure $ PrimeDiag (False, False)-    parseChar '/'  = pure $ PrimeDiag (True,  False)-    parseChar '\\' = pure $ PrimeDiag (False, True)-    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
@@ -2,16 +2,19 @@  module Main where -import Text.Puzzles.Puzzle-import Data.Puzzles.PuzzleTypes (typeNames, PuzzleType(..))-import Data.Puzzles.CmdLine (exitErr, readPuzzle, checkType)+import Parse.Puzzle+import Data.PuzzleTypes (typeNames, PuzzleType(..))+import Data.CmdLine (exitErr, readPuzzle, checkType) -import Data.Puzzles.Elements (digitList)-import qualified Text.Puzzles.PuzzleTypes as T+import Data.Elements (KropkiDot(..), digitList)+import Data.GridShape (Edge, N, C, rows, edgesM, unorient, ends, dualE)+import Data.Grid (Grid)+import qualified Parse.PuzzleTypes as T  import Options.Applicative import Control.Monad import Data.Maybe+import qualified Data.Map.Strict as Map import Data.List (intercalate, sort)  import System.Exit@@ -70,6 +73,7 @@ check t (pv, sv) =     case t of         ABCtje -> checkABCtje (pv, sv)+        Kropki -> checkKropki (pv, sv)         _      -> return []  checkABCtje :: (Y.Value, Y.Value) -> Y.Parser [String]@@ -92,3 +96,45 @@       where         l c = fromMaybe 0 . lookup c . map (\(x, y) -> (y, x)) $ vs         val = sum . map l++checkKropki :: (Y.Value, Y.Value) -> Y.Parser [String]+checkKropki (pv, sv) = do+    p <- fst T.kropki $ pv+    s <- snd T.kropki $ sv+    return . catMaybes . map (\c -> c p s) $+        [ match, latin, dots ]+  where+    match :: Map.Map (Edge N) KropkiDot -> Grid C Int -> Maybe String+    match p s = if solEdges == puzEdges then+                    Nothing+                else+                    Just "puzzle and solution shape don't match"+      where+        solEdges = let+            (outer, inner) = edgesM s+          in+            sort (inner ++ map unorient outer)+        puzEdges = sort (Map.keys p)+    latin _ s = either Just (const Nothing) . mapM_ latinRow . rows $ s+    latinRow ds = if sort ds == [1 .. length ds] then+                      Right ()+                  else+                      Left $ "row not 1..N: " ++ show ds+    dots p s = either Just (const Nothing) . mapM_ (checkEdge s) . Map.toList $ p+    checkEdge :: Grid C Int -> (Edge N, KropkiDot) -> Either String ()+    checkEdge s (e, d) =+      let+        (a, b) = ends (dualE e)+        okDot x y =+          let+            white = x - y == 1 || x - y == -1+            black = x == y * 2 || y == x * 2+          in+            case d of+                KBlack -> black+                KWhite -> white+                KNone  -> not black && not white+      in+        case (Map.lookup a s, Map.lookup b s) of+            (Just x, Just y) -> if okDot x y then Right () else Left ("bad dot between: " ++ show a ++ "," ++ show b)+            _                -> if d == KNone then Right () else Left "dot on the edge"
src/tools/drawpuzzle.hs view
@@ -2,17 +2,17 @@  module Main where -import Diagrams.Prelude hiding (value, option, (<>), Result)+import Diagrams.Prelude hiding (value, option, (<>), Result, render) -import Data.Puzzles.CmdLine-import Diagrams.Puzzles.CmdLine+import Data.CmdLine+import Draw.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 Parse.Puzzle+import Parse.Code+import Data.Compose+import Data.PuzzleTypes (typeNames)+import Draw.Draw+import Draw.Code  import Options.Applicative import Control.Monad@@ -36,10 +36,12 @@ data PuzzleOpts = PuzzleOpts     { _format   :: String     , _type     :: Maybe String+    , _dir      :: FilePath     , _puzzle   :: Bool     , _solution :: Bool     , _example  :: Bool     , _code     :: Bool+    , _scale    :: Double     , _input    :: FilePath     } @@ -54,6 +56,11 @@             (long "type" <> short 't'              <> metavar "TYPE"              <> help "Puzzle type, overriding type in input file"))+    <*> strOption+            (long "directory" <> short 'd'+             <> value "."+             <> metavar "DIR"+             <> help "Output directory")     <*> switch             (long "puzzle" <> short 'p'              <> help "Render puzzle (to base.ext)")@@ -66,6 +73,11 @@     <*> switch             (long "code" <> short 'c'              <> help "Add solution code markers")+    <*> option auto+            (long "scale"+             <> value 1.0+             <> metavar "FACTOR"+             <> help "Scale the size by this factor")     <*> argument str             (metavar "INPUT"              <> help "Puzzle file in .pzl format")@@ -80,15 +92,17 @@ outputSuffix DrawSolution = "-sol" outputSuffix DrawExample = "" -toRenderOpts :: OutputChoice -> Double -> PuzzleOpts -> RenderOpts-toRenderOpts oc w opts = RenderOpts out w'+toRenderOpts :: OutputChoice -> (Double, Double) -> PuzzleOpts -> RenderOpts+toRenderOpts oc (w, h) opts = RenderOpts out sz   where     f = _format opts     u = case f of "png" -> Pixels                   _     -> Points-    w' = toOutputWidth u w+    w' = toOutputWidth u w * (_scale opts)+    h' = toOutputWidth u h * (_scale opts)+    sz = mkSizeSpec2D (Just w') (Just h')     base = takeBaseName (_input opts)-    out = addExtension (base ++ outputSuffix oc) f+    out = _dir opts </> (base ++ outputSuffix oc) <.> f  renderPuzzle :: PuzzleOpts -> (OutputChoice -> Maybe (Diagram B)) ->                 (OutputChoice, Bool) -> IO ()@@ -98,7 +112,7 @@         exitErr ("failed to render (no solution?): " ++ show oc)     when (isJust x) $ do         let Just x' = x-            ropts = toRenderOpts oc (diagramWidth x') opts+            ropts = toRenderOpts oc (diagramSize x') opts         renderToFile ropts x'  defaultOpts :: Parser a -> IO a@@ -160,5 +174,5 @@     t <- checkType $ _type opts `mplus` mt     let ps = Y.parseEither (handle drawPuzzleMaybeSol t) (pv, msv')     mcode <- sequenceA $ parseAndDrawCode <$> mc'-    case ps of Right ps' -> mapM_ (renderPuzzle opts (draw mcode ps')) ocs+    case ps of Right ps' -> mapM_ (renderPuzzle opts (render mcode ps')) ocs                Left    e -> exitErr $ "parse failure: " ++ e
tests/Data.hs view
@@ -1,8 +1,8 @@ module Data where  import qualified Data.Text as T-import Data.Yaml import Data.Maybe (fromJust)+import Data.Yaml import qualified Data.ByteString as B import Data.Text.Encoding (encodeUtf8) @@ -10,7 +10,7 @@ packStr = encodeUtf8 . T.pack  decodeLines :: [String] -> Value-decodeLines = fromJust . decode . packStr . unlines+decodeLines = fromJust . decodeThrow . packStr . unlines  packLine :: String -> Value packLine = String . T.pack
+ tests/Data/GridShapeSpec.hs view
@@ -0,0 +1,17 @@+module Data.GridShapeSpec where++import Test.Hspec (Spec, describe, it, shouldBe)++import Data.GridShape+import qualified Data.Map.Strict as Map+import Data.List (sort)++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)+    describe "rows" $ do+        it "computes the rows for a simple grid" $ do+            sort (rows (Map.fromList [ ((C 1 1), 1::Int), ((C 1 2), 2), ((C 2 1), 3), ((C 2 2), 4) ]))+                `shouldBe` sort [ [1, 2], [3, 4], [1, 3], [2, 4] ]
+ tests/Data/GridSpec.hs view
@@ -0,0 +1,22 @@+module Data.GridSpec where++import qualified Data.Set as Set+import Test.Hspec (Spec, describe, it, shouldBe)++import Data.Grid+import Data.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/Data/Puzzles/GridShapeSpec.hs
@@ -1,11 +0,0 @@-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
@@ -1,22 +0,0 @@-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
@@ -1,32 +0,0 @@-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/Draw/GridSpec.hs view
@@ -0,0 +1,58 @@+module Draw.GridSpec where++import Diagrams.Prelude (p2)+import Diagrams.Path (pathPoints)+import Test.Hspec (Spec, describe, it, shouldBe, shouldSatisfy)++import Data.Grid+import Data.GridShape+import Draw.Grid+import Draw.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)++    describe "offsetBorder" $ do+        it "gives the corners of a square cell" $ do+            let b = offsetBorder 0 [C 0 0]+                [vs] = pathPoints b+            length vs `shouldBe` 4+            vs `shouldSatisfy` elem (p2i (0, 0))+            vs `shouldSatisfy` elem (p2i (1, 0))+            vs `shouldSatisfy` elem (p2i (0, 1))+            vs `shouldSatisfy` elem (p2i (1, 1))+        it "omits in-between nodes of a rectangle" $ do+            let b = offsetBorder 0 [C 0 0, C 1 0]+                [vs] = pathPoints b+            length vs `shouldBe` 4+            vs `shouldSatisfy` elem (p2i (0, 0))+            vs `shouldSatisfy` elem (p2i (2, 0))+            vs `shouldSatisfy` elem (p2i (0, 1))+            vs `shouldSatisfy` elem (p2i (2, 1))+        it "offsets inside for negative offset" $ do+            let b = offsetBorder (-0.5) [C 0 0, C 1 0, C 1 1, C 0 1]+                [vs] = pathPoints b+            length vs `shouldBe` 4+            vs `shouldSatisfy` elem (p2 (0.5, 0.5))+            vs `shouldSatisfy` elem (p2 (0.5, 1.5))+            vs `shouldSatisfy` elem (p2 (1.5, 1.5))+            vs `shouldSatisfy` elem (p2 (1.5, 0.5))
+ tests/Parse/PuzzleTypesSpec.hs view
@@ -0,0 +1,54 @@+module Parse.PuzzleTypesSpec where++import Data.Maybe (isJust)+import qualified Data.Map.Strict 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.GridShape (edgeSize)+import Data.Elements (DigitRange(..))+import Parse.PuzzleTypes++packLines :: [String] -> B.ByteString+packLines = encodeUtf8 . T.pack . unlines++parse :: (Value -> Parser a) -> B.ByteString -> Maybe a+parse p t = decodeThrow 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/Parse/UtilSpec.hs view
@@ -0,0 +1,68 @@+{-# LANGUAGE TypeFamilies #-}+module Parse.UtilSpec where++import qualified Data.Map.Strict as Map+import Data.Yaml+import qualified Data.Text as T++import Test.Hspec (Spec, describe, it, shouldBe)++import Data.GridShape (Edge(..), N(..), Dir(..))+import Data.Elements (Relation(..))+import Parse.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/Text/Puzzles/PuzzleTypesSpec.hs
@@ -1,54 +0,0 @@-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
@@ -1,68 +0,0 @@-{-# 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/compare.hs
@@ -1,27 +0,0 @@-{-# 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
@@ -4,27 +4,27 @@  import Data.Yaml import Data.List (sort)-import qualified Data.Map as Map+import qualified Data.Map.Strict as Map -import Text.Puzzles.Puzzle-import Data.Puzzles.Elements (Thermometer, MasyuPearl(..))-import Text.Puzzles.Util+import Parse.Puzzle+import Data.Elements (Thermometer, MasyuPearl(..))+import Parse.Util     (parseChar, parseMultiOutsideClues, parsePlainEdgeGrid)-import Text.Puzzles.PuzzleTypes-import qualified Data.Puzzles.Grid as Grid-import Data.Puzzles.Pyramid (PyramidSol(..))-import Data.Puzzles.Grid-import Data.Puzzles.GridShape-import Data.Puzzles.Util+import Parse.PuzzleTypes+import qualified Data.Grid as Grid+import Data.Pyramid (PyramidSol(..))+import Data.Grid+import Data.GridShape+import Data.Util   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+import qualified Data.GridSpec+import qualified Data.GridShapeSpec+import qualified Draw.GridSpec+import qualified Parse.PuzzleTypesSpec+import qualified Parse.UtilSpec  main :: IO () main = do@@ -33,11 +33,11 @@  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)+            [ ("Data.Grid",        Data.GridSpec.spec)+            , ("Data.GridShape",   Data.GridShapeSpec.spec)+            , ("Draw.Grid",    Draw.GridSpec.spec)+            , ("Parse.PuzzleTypes", Parse.PuzzleTypesSpec.spec)+            , ("Parse.Util",        Parse.UtilSpec.spec)             ]  tests :: [TestTree]