Tackled problem 1625
[cses-programming-tasks.git] / app / cses1625-array-count.hs
diff --git a/app/cses1625-array-count.hs b/app/cses1625-array-count.hs
new file mode 100644 (file)
index 0000000..32989b3
--- /dev/null
@@ -0,0 +1,138 @@
+{-# LANGUAGE RecordWildCards, NamedFieldPuns #-}
+
+import Data.Ix
+-- import qualified Data.Set as S
+import qualified Data.Array.Unboxed as U
+import qualified Data.Array.IArray as A
+import Data.Array.IArray ((!), (//))
+import Control.Monad
+
+
+data Point = Point Int Int -- row, column
+  deriving (Show, Eq, Ord, Ix)
+
+-- type Grid = S.Set Point
+type Grid = U.UArray 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)
+
+data State = State { visited :: Grid
+                   , pos :: Point
+                   , trail :: [Direction]
+                   , constraints :: [DirectionConstraint]
+                   }
+  deriving (Show, Eq, Ord)
+
+initialState :: [DirectionConstraint] -> State
+initialState cs = 
+  State { visited = A.array gridBounds [(i, False) | i <- range gridBounds] // [(pathStart, True)]
+        , pos = pathStart
+        , constraints = cs
+        , trail = []
+        }
+
+
+main :: IO ()
+main = do
+  let cs = replicate fullPathLength Unknown
+  -- let cs = readConstraints "??????R??????U??????????????????????????LD????D?"
+  -- let cs = readConstraints "DRURRRRRDDDLUULDDDLDRRURDDLLLLLURULURRUULDLLDDDD"
+  -- line <- getLine
+  -- let cs = readConstraints line
+  -- let paths = allPaths cs
+  print $ allPathsCount cs
+  -- print paths
+  -- let n = read line
+  -- let nums = unfoldr collatzStep n
+  -- let outStr = intercalate " " $ map show nums
+  -- putStrLn outStr
+
+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 :: Direction -> DirectionConstraint -> Bool
+compatible U CU = True
+compatible R CR = True
+compatible D CD = True
+compatible L CL = True
+compatible _ Unknown = True
+compatible _ _ = False
+
+
+isComplete :: State -> Bool
+isComplete (State {..}) =
+  pos == pathEnd && length trail == fullPathLength
+
+successors :: State -> [State]
+successors state@(State {..}) 
+  | pos == pathEnd = []
+  | isComplete state = []
+  | otherwise =
+      do dir <- [minBound..maxBound] 
+         let givenDirection = head constraints
+         guard $ compatible dir givenDirection
+         let nextPos = pos ^+^ (delta dir)
+         guard $ inRange gridBounds nextPos
+         guard $ not $ visited ! nextPos
+         let nextVisited = visited // [(nextPos, True)]
+         return $ State { visited = nextVisited
+                        , pos = nextPos
+                        , constraints = tail constraints
+                        , trail = dir : trail
+                        }
+
+allPathsCount :: [DirectionConstraint] -> Int
+allPathsCount constraints = dfs [initialState constraints] 0
+
+dfs :: [State] -> Int -> Int
+dfs [] completeds = completeds
+dfs (current:agenda) completeds
+  | isComplete current = dfs agenda (completeds + 1)
+  | oppositePair succs = dfs agenda completeds
+  | otherwise = dfs (succs ++ agenda) completeds
+  where
+    succs = successors current
+    oppositePair ss
+      | length ss /= 2 = False
+      | otherwise = opposite (pos $ ss !! 0) (pos $ ss !! 1)