-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay17.hs
128 lines (105 loc) · 3.64 KB
/
Day17.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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
module Day17
( part1
, part2
) where
import Data.Array.Unboxed (UArray, bounds, inRange, (!))
import Data.Bits (shiftL, shiftR, (.&.))
import Data.ByteString (ByteString)
import Data.Hashable (Hashable, hashWithSalt)
import qualified Data.IntMap.Strict as M (IntMap, elems, empty, insert,
lookup, singleton)
import qualified Data.IntPSQ as Q (insert, singleton)
import Data.Maybe (fromJust)
import Helpers.Parsers.ByteString (digitArrayFromByteString)
import Helpers.Search.Int (IntLike, dijkstraMech, fromInt,
toInt)
import Linear.V2 (V2 (..))
import Debug.Trace
data Crucible = Crucible
{ pos :: Pos
, dir :: Pos
, acc :: Int
} deriving (Show, Eq, Ord)
type Blocks = UArray Pos Int
type Pos = V2 Int
instance Integral a => IntLike (V2 a) where
toInt (V2 a b) = fromIntegral a + shiftL (fromIntegral b) 8
fromInt i = V2 (fromIntegral $ i .&. 255) (fromIntegral $ shiftR i 8)
instance IntLike Crucible where
toInt (Crucible pos dir acc) = toInt pos + shiftL intPos 16 + shiftL acc 18
where
intPos
| dir == north = 0
| dir == east = 1
| dir == south = 2
| dir == west = 3
fromInt i = Crucible pos dir acc
where
pos = fromInt $ i .&. 65535
dir = posInt $ shiftR i 16 .&. 3
acc = shiftR i 18
posInt 0 = north
posInt 1 = east
posInt 2 = south
posInt 3 = west
instance Hashable Crucible where
hashWithSalt salt (Crucible a b c) =
salt `hashWithSalt` a `hashWithSalt` b `hashWithSalt` c
north = V2 0 (-1)
south = V2 0 1
east = V2 1 0
west = V2 (-1) 0
minMove1 = 0
maxMove1 = 3
minMove2 = 4
maxMove2 = 10
left :: Pos -> Pos
left (V2 x y) = V2 y (-x)
right :: Pos -> Pos
right (V2 x y) = V2 (-y) x
nextMoves :: Crucible -> [Crucible]
nextMoves (Crucible p d a) =
Crucible (p + d) d (a + 1)
: map (\x -> Crucible (p + x d) (x d) 1) [left, right]
heatLoss :: Blocks -> Pos -> Pos -> Int
heatLoss blocks _ p = blocks ! p
moves :: Int -> Int -> Blocks -> Int -> [(Int, Int)]
moves minMoves maxMoves blocks intc =
map (\x -> (toInt x, blocks ! pos x))
. filter
(\(Crucible np _ na) -> inRange (bounds blocks) np && na <= maxMoves)
$ next
where
c@(Crucible p d nm) = fromInt intc
next
| nm < minMoves = [Crucible (p + d) d (nm + 1)]
| otherwise = nextMoves c
part1 :: Bool -> ByteString -> String
part1 _ input = show dijkVal
where
blocks = digitArrayFromByteString input
startPos = toInt $ Crucible start east 0
(start, endGoal) = bounds blocks
(Just actualGoal, (dijVals, _)) =
dijkstraMech
(Q.singleton startPos 0 ())
(M.singleton startPos 0)
M.empty
(moves minMove1 maxMove1 blocks)
((==) endGoal . pos . fromInt)
dijkVal = fromJust . M.lookup actualGoal $ dijVals
part2 :: Bool -> ByteString -> String
part2 _ input = show dijkVal
where
blocks = digitArrayFromByteString input
startPosEast = toInt $ Crucible start east 0
startPosSouth = toInt $ Crucible start south 0
(start, endGoal) = bounds blocks
(Just actualGoal, (dijVals, _)) =
dijkstraMech
(Q.insert startPosEast 0 () $ Q.singleton startPosSouth 0 ())
(M.insert startPosEast 0 $ M.singleton startPosSouth 0)
M.empty
(moves minMove2 maxMove2 blocks)
(\c -> pos (fromInt c) == endGoal && acc (fromInt c) >= 4)
dijkVal = fromJust . M.lookup actualGoal $ dijVals