{- Juergen Goering Labyrinth der Denkspiele, Seite 127 Zerlege folgende Felder in Dominosteine: 3134205 3110266 3550426 6321201 5045254 3660301 5451243 6264410 25114225 25304365 11305361 24465661 23560204 63540204 60043311 -} module Main where import qualified Math.SetCover.Exact as ESC import qualified Data.List.HT as ListHT import qualified Data.Set as Set import Data.Set (Set) import Data.Monoid (Monoid, mempty, mappend, mconcat) data Score = S0 | S1 | S2 | S3 | S4 | S5 | S6 deriving (Eq, Ord, Show, Enum, Bounded) data X = Brick Score Score | Position Int Int deriving (Eq, Ord, Show) type Position = (Int, Int) data Corner = Corner (Bool, Bool) (Bool, Bool) deriving (Eq) data Borders = Borders { vertical, horizontal :: Set Position } type Assign = ESC.Assign Borders (Set X) instance Monoid Borders where mempty = Borders mempty mempty mappend x y = Borders { vertical = Set.union (vertical x) (vertical y), horizontal = Set.union (horizontal x) (horizontal y) } above, left :: Position -> Position above (row,col) = (row-1, col) left (row,col) = (row, col-1) below, right :: Position -> Position below (row,col) = (row+1, col) right (row,col) = (row, col+1) brick :: Score -> Score -> X brick x y = Brick (min x y) (max x y) assign :: (Ord a) => map -> [a] -> ESC.Assign map (Set a) assign m xs = ESC.assign m (Set.fromList xs) attachPositions :: [[a]] -> [[(Position, a)]] attachPositions = zipWith (\x -> zipWith (\y -> (,) (x,y)) [0..]) [0..] assigns :: [[Score]] -> [Assign] assigns xs = let ps = attachPositions xs in concat (ListHT.mapAdjacent (zipWith (\(p0,b0) (p1,b1) -> assign (Borders { vertical = Set.fromList $ p0 : p1 : right p0 : right p1 : [], horizontal = Set.fromList $ p0 : below p1 : [] }) [brick b0 b1, uncurry Position p0, uncurry Position p1])) ps) ++ concatMap (ListHT.mapAdjacent (\(p0,b0) (p1,b1) -> assign (Borders { horizontal = Set.fromList $ p0 : p1 : below p0 : below p1 : [], vertical = Set.fromList $ p0 : right p1 : [] }) [brick b0 b1, uncurry Position p0, uncurry Position p1])) ps formatBar :: Corner -> Char formatBar set = case set of Corner (False, False) (False, False) -> ' ' Corner (False, False) (True, True ) -> '\x2500' Corner (True, True ) (False, False) -> '\x2502' Corner (True, True ) (True, True ) -> '\x253C' Corner (False, False) (False, True ) -> '\x2574' Corner (False, False) (True , False) -> '\x2576' Corner (False, True ) (False, False) -> '\x2577' Corner (True, False) (False, False) -> '\x2575' Corner (False, True ) (False, True ) -> '\x250C' Corner (False, True ) (True, False) -> '\x2510' Corner (True, False) (False, True ) -> '\x2514' Corner (True, False) (True, False) -> '\x2518' Corner (True, True ) (False, True ) -> '\x251C' Corner (True, True ) (True, False) -> '\x2524' Corner (False, True ) (True, True ) -> '\x252C' Corner (True, False) (True, True ) -> '\x2534' double :: a -> (a,a) double a = (a,a) formatCorner, formatHorizontal, formatVertical :: Borders -> Position -> Char formatCorner m p = formatBar $ Corner (Set.member (above p) (vertical m), Set.member p (vertical m)) (Set.member (left p) (horizontal m), Set.member p (horizontal m)) formatHorizontal m p = formatBar (Corner (False,False) (double $ Set.member p (horizontal m))) formatVertical m p = formatBar (Corner (double $ Set.member p (vertical m)) (False,False)) {- | @mapIntersperse f g [a,b,c]@ computes @[f 0, g 0 a, f 1, g 1 b, f 2, g 2 c, f 3]@ -} mapIntersperse :: (Int -> b) -> (Int -> a -> b) -> [a] -> [b] mapIntersperse f g xs = f 0 : concat (zipWith (\n x -> [g n x, f (n+1)]) [0..] xs) format :: [[Score]] -> Borders -> String format xss m = unlines $ mapIntersperse (\row -> mapIntersperse (\col -> formatCorner m (row,col)) (\col _ -> formatHorizontal m (row,col)) (case xss of xs:_ -> xs; [] -> [])) (\row -> mapIntersperse (\col -> formatVertical m (row,col)) (\ _col n -> toEnum $ fromEnum '0' + fromEnum n)) xss fieldSimple :: [[Score]] fieldSimple = map (map toEnum) $ (3:1:3:4:2:0:5:[]) : (3:1:1:0:2:6:6:[]) : (3:5:5:0:4:2:6:[]) : (6:3:2:1:2:0:1:[]) : (5:0:4:5:2:5:4:[]) : (3:6:6:0:3:0:1:[]) : (5:4:5:1:2:4:3:[]) : (6:2:6:4:4:1:0:[]) : [] fieldRussian :: [[Score]] fieldRussian = map (map toEnum) $ (2:5:1:1:4:2:2:5:[]) : (2:5:3:0:4:3:6:5:[]) : (1:1:3:0:5:3:6:1:[]) : (2:4:4:6:5:6:6:1:[]) : (2:3:5:6:0:2:0:4:[]) : (6:3:5:4:0:2:0:4:[]) : (6:0:0:4:3:3:1:1:[]) : [] main :: IO () main = let field = fieldRussian in mapM_ (putStrLn . format field . mconcat) $ ESC.partitions $ assigns field