Day2.hs (3627B)
1 module Day2 (solution) where 2 3 import Control.Monad (join, liftM) 4 import Data.List (genericLength) 5 import Utils (SolType(..), safeRead, integerDiff, xor, (|||)) 6 7 input :: IO String 8 input = readFile "inputs/day2" 9 10 exampleInput :: String 11 exampleInput = join [ 12 "7 6 4 2 1\n", 13 "1 2 7 8 9\n", 14 "9 7 6 2 1\n", 15 "1 3 2 4 5\n", 16 "8 6 4 4 1\n", 17 "1 3 6 7 9\n" 18 ] 19 20 type Level = Integer 21 22 -- | Parse the input into a list of level lists 23 -- Example: 24 -- >>> parse exampleInput 25 -- Just [[7,6,4,2,1],[1,2,7,8,9],[9,7,6,2,1],[1,3,2,4,5],[8,6,4,4,1],[1,3,6,7,9]] 26 -- >>> parse "this is an invalid string" 27 -- Nothing 28 parse :: String -> Maybe [[Level]] 29 parse str = mapM ((mapM safeRead) . words) $ lines str 30 31 -- | Determine whether a sequence is safe or not according to the instructions 32 -- of part 1 33 -- Example: 34 -- >>> safeSequence [7,6,4,2,1] 35 -- True 36 -- >>> safeSequence [1,2,7,8,9] 37 -- False 38 -- >>> safeSequence [9,7,6,2,1] 39 -- False 40 -- >>> safeSequence [1,3,2,4,5] 41 -- False 42 -- >>> safeSequence [8,6,4,4,1] 43 -- False 44 -- >>> safeSequence [1,3,6,7,9] 45 -- True 46 safeSequence :: [Level] -> Bool 47 safeSequence = helper Nothing Nothing 48 where 49 -- This could almost definitely be done as a fold operation but I don't care 50 helper :: Maybe Bool -> Maybe Integer -> [Level] -> Bool 51 helper Nothing Nothing (x:y:ys) 52 | x == y = False 53 | otherwise = helper (Just $ x < y) (Just x) (y:ys) 54 helper Nothing _ _ = False 55 helper _ Nothing _ = False 56 helper (Just pos) (Just x) (y:ys) = (not $ xor pos $ x < y) 57 && diff <= 3 && diff > 0 58 && helper (Just pos) (Just y) ys 59 where diff = integerDiff x y 60 helper _ _ [] = True 61 62 -- | Solution to day 2 part 1 63 -- Example 64 -- >>> liftM solution1 $ parse exampleInput 65 -- Just 2 66 solution1 :: [[Level]] -> Integer 67 solution1 = genericLength . filter safeSequence 68 69 -- | Finds all subsequences of the given with one less element 70 -- Example: 71 -- >>> reverse $ subSeqs [7,6,4,2,1] 72 -- [[6,4,2,1],[7,4,2,1],[7,6,2,1],[7,6,4,1],[7,6,4,2]] 73 -- >>> reverse $ subSeqs [1,2,7,8,9] 74 -- [[2,7,8,9],[1,7,8,9],[1,2,8,9],[1,2,7,9],[1,2,7,8]] 75 -- >>> reverse $ subSeqs [9,7,6,2,1] 76 -- [[7,6,2,1],[9,6,2,1],[9,7,2,1],[9,7,6,1],[9,7,6,2]] 77 -- >>> reverse $ subSeqs [1,3,2,4,5] 78 -- [[3,2,4,5],[1,2,4,5],[1,3,4,5],[1,3,2,5],[1,3,2,4]] 79 -- >>> reverse $ subSeqs [8,6,4,4,1] 80 -- [[6,4,4,1],[8,4,4,1],[8,6,4,1],[8,6,4,1],[8,6,4,4]] 81 -- >>> reverse $ subSeqs [1,3,6,7,9] 82 -- [[3,6,7,9],[1,6,7,9],[1,3,7,9],[1,3,6,9],[1,3,6,7]] 83 subSeqs :: [Level] -> [[Level]] 84 subSeqs = helper [] [] 85 where 86 helper :: [[Level]] -> [Level] -> [Level] -> [[Level]] 87 helper acc prev (x:xs) = helper (subList : acc) (x : prev) xs 88 where subList = reverse prev ++ xs 89 helper acc _ [] = acc 90 91 -- Brute forcing this out of laziness 92 -- | Determine whether a sequence is safe or not with dampening 93 -- Example: 94 -- >>> safeDampedSequence [7,6,4,2,1] 95 -- True 96 -- >>> safeDampedSequence [1,2,7,8,9] 97 -- False 98 -- >>> safeDampedSequence [9,7,6,2,1] 99 -- False 100 -- >>> safeDampedSequence [1,3,2,4,5] 101 -- True 102 -- >>> safeDampedSequence [8,6,4,4,1] 103 -- True 104 -- >>> safeDampedSequence [1,3,6,7,9] 105 -- True 106 safeDampedSequence :: [Level] -> Bool 107 safeDampedSequence = safeSequence ||| ((any safeSequence) . subSeqs) 108 109 -- | Solution to day 2 part 2 110 -- Example 111 -- >>> liftM solution2 $ parse exampleInput 112 -- Just 4 113 solution2 :: [[Level]] -> Integer 114 solution2 = genericLength . filter safeDampedSequence 115 116 solution :: IO (SolType, SolType) 117 solution = do 118 parsedLists <- liftM parse input 119 let sol1 = liftM (IntSol . solution1) parsedLists 120 let sol2 = liftM (IntSol . solution2) parsedLists 121 return (MaybeSol sol1, MaybeSol sol2)