Tackled problem 1625
[cses-programming-tasks.git] / app / cses1625-mutable.hs
diff --git a/app/cses1625-mutable.hs b/app/cses1625-mutable.hs
new file mode 100644 (file)
index 0000000..4a53fc0
--- /dev/null
@@ -0,0 +1,138 @@
+{-# LANGUAGE MultiWayIf #-}
+
+import Debug.Trace
+
+import Data.Ix
+import qualified Data.Array.ST as U
+import qualified Data.Array.MArray as A
+import Control.Monad
+import Control.Monad.ST
+import Data.STRef as S
+
+
+data Point = Point Int Int -- row, column
+  deriving (Show, Eq, Ord, Ix)
+
+type Grid s = U.STUArray s Point Bool
+
+gridBounds = (Point 1 1, Point 7 7)
+pathStart = Point 1 1
+pathEnd = Point 7 1
+fullPathLength = 48
+
+-- gridBounds = (Point 1 1, Point 2 2)
+-- pathStart = Point 1 1
+-- pathEnd = Point 2 1
+-- fullPathLength = 3
+
+-- gridBounds = (Point 1 1, Point 4 4)
+-- pathStart = Point 1 1
+-- pathEnd = Point 4 1
+-- fullPathLength = 15
+
+data Direction = U | R | D | L
+  deriving (Show, Eq, Ord, Enum, Bounded)
+
+data DirectionConstraint =  CU | CR | CD | CL | Unknown
+  deriving (Show, Eq, Ord, Enum, Bounded)
+
+
+main :: IO ()
+main = do
+  -- let cs = replicate fullPathLength Unknown
+  -- let cs = [CR, CD, CL]
+  -- let cs = readConstraints "??????R??????U??????????????????????????LD????D?"
+  -- let cs = readConstraints "DRURRRRRDDDLUULDDDLDRRURDDLLLLLURULURRUULDLLDDDD"
+  -- line <- getLine
+  let cs = readConstraints line
+  let paths = allPaths cs
+  print $ allPathsCount cs
+
+
+readConstraints :: String -> [DirectionConstraint]
+readConstraints = map readConstraint
+
+readConstraint :: Char -> DirectionConstraint
+readConstraint 'U' = CU
+readConstraint 'R' = CR
+readConstraint 'D' = CD
+readConstraint 'L' = CL
+readConstraint _ = Unknown
+
+delta :: Direction -> Point
+delta U = Point (-1) 0
+delta R = Point 0 1
+delta D = Point 1 0
+delta L = Point 0 (-1)
+
+(^+^) :: Point -> Point -> Point
+(Point r1 c1) ^+^ (Point r2 c2) = Point (r1 + r2) (c1 + c2)
+
+opposite :: Point -> Point -> Bool
+opposite (Point r1 c1) (Point r2 c2) 
+  | r1 == r2 && c1 /= c2 = True
+  | c1 == c2 && r1 /= r2 = True
+  | otherwise = False
+
+
+compatible :: DirectionConstraint -> Direction -> Bool
+compatible CU U = True
+compatible CR R = True
+compatible CD D = True
+compatible CL L = True
+compatible Unknown _ = True
+compatible _ _ = False
+
+directionsOf :: DirectionConstraint -> [Direction]
+directionsOf CU = [U]
+directionsOf CR = [R]
+directionsOf CD = [D]
+directionsOf CL = [L]
+directionsOf Unknown = [minBound..maxBound]
+
+
+allPathsCount :: [DirectionConstraint] -> Int
+allPathsCount constraints = runST $ do
+  visited <- U.newArray gridBounds False
+  count <- S.newSTRef 0
+  dfs constraints visited count pathStart
+  S.readSTRef count
+
+
+dfs :: [DirectionConstraint] -> Grid s -> S.STRef s Int -> Point -> ST s ()
+-- dfs cs _ _ pos | trace ("dfs " ++ (show cs ++ " " ++ (show pos))) False = undefined
+dfs [] visited count pos = do 
+  A.writeArray visited pos True
+  completed <- isComplete visited pos
+  when completed $ S.modifySTRef' count (+1)
+  A.writeArray visited pos False
+dfs (c:cs) visited count pos = do
+  A.writeArray visited pos True
+  completed <- isComplete visited pos
+  if | completed -> S.modifySTRef' count (+1)
+     | pos == pathEnd -> return ()
+     | otherwise -> do succs <- successors visited pos c
+                       mapM_ (dfs cs visited count) succs
+  A.writeArray visited pos False
+
+isComplete :: Grid s -> Point -> ST s Bool
+isComplete grid pos 
+  | pos == pathEnd = 
+      do  cells <- A.getElems grid
+          let allVisited = and cells
+          return allVisited
+  | otherwise = return False
+
+
+successors :: Grid s -> Point -> DirectionConstraint -> ST s [Point]
+successors grid pos constraint = do
+  let directions = {-# SCC "dir" #-} directionsOf constraint
+  let nextPoses0 = {-# SCC "pos0" #-} map ((^+^ pos) . delta) directions
+  let nextPoses1 = {-# SCC "inbouds" #-} filter (inRange gridBounds) nextPoses0
+  nextPoses2 <- {-# SCC "unvisited" #-} filterM ((liftM not) . (A.readArray grid)) nextPoses1
+  return $ {-# SCC "opps" #-} if length nextPoses2 == 2 then
+              let [p1, p2] = nextPoses2
+              in if opposite p1 p2 then [] else nextPoses2
+           else nextPoses2
+
+