packages feed

set-cover-0.0.8: example/Nonogram.hs

{-
* <https://en.wikipedia.org/wiki/Nonogram>
* <https://de.wikipedia.org/wiki/Datei:Paint_by_numbers_Animation.gif>

With the Combinatoric encoding and the priority queue based solver
the performance is appropriate but specialized solvers are still faster.
-}
module Main where

import qualified Nonogram.Example as Example
import qualified Nonogram.Encoding.Combinatoric as Combinatoric
import qualified Nonogram.Encoding.BlackWhite as BlackWhite
import qualified Nonogram.Encoding.Plug as Plug
import qualified Nonogram.Encoding.Naive as Naive
import Nonogram.Base (Strip, Color(White, Black))

import qualified Math.SetCover.Exact.Priority as ESC
import qualified Math.SetCover.Exact as ESCS
import qualified Math.SetCover.BitPosition as BitPos
import qualified Math.SetCover.BitSet as BitSet
import qualified Math.SetCover.Queue as Queue

import qualified Data.OrdPSQ as PSQ; import Data.OrdPSQ (OrdPSQ)
import qualified Data.Map as Map; import Data.Map (Map)
import qualified Data.Set as Set; import Data.Set (Set)
import qualified Data.EnumMap as EnumMap
import qualified Data.NonEmpty as NonEmpty
import qualified Data.List.HT as ListHT
import Data.IntPSQ (IntPSQ)
import Data.EnumSet (EnumSet)
import Data.Foldable (foldMap)
import Data.NonEmpty ((!:))


decode :: [[Int]] -> [[Int]] -> [Set (Int, Int)]
decode rows columns =
   map Set.unions $
   case 01::Int of
      00 -> ESC.partitions ESC.queueBit $
               Combinatoric.bitVectorAssigns (length rows) (length columns) $
               Combinatoric.assigns rows columns
      01 -> ESC.partitions queueMapBit $
               Combinatoric.bitAssigns $ Combinatoric.assigns rows columns
      02 -> ESCS.partitions $
               Combinatoric.intSetAssigns (length rows) (length columns) $
               Combinatoric.assigns rows columns
      03 -> partitionsSet $ Combinatoric.assigns rows columns
      10 -> ESC.partitions queueMapBit $
               BlackWhite.bitAssigns rows columns $
               BlackWhite.assigns rows columns
      11 -> partitionsSet $ BlackWhite.assigns rows columns
      20 -> partitionsSet $ Plug.assigns rows columns
      _  -> partitionsSet $ Naive.assigns rows columns

partitionsSet :: (Ord a) => [ESC.Assign label (Map Strip (Set a))] -> [[label]]
partitionsSet = ESC.partitions queueMap

queueMap ::
   (Ord a) =>
   Queue.Methods
      (OrdPSQ Strip Int (OrdPSQ a Int (EnumSet Queue.SetId)))
      (Map Strip (Set a))
queueMap = ESC.queueMap ESC.queueSet

queueMapBit ::
   (BitPos.C bits) =>
   Queue.Methods
      (OrdPSQ Strip Int (IntPSQ Int (EnumSet Queue.SetId)))
      (Map Strip (BitSet.Set bits))
queueMapBit = ESC.queueMap ESC.queueBit


format :: Int -> Int -> Set (Int, Int) -> String
format rows columns set =
   unlines $
   ListHT.outerProduct
      (\r c -> if Set.member (r,c) set then 'X' else '.')
      (take rows [0..])
      (take columns [0..])

formatBW :: Int -> Int -> Map (Int, Int) Color -> String
formatBW rows columns set =
   unlines $
   ListHT.outerProduct
      (\r c ->
         case Map.lookup (r,c) set of
            Nothing -> '_'
            Just Black -> 'X'
            Just White -> '.')
      (take rows [0..])
      (take columns [0..])

besidesMany :: Int -> [String] -> String
besidesMany space =
   let besides blockL blockR =
         let width = NonEmpty.maximum (0 !: map length blockL) + space
         in  zipWith (\l r -> ListHT.padRight ' ' width l ++ r) blockL blockR
   in  unlines . foldr1 besides . map lines

testSimple :: ([[Int]], [[Int]]) -> IO ()
testSimple (rows, columns) = do
   let assigns = Naive.assigns rows columns
   mapM_ (print . ESC.labeledSet) assigns
   putStrLn "set union:"
   print $ foldMap ESC.labeledSet assigns
   mapM_ (putStrLn . format (length rows) (length columns) . Set.unions) $
      partitionsSet assigns

decodeImage :: ([[Int]], [[Int]]) -> IO ()
decodeImage (rows, columns) =
   mapM_ (putStrLn . format (length rows) (length columns)) $
   decode rows columns

testImage :: IO ()
testImage = decodeImage $ Example.encodeStrings Example.letterP

evolve :: ([[Int]], [[Int]]) -> IO ()
evolve (rows, columns) =
   let formatIntermediate state =
          (show $ EnumMap.size $ ESC.availableSubsets state) ++ '\n' :
          (formatBW (length rows) (length columns) .
           Map.unionsWith (error "conflicting colors") .
           ESC.usedSubsets $ state)
   in  mapM_ (putStrLn . besidesMany 2 . map formatIntermediate) $
       fst $ ListHT.breakAfter (all (PSQ.null . ESC.queue)) $
       iterate (concatMap (ESC.step queueMap))
         [ESC.initState queueMap $ Combinatoric.assignsBW rows columns]

main :: IO ()
main = evolve Example.soccerEnc