-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay09.hs
105 lines (93 loc) · 3.49 KB
/
Day09.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
module Year2021.Day09 (run, star1, star2) where
import Data.Array as Array (Array, elems, listArray, (!))
import Data.Char (digitToInt, isDigit)
import Data.List (sort)
import Data.Set (fromList, toList)
import System.IO (isEOF)
import Prelude hiding (Left, Right)
type HeightMap = Array.Array Int (Array.Array Int Int)
-- Check if the current (m, n) coords is the lowest number.
-- Relies heavily on fast indexing.
isLowPoint :: Int -> Int -> HeightMap -> Int -> Int -> Bool
isLowPoint m n xxs i j = up && right && down && left
where
current = xxs ! i ! j
left = (j == 0) || ((xxs ! i ! (j - 1)) > current)
right = (j == n - 1) || ((xxs ! i ! (j + 1)) > current)
up = (i == 0) || ((xxs ! (i - 1) ! j) > current)
down = (i == m - 1) || ((xxs ! (i + 1) ! j) > current)
findLowPoints :: HeightMap -> [(Int, Int)]
findLowPoints xxs = [(i, j) | i <- [0 .. m - 1], j <- [0 .. n - 1], isLp i j]
where
m = length xxs
n = length (head $ elems xxs)
isLp = isLowPoint m n xxs
parseInput :: IO HeightMap
parseInput =
parseInput' >>= \xxs ->
let m = length xxs
in return $ Array.listArray (0, m - 1) xxs
where
parseInput' = do
done <- isEOF
if done
then return []
else do
ln <- getLine
let
n = length ln
lnInts = Array.listArray (0, n - 1) [digitToInt ch | ch <- ln, isDigit ch]
rest <- parseInput'
return (lnInts : rest)
star1 :: HeightMap -> Int
star1 xxs =
let lowPoints = findLowPoints xxs
in sum [(xxs ! i ! j) + 1 | (i, j) <- lowPoints]
type Basin = [(Int, Int)]
-- Low means the "origin" of the basin is the lowest point.
data From = Low | Up | Right | Down | Left
wall :: Int
wall = 9
findBasinFromLow :: (Int, Int) -> HeightMap -> Basin
findBasinFromLow pair xxs = toList . fromList $ findBasinFromLow' pair Low
where
m = length xxs
n = length (head $ elems xxs)
get (i, j) = xxs ! i ! j
pairIfNotWall :: Basin -> (Int, Int) -> Basin
pairIfNotWall basin pair'
| get pair' >= wall = []
| otherwise = pair' : basin
up (i, j)
| i == 0 = []
| otherwise = findBasinFromLow' (i - 1, j) Down
down (i, j)
| i == m - 1 = []
| otherwise = findBasinFromLow' (i + 1, j) Up
left (i, j)
| j == 0 = []
| otherwise = findBasinFromLow' (i, j - 1) Right
right (i, j)
| j == n - 1 = []
| otherwise = findBasinFromLow' (i, j + 1) Left
maybeSide j
| j < snd pair = [left]
| j > snd pair = [right]
| otherwise = [left, right]
findBasinFromLow' :: (Int, Int) -> From -> Basin
findBasinFromLow' pair' Low = pairIfNotWall ([up, right, down, left] >>= ($ pair')) pair'
findBasinFromLow' pair'@(_, j) Up = pairIfNotWall ((down : maybeSide j) >>= ($ pair')) pair'
findBasinFromLow' pair'@(_, j) Down = pairIfNotWall ((up : maybeSide j) >>= ($ pair')) pair'
findBasinFromLow' pair' Right = pairIfNotWall ([up, down, left] >>= ($ pair')) pair'
findBasinFromLow' pair' Left = pairIfNotWall ([up, down, right] >>= ($ pair')) pair'
star2 :: HeightMap -> Int
star2 xxs = do
let lowPoints = findLowPoints xxs
product . take 3 . reverse . sort $
[length bas | lp <- lowPoints, let bas = findBasinFromLow lp xxs]
run :: IO ()
run = do
xxs <- parseInput
let res1 = star1 xxs
res2 = star2 xxs
putStrLn $ "Day 9 results: [star1: " ++ show res1 ++ "] [star2: " ++ show res2 ++ "]"