set-cover-0.1: 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), ColorMap)
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.BitPriorityQueue as BitPQ
import qualified Math.SetCover.Queue as Queue
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.OrdPSQ (OrdPSQ)
import Data.IntPSQ (IntPSQ)
import Data.EnumSet (EnumSet)
import Data.IntSet (IntSet)
import Data.Foldable (foldMap)
import Data.NonEmpty ((!:))
import Data.Word (Word64)
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 -> ColorMap -> 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
type Evolve queue set = ([[Int]], [[Int]]) -> [[ESC.State queue ColorMap set]]
evolveGen ::
Queue.Methods queue set ->
[ESCS.Assign ColorMap set] -> [[ESC.State queue ColorMap set]]
evolveGen methods assigns =
takeWhile (not . null) $
iterate
(concatMap (ESC.step methods) .
filter (not . Queue.null methods . ESC.queue))
[ESC.initState methods assigns]
evolveQueueMapBW ::
Evolve
(OrdPSQ Strip Int (OrdPSQ BlackWhite.Item Int (EnumSet Queue.SetId)))
(Map Strip (Set BlackWhite.Item))
evolveQueueMapBW = evolveGen queueMap . uncurry BlackWhite.assignsBW
evolveQueueMap ::
Evolve
(OrdPSQ Strip Int (OrdPSQ Combinatoric.Item Int (EnumSet Queue.SetId)))
(Map Strip (Set Combinatoric.Item))
evolveQueueMap = evolveGen queueMap . uncurry Combinatoric.assignsBW
evolveQueueMapBit ::
Evolve
(OrdPSQ Strip Int (IntPSQ Int (EnumSet Queue.SetId)))
(Map Strip (BitSet.Set Word64))
evolveQueueMapBit =
evolveGen queueMapBit .
Combinatoric.bitAssigns . uncurry Combinatoric.assignsBW
evolveQueueBitPQ ::
Evolve (BitPQ.Queue Integer Queue.SetId) (BitSet.Set Integer)
evolveQueueBitPQ (rows, columns) =
evolveGen ESC.queueBitPQ $
Combinatoric.bitVectorAssigns (length rows) (length columns) $
Combinatoric.assignsBW rows columns
evolveQueueBit ::
Evolve (IntPSQ Int (EnumSet Queue.SetId)) (BitSet.Set Integer)
evolveQueueBit (rows, columns) =
evolveGen ESC.queueBit $
Combinatoric.bitVectorAssigns (length rows) (length columns) $
Combinatoric.assignsBW rows columns
evolveQueueIntSet :: Evolve (IntPSQ Int (EnumSet Queue.SetId)) IntSet
evolveQueueIntSet (rows, columns) =
evolveGen ESC.queueIntSet $
Combinatoric.intSetAssigns (length rows) (length columns) $
Combinatoric.assignsBW rows columns
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) $
evolveQueueMap (rows, columns)
main :: IO ()
main = evolve Example.soccerEnc