packages feed

obdd-0.8.2: examples/Domino.hs

{-
In how many ways can a n x m board be entirely covered by 2 x 1 domino
pieces?

Usage: ./Domino 4 3
       time stack runghc examples/Domino.hs 4 9
       VERBOSE=1 stack runghc examples/Domino.hs 2 3
   or: ghci> prettySolve 4 3
-}
module Main where

import Prelude hiding (bool,not,and,or,any,all,(&&),(||))
import Control.Monad (guard, when)
import Data.Maybe (isJust)
import System.Environment (getArgs, lookupEnv)
import qualified Data.Map
import qualified Data.Set
import Data.List (tails)

import Prelude hiding ((&&),(||),not,and,or,all,any)
import OBDD

dominoes w h = rows ++ cols
    where rows = positions [1..h-1] [1..w] $ \x y -> ((x, y), (x+1, y))
          cols = positions [1..h] [1..w-1] $ \x y -> ((x, y), (x, y+1))
          positions xs ys f = f <$> xs <*> ys

positions w h = (,) <$> [1..h] <*> [1..w]

dominoFormula w h =
  all ( \ f -> exactly_one (map variable $ filter (isCoveredBy f) (dominoes w h) )
      ) (positions w h)

f `isCoveredBy` p = f == fst p || f == snd p

exactly_one xs = or xs && atmost_one xs

atmost_one :: Boolean b => [b] -> b
atmost_one = atmost_one_lin

atmost_one_lin xs =
  let go [] = (true,false)
      go [x] = (not x, x)
      go xs = let (ys,zs) = splitAt (div (length xs) 2) xs
                  (y0,y1) = go ys
		  (z0,z1) = go zs
              in  (y0 && z0, y1 && z0 || y0 && z1)
      (x0,x1) = go xs
  in  x0 || x1

atmost_one_quad xs = not $ or $ do
  p <- xs ; q <- xs ; guard $ p < q
  return $ and [ OBDD.unit p True, OBDD.unit q True ]


prettySolution w h ds = putStr $ concat $ do
    x <- [1..h]
    y <- [1..w]
    let letter = case find (\(domino, _) -> fst domino == (x, y) || snd domino == (x, y)) withNames of
                     Nothing -> "."
                     Just (_, letter) -> letter:[]
    return $ if y == w then letter ++ "\n" else letter
    where withNames = if length names < length ds
                      then error "not enough names"
                      else zip ds names
          names = ['0'..'9'] ++ ['a'..'z'] ++ ['A'..'Z']
          find f xs = case filter f xs of
                          []  -> Nothing
                          x:_ -> Just x

prettySolve w h =
    mapM_ (\ds -> prettySolution w h ds >> putStrLn "")
    . map (map fst . filter snd)
    . map (Data.Map.toList) $
    models (Data.Set.fromList $ dominoes w h) $ dominoFormula w h

main = do
    args <- getArgs
    let [width, height] = case map read args :: [Int] of
              [] -> [4, 3]
              [width, height] -> [width, height]
    let formula = dominoFormula width height
    verbose <- lookupEnv "VERBOSE" >>= return . isJust
    when verbose $ mapM_ (\ds -> prettySolution width height ds >> putStrLn "")
                   . map (map fst . filter snd)
                   . map (Data.Map.toList) $
                   models (Data.Set.fromList $ dominoes width height) formula
    print $ number_of_models (Data.Set.fromList $ dominoes width height) formula