Haskell
This was a fun one! Infinite loops, performance concerns and so on. Part 2 could be made a bit faster with a recursive approach (I think), but this is good enough for me. Lost quite a bit of time with an incorrect walk
function that passed the test data and part 1 but not part 2.
import Data.Array.Unboxed (UArray)
import Data.Array.Unboxed qualified as Array
import Data.List
import Data.Maybe
import Data.Set (Set)
import Data.Set qualified as Set
readInput :: String -> UArray (Int, Int) Char
readInput s =
let rows = lines s
in Array.listArray ((1, 1), (length rows, length $ head rows)) $ concat rows
startPos = fst . fromJust . find ((== '^') . snd) . Array.assocs
walk grid = go (startPos grid) (-1, 0)
where
go pos@(i, j) dir@(di, dj) =
(pos, dir)
: let pos' = (i + di, j + dj)
in if Array.inRange (Array.bounds grid) pos'
then case grid Array.! pos' of
'#' -> go pos (dj, -di)
_ -> go pos' dir
else []
path = Set.fromList . map fst . walk
part1 = Set.size . path
part2 grid = Set.size $ Set.filter (isLoop . walk . addO) $ Set.delete (startPos grid) $ path grid
where
addO pos = grid Array.// [(pos, '#')]
isLoop xs = or $ zipWith Set.member xs $ scanl' (flip Set.insert) Set.empty xs
main = do
input <- readInput <$> readFile "input06"
print $ part1 input
print $ part2 input