diff --git a/common/src/Advent/Queue.hs b/common/src/Advent/Queue.hs index 1604f9b..0684753 100644 --- a/common/src/Advent/Queue.hs +++ b/common/src/Advent/Queue.hs @@ -115,7 +115,7 @@ snoc :: a -> Queue a -> Queue a snoc x (Queue f r s) = exec f (x:r) s exec :: [a] -> [a] -> Int -> Queue a -exec f r 0 = fromList (rotate f r []) +exec f r 0 = fromList (rotate f r []) exec f r i = Queue f r (i-1) rotate :: [a] -> [a] -> [a] -> [a] diff --git a/solutions/src/2023/20.hs b/solutions/src/2023/20.hs index 463de84..e06a382 100644 --- a/solutions/src/2023/20.hs +++ b/solutions/src/2023/20.hs @@ -73,6 +73,7 @@ part2 incoming = foldl lcm 1 . go 0 (Set.fromList (Map.findWithDefault [] conj i where [conj] = incoming Map.! "rx" + -- finds the first button count for each gate in gates go n gates ((src, dst, msg) :| xs) | Set.null gates = [] | "button" == src = go (n + 1) gates xs diff --git a/solutions/src/2023/23.hs b/solutions/src/2023/23.hs index 106d15e..c82a90c 100644 --- a/solutions/src/2023/23.hs +++ b/solutions/src/2023/23.hs @@ -1,4 +1,4 @@ -{-# Language BangPatterns, ImportQualifiedPost #-} +{-# Language BangPatterns, LambdaCase, ImportQualifiedPost #-} {-| Module : Main Description : Day 23 solution @@ -56,61 +56,61 @@ main :: IO () main = do input <- getInputArray 2023 23 let (_, C ymax _) = bounds input - let input1 = buildPaths input part1 - let input2 = buildPaths input part2 - print (maximum (enum ymax (C 0 1) input1 0)) - print (maximum (enum ymax (C 0 1) input2 0)) + let solve = maximum . enum ymax (C 0 1) 0 . buildPaths input + print (solve part1) + print (solve part2) -- | Generate all the possible distances from the start to the end. -enum :: Int -> Coord -> Map Coord [(Coord, Int)] -> Int -> [Int] -enum !ymax !here edges !dist +enum :: Int -> Coord -> Int -> Map Coord [(Coord, Int)] -> [Int] +enum !ymax !here !dist edges | coordRow here == ymax = [dist] | otherwise = do let edges' = Map.delete here edges (next, cost) <- Map.findWithDefault [] here edges - enum ymax next edges' (dist + cost) + enum ymax next (dist + cost) edges' +-- | Build a map of locations and distances reachable from each key. buildPaths :: - UArray Coord Char -> - (Char -> Coord -> Bool) -> + UArray Coord Char {- ^ input grid -} -> + (Coord -> Char -> Bool) {- ^ adjacency rule -} -> Map Coord [(Coord, Int)] buildPaths input isOpen = go Map.empty (C 0 1) where (_, C ymax _) = bounds input - go acc x - | Map.member x acc = acc - | otherwise = foldl go (Map.insert x reachable acc) (map fst reachable) + go acc c + | Map.member c acc = acc -- already computed, skip + | otherwise = foldl go (Map.insert c reachable acc) (map fst reachable) where - reachable = - do c <- adj input isOpen x - walk c x 1 + reachable = map (walk 1 c) (adj input isOpen c) - walk here there dist = - case delete there (adj input isOpen here) of - [next] | coordRow next /= ymax -> walk next here (dist+1) - _ -> [(here, dist)] + -- find the next intersection in this direction and track the distance to it + walk dist prev cur + | [next] <- delete prev (adj input isOpen cur) -- only one next location + , coordRow next /= ymax -- not the terminal location + = walk (dist + 1) cur next -- keep walking -adj :: UArray Coord Char -> (Char -> Coord -> Bool) -> Coord -> [Coord] -adj input isOpen here = + | otherwise = (cur, dist) -- record interesting location + +-- | Return all the coordinates that are adjacent to this one. +adj :: UArray Coord Char -> (Coord -> Char -> Bool) -> Coord -> [Coord] +adj input isOpen cur = [ next - | next <- cardinal here - , cell <- arrIx input next - , isOpen cell (next - here) + | next <- cardinal cur + , char <- arrIx input next + , isOpen (next - cur) char ] -part1 :: Char -> Coord -> Bool -part1 c dir = - case c of - '#' -> False - '>' -> dir == east - 'v' -> dir == south - '^' -> dir == north - '<' -> dir == west - _ -> True +-- | Adjacency rule that respects slope characters. +part1 :: Coord -> Char -> Bool +part1 dir = \case + '#' -> False + '>' -> dir == east + 'v' -> dir == south + '^' -> dir == north + '<' -> dir == west + _ -> True -part2 :: Char -> Coord -> Bool -part2 c _ = - case c of - '#' -> False - _ -> True +-- | Adjacency rule that ignores slope characters. +part2 :: Coord -> Char -> Bool +part2 _ = ('#' /=)