Guest User

Untitled

a guest
Oct 9th, 2009
294
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. {-# LANGUAGE ParallelListComp, RecordWildCards #-}
  2. module Main where
  3.  
  4. import Data.Char
  5. import Data.List (sort)
  6. import Data.Map (Map)
  7. import qualified Data.Map as Map
  8. import Data.Set (Set)
  9. import qualified Data.Set as Set
  10. import System.Environment (getArgs)
  11.  
  12. type Slots a = Map a (Set Int)
  13. data Puzzle = Puzzle { puzzleGrid :: Map Pos Int,
  14.                        puzzleOpen :: Set Pos,
  15.                        puzzleCols :: Slots Int,
  16.                        puzzleRows :: Slots Int,
  17.                        puzzleBlocks :: Slots Pos }
  18. type Pos = (Int, Int)
  19.  
  20. posBlock (x, y) = (x `div` 3, y `div` 3)
  21.  
  22. puzzleInsert pos@(x, y) n (Puzzle {..}) =
  23.     Puzzle (Map.insert pos n puzzleGrid) (pos `Set.delete` puzzleOpen)
  24.            (removeSlot x puzzleCols) (removeSlot y puzzleRows)
  25.            (removeSlot (posBlock pos) puzzleBlocks)
  26.   where
  27.     removeSlot :: Ord a => a -> Slots a -> Slots a
  28.     removeSlot = Map.adjust (Set.delete n)
  29.  
  30. find :: Ord a => a -> Slots a -> Set Int
  31. find = Map.findWithDefault undefined
  32.  
  33. availableNumbers pos@(x, y) (Puzzle {..}) =
  34.     find x puzzleCols `Set.intersection` find y puzzleRows
  35.     `Set.intersection` find (posBlock pos) puzzleBlocks
  36.  
  37. forwardCheck pos@(x, y) p = check (gridCols x) && check (gridRows y)
  38.                             && check (gridBlocks (posBlock pos))
  39.   where
  40.     check = not . any (\pos -> if pos `Map.member` puzzleGrid p then False
  41.                                else Set.null (availableNumbers pos p))
  42.  
  43. numValuesRuledOut :: Pos -> Puzzle -> Int -> Int
  44. numValuesRuledOut pos@(x, y) (Puzzle {..}) n =
  45.     countTrue $ [puzzleRows `has` y' && puzzleBlocks `has` posBlock b'
  46.                  | b'@(x', y') <- gridCols x, y /= y']
  47.              ++ [puzzleCols `has` x' && puzzleBlocks `has` posBlock b'
  48.                  | b'@(x', y') <- gridRows y, x /= x']
  49.              ++ [puzzleCols `has` x' && puzzleRows `has` y'
  50.                  | let b = posBlock pos, b'@(x', y') <- gridBlocks b, b /= b']
  51.   where
  52.     slots `has` ix = n `Set.member` find ix slots
  53.     countTrue = foldl (\n b -> if b then n + 1 else n) 0
  54.  
  55. numConstraints pos@(x, y) (Puzzle {..}) =
  56.     Set.size (find x puzzleCols) + Set.size (find y puzzleRows)
  57.     + Set.size (find (posBlock pos) puzzleBlocks)
  58.  
  59. gridCols x = [(x, y) | y <- [0..8]]
  60. gridRows y = [(x, y) | x <- [0..8]]
  61. gridBlocks (x, y) = [(x', y') | x' <- [x*3..x*3+2], y' <- [y*3..y*3+2]]
  62.  
  63. makePuzzle ss =
  64.     let ss'    = zip (filter (`elem` " 123456789") ss)
  65.                     [(x, y) | y <- [0..8], x <- [0..8]]
  66.        (m, o) = if length ss' /= 81 then error "Bad puzzle"
  67.                  else foldl insertElem (Map.empty, Set.empty) ss'
  68.    in Puzzle m o
  69.       (Map.fromList [(x, removeNums m (gridCols x)) | x <- [0..8]])
  70.       (Map.fromList [(y, removeNums m (gridRows y)) | y <- [0..8]])
  71.       (Map.fromList [(pos, removeNums m (gridBlocks pos))
  72.                      | x <- [0..2], y <- [0..2], let pos = (x, y)])
  73.  where
  74.    insertElem (m, o) (s, pos) | s == ' '  = (m, pos `Set.insert` o)
  75.                               | otherwise = (Map.insert pos (ord s - ord '0')
  76.                                                         m, o)
  77.  
  78.    removeNums m = foldl (\s -> maybe s (`Set.delete` s) . (`Map.lookup` m))
  79.                         (Set.fromList [1..9])
  80.  
  81. puzzleStr (Puzzle {..}) =
  82.    unlines [[maybe ' ' (chr . (ord '0' +)) ((x, y) `Map.lookup` puzzleGrid)
  83.              | x <- [0..8]] | y <- [0..8]]
  84.  
  85. -- SOLVER
  86.  
  87. main = do [fc, h, level] <- getArgs
  88.          let Just puzzle     = lookup level levels
  89.              (solution, ctr) = backtrackingSearch (read fc) (read h) puzzle
  90.          putStrLn $ puzzleStr puzzle
  91.          putStrLn $ maybe "No solution." puzzleStr solution
  92.          putStrLn $ "Searched " ++ show ctr ++ " nodes."
  93.  
  94. backtrackingSearch forwardChecking heuristics p = search p 0
  95.  where
  96.    search p n = case order [(mcv h, h) | pos <- Set.toList (puzzleOpen p),
  97.                             let h = (pos, availableNumbers pos p)] of
  98.                 ((_, pos):_) -> uncurry searchPos pos
  99.                 []           -> (Just p, n)
  100.      where
  101.        mcv (pos, nums) = (Set.size nums, -numConstraints pos p)
  102.        searchPos pos available = let vals = [(lcv val, val)
  103.                                              | val <- Set.toList available]
  104.            in foldl nextNode (Nothing, n) (snd `map` order vals)
  105.          where
  106.            lcv = numValuesRuledOut pos p
  107.            nextNode (Nothing, n) val | discardEarly = (Nothing, n+1)
  108.                                      | otherwise    = search p' (n+1)
  109.               where
  110.                 discardEarly = forwardChecking && not (forwardCheck pos p')
  111.                p'           = puzzleInsert pos val p
  112.             nextNode solution _ = solution
  113.         order :: Ord a => [a] -> [a]
  114.         order = if heuristics then sort else id
  115.  
  116. easy = makePuzzle " 2 |6 5|   \
  117.                  \ 75|   |  2\
  118.                  \ 9| 73|581\
  119.                  \---+---+---\
  120.                  \2  |81 |  9\
  121.                  \ 8 |5 9| 4 \
  122.                  \9  | 34|  8\
  123.                  \---+---+---\
  124.                  \491|38 |6  \
  125.                  \5  |   |81 \
  126.                  \  |4 1| 2 "
  127.  
  128. medium = makePuzzle " 82|46 |   \
  129.                    \ 5 |73 | 2 \
  130.                    \1  | 8 |   \
  131.                    \---+---+---\
  132.                    \2 4|   |1  \
  133.                    \  |1 6|   \
  134.                    \ 6|   |3 8\
  135.                    \---+---+---\
  136.                    \  | 9 |  6\
  137.                    \ 2 | 78| 1 \
  138.                    \  | 14|73 "
  139.  
  140. hard = makePuzzle "  6|   |1  \
  141.                  \7  | 61|   \
  142.                  \ 1 |   |5  \
  143.                  \---+---+---\
  144.                  \ 9 |1 6|7  \
  145.                  \5  |973|  8\
  146.                  \ 2|4 8| 9 \
  147.                  \---+---+---\
  148.                  \ 5|   | 2 \
  149.                  \  |83 |  6\
  150.                  \ 8|   |9  "
  151.  
  152. evil = makePuzzle " 2 |  8|6  \
  153.                  \8  |  7|   \
  154.                  \ 73| 4 |   \
  155.                  \---+---+---\
  156.                  \2  |  5| 9 \
  157.                  \5  |6 9|  4\
  158.                  \ 9 |3  |  5\
  159.                  \---+---+---\
  160.                  \  | 9 |31 \
  161.                  \  |2  |  7\
  162.                  \ 2|4  | 8 "
  163.  
  164. levels = zip ["easy", "medium", "hard", "evil"] [easy, medium, hard, evil]
Advertisement
Add Comment
Please, Sign In to add comment