|
| 1 | +module Main where |
| 2 | + |
| 3 | +import Data.Bits |
| 4 | +import Data.List |
| 5 | +import Data.List.Split |
| 6 | +import Data.List.Unique |
| 7 | +import Data.Map (Map) |
| 8 | +import Data.Map qualified as M |
| 9 | +import Data.Matrix (Matrix, (!)) |
| 10 | +import Data.Matrix qualified as Mat |
| 11 | +import Data.Set (Set) |
| 12 | +import Data.Set qualified as S |
| 13 | +import Data.Tuple.Extra |
| 14 | +import Debug.Trace |
| 15 | +import System.Environment |
| 16 | +import Text.Regex.TDFA ((=~)) |
| 17 | +-- TODO: Cleanup imports after day done |
| 18 | + |
| 19 | +type Input = (Matrix Char, (Int, Int)) |
| 20 | +type Output = Int |
| 21 | + |
| 22 | +parseInput :: String -> Input |
| 23 | +parseInput input = (maze, start) |
| 24 | + where |
| 25 | + maze = Mat.fromLists $ lines input |
| 26 | + start = head [(y, x) | y <-[1..Mat.nrows maze], x <-[1..Mat.ncols maze], maze ! (y, x) == 'S'] |
| 27 | + |
| 28 | + |
| 29 | +data Dir = UP | LEFT | RIGHT | DOWN deriving (Show, Eq, Ord) |
| 30 | + |
| 31 | +move :: (Int, Int) -> Dir -> (Int, Int) |
| 32 | +move (y, x) UP = (y - 1, x) |
| 33 | +move (y, x) DOWN = (y + 1, x) |
| 34 | +move (y, x) LEFT = (y, x - 1) |
| 35 | +move (y, x) RIGHT = (y, x + 1) |
| 36 | + |
| 37 | +turn90 :: Dir -> [Dir] |
| 38 | +turn90 UP = [LEFT, RIGHT] |
| 39 | +turn90 DOWN = [LEFT, RIGHT] |
| 40 | +turn90 LEFT = [UP, DOWN] |
| 41 | +turn90 RIGHT = [UP, DOWN] |
| 42 | + |
| 43 | +pathFind ::Matrix Char -> Map ((Int, Int), Dir) Int -> Int -> Dir -> (Int, Int) -> (Bool, Map ((Int, Int), Dir) Int, Int, Set (Int, Int)) |
| 44 | +pathFind maze seen score dir pos |
| 45 | + -- | rightPos && trace ("At " ++ show pos) (False ) = (False, seen, -1, S.empty) |
| 46 | + | (pos, dir) `M.member` seen && seen M.! (pos, dir) < score = (False, seen, -1, S.empty) -- Lower score ok |
| 47 | + -- | rightPos && trace ("At " ++ show pos ++ " has better score") (False ) = (False, seen, -1, S.empty) |
| 48 | + | maze ! pos == 'E' = (True, seen, score, S.singleton pos) |
| 49 | + -- | rightPos && trace ("At " ++ show pos ++ " not at end") (False ) = (False, seen, -1, S.empty) |
| 50 | + | maze ! pos == '#' = (False, seen, -1, S.empty) |
| 51 | + -- | rightPos && trace ("At " ++ show pos ++ " not at wall" ++ show bestSols) (False ) = (False, seen, -1, S.empty) |
| 52 | + | length bestSols /= 0 = bestSol |
| 53 | + | otherwise = (False, s2seen, -1, S.empty) |
| 54 | + where |
| 55 | + rightPos = (pos == (5, 4) || (pos == (4, 4) && dir == UP)) |
| 56 | + |
| 57 | + |
| 58 | + |
| 59 | + currSeen = (M.insert (pos, dir) score seen) |
| 60 | + (fbool, fseen, fscore, fseats) = pathFind maze currSeen (score + 1) dir (move pos dir) |
| 61 | + [d1, d2] = turn90 dir |
| 62 | + (s1bool, s1seen, s1score, s1seats) = pathFind maze fseen (score + 1001) d1 (move pos d1) |
| 63 | + (s2bool, s2seen, s2score, s2seats) = pathFind maze s1seen (score + 1001) d2 (move pos d2) |
| 64 | + |
| 65 | + bestSols :: [[(Bool, Set (Int, Int), Int)]] |
| 66 | + bestSols = groupBy (\a b -> thd3 a == thd3 b) . sortOn thd3 $ filter (fst3) [(fbool, fseats, fscore), (s1bool, s1seats, s1score), (s2bool, s2seats, s2score)] |
| 67 | + |
| 68 | + seats = S.unions $ map snd3 $ head bestSols |
| 69 | + |
| 70 | + bestSol = (True, s2seen, thd3 . head $ head bestSols, S.insert pos seats) |
| 71 | + |
| 72 | +part :: Input -> (Output, Output) |
| 73 | +part (maze, pos) = (\(_,_,p1,p2) -> (p1, S.size p2)) $ pathFind maze M.empty 0 RIGHT pos |
| 74 | + |
| 75 | +showMaze :: Matrix Char -> Set (Int, Int) -> [String] |
| 76 | +showMaze maze path = map (map (\yx -> if yx `S.member` path then 'O' else maze ! yx)) [[(y, x) | x<-[1..Mat.ncols maze]] | y<-[1..Mat.nrows maze]] |
| 77 | + |
| 78 | + |
| 79 | + |
| 80 | +main :: IO () |
| 81 | +main = do |
| 82 | + args <- getArgs |
| 83 | + content <- readFile (last args) |
| 84 | + let input = parseInput content |
| 85 | + |
| 86 | + -- print $ first Mat.toLists input |
| 87 | + |
| 88 | + let (part1, part2) = part input |
| 89 | + |
| 90 | + print $ (== 85480) $ part1 |
| 91 | + print $ (== 518) $ part2 |
| 92 | + |
| 93 | + -- putStrLn $ unlines . showMaze (fst input) $ (\(_,_,_,e) -> e) $ pathFind (fst input) M.empty 0 RIGHT (snd input) |
0 commit comments