-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday15.hs
64 lines (51 loc) · 1.86 KB
/
day15.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
module Main where
import Control.Monad (guard, when)
import Data.List.Split (splitOn)
import Data.Map qualified as M
import Data.Maybe (fromMaybe)
import Data.Set qualified as S
type Pos = (Int, Int)
type Warehouse = M.Map Pos Char
type Command = Char
main = interact (unlines . sequence [part1 . parse, part2 . parse . scaleUp])
part1, part2 :: ((Warehouse, Pos), [Command]) -> String
part1 = ("Part 1: " ++) . show . sum . gps . uncurry simulate
part2 = ("Part 2: " ++) . show . sum . gps . uncurry simulate
gps = map (uncurry (+) . ((* 100) <$>)) . M.keys . M.filter (`elem` "O[")
simulate :: (Warehouse, Pos) -> [Command] -> Warehouse
simulate = (fst .) . foldl (\acc c -> fromMaybe acc (move acc c))
move :: (Warehouse, Pos) -> Command -> Maybe (Warehouse, Pos)
move state c = go state
where
go (wh, pos) = do
from <- wh M.!? pos
let pos' = newPos pos c
wh' <- attempt (wh, pos')
return (M.insert pos' from $ M.insert pos '.' wh', pos')
attempt (wh, pos') = do
to <- wh M.!? pos'
let branch dir = do (wh', _) <- go (wh, pos'); go (wh', newPos pos' dir)
fst <$> case to of
'.' -> return (wh, pos')
'[' | c `elem` "^v" -> branch '>'
']' | c `elem` "^v" -> branch '<'
_ -> go (wh, pos')
newPos :: (Num a, Num b) => (a, b) -> Char -> (a, b)
newPos (x, y) c = case c of
'>' -> (x + 1, y)
'<' -> (x - 1, y)
'^' -> (x, y - 1)
'v' -> (x, y + 1)
scaleUp = concatMap go
where
go '#' = "##"
go 'O' = "[]"
go '.' = ".."
go '@' = "@."
go c = [c]
parse :: String -> ((Warehouse, Pos), [Command])
parse input = ((warehouse, start warehouse), concat $ lines commands)
where
[state, commands] = splitOn "\n\n" input
warehouse = M.fromList [((x, y), c) | (y, row) <- zip [0 ..] (lines state), (x, c) <- zip [0 ..] row, c /= '#']
start = (!! 0) . M.keys . M.filter (== '@')