-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay24.hs
70 lines (61 loc) · 2.51 KB
/
Day24.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
65
66
67
68
69
70
{-# LANGUAGE TupleSections #-}
module Day24 (solve) where
import Data.Function ( (&) )
import Data.List
import qualified Data.Set as Set
solve input lines = do
let (pathOut, restOut) = explore initialValley entrance exit
let lenOut = length pathOut
print lenOut
let (pathSnack, restSnack) = explore (restOut & head & fst) exit entrance
let (pathFinal, _) = explore (restSnack & head & fst) entrance exit
print $ lenOut + length pathSnack + length pathFinal
where
explore valley start end = do
let time = iterate (minute width height) (valley, [start])
let path = time & takeWhile
((> 0). minimum . map (distance end) . snd)
let rest = drop (length path) time
(path, rest)
initialValley = readValley lines width height
entrance = (1, 0)
height = length lines - 2
width = length (head lines) - 2
exit = (width, height + 1)
minute width height (valley, positions) = do
let valley' = nextValley width height valley
let positions' = nextPos (occupied valley') positions
(valley', positions')
nextValley width height valley = do
let [wall, north, east, south, west] = valley
[ wall
, moveAll (moveNorth height) north
, moveAll (moveEast width) east
, moveAll (moveSouth height) south
, moveAll (moveWest width) west ]
nextPos occupied positions =
positions & concatMap nextPos & nub
where
nextPos pos@(x, y) =
[(0, 0), (0, -1), (1, 0), (0, 1), (-1, 0)]
& map (\ (dx, dy) -> (x + dx, y + dy))
& filter (`Set.notMember` occupied)
distance (x', y') (x, y) = abs (x' - x) + abs (y' - y)
occupied valley = valley & concat & map fst & Set.fromList
moveNorth height (x, y) = y - 2 & (`mod` height) & (+ 1) & (x,)
moveEast width (x, y) = x & (`mod` width) & (+ 1) & (,y)
moveSouth height (x, y) = y & (`mod` height) & (+ 1) & (x,)
moveWest width (x, y) = x - 2 & (`mod` width) & (+ 1) & (,y)
moveAll move kind = map (\ (pos, c) -> (move pos, c)) kind
readValley lines width height = do
let parts =
zip [0 ..] lines & concatMap
(\ (y, line) -> zipWith (\ x c -> ((x, y), c)) [0 .. ] line )
let getKind k = parts & filter ((== k). snd)
let wall = getKind '#'
let north = getKind '^'
let east = getKind '>'
let south = getKind 'v'
let west = getKind '<'
[((width, height + 2), '#') : ((1, -1), '#') : wall
, north, east, south, west]