1
1
import Data.Char (digitToInt )
2
+ import Data.List (find )
3
+ import Data.Maybe
2
4
3
5
enumerate :: [a ] -> [(Int , a )]
4
6
enumerate = zip [0 .. ]
@@ -7,32 +9,62 @@ linesToHeightmap :: [String] -> [[Int]]
7
9
linesToHeightmap = map (map digitToInt)
8
10
9
11
isOutOfBounds :: [[Int ]] -> (Int , Int ) -> Bool
10
- isOutOfBounds matrix (x, y) = x < 0 || y < 0 || x >= maxX || y >= maxY
11
- where
12
- maxX = length matrix
13
- maxY = length (head matrix)
12
+ isOutOfBounds matrix (x, y) = x < 0 || y < 0 || x >= length matrix || y >= length (matrix !! x)
14
13
15
- findSurrounding :: [[Int ]] -> (Int , Int ) -> [Int ]
14
+ findSurrounding :: [[Int ]] -> (Int , Int ) -> [Maybe Int ]
16
15
findSurrounding matrix (x, y) = map (getElementAt matrix) positions
17
16
where
18
17
positions = [(x - 1 , y), (x + 1 , y), (x, y - 1 ), (x, y + 1 )]
19
18
20
- getElementAt :: [[Int ]] -> (Int , Int ) -> Int
21
- getElementAt matrix point@ (x, y) = if isOutOfBounds matrix point then - 1 else ( matrix !! y ) !! x
19
+ getElementAt :: [[Int ]] -> (Int , Int ) -> Maybe Int
20
+ getElementAt matrix point@ (x, y) = if isOutOfBounds matrix point then Nothing else Just (( matrix !! x ) !! y)
22
21
23
- isPointLowest :: Int -> [Int ] -> Bool
24
- isPointLowest x = all (> x)
22
+ isPointLowest :: Int -> [Maybe Int ] -> Bool
23
+ isPointLowest x = all f
24
+ where
25
+ f (Just n) = n > x
26
+ f Nothing = True
25
27
26
28
getLowPoints :: [[Int ]] -> [Int ]
27
- getLowPoints matrix = foldl (\ acc (x, row) -> acc ++ getLowPointsFromRow x row) [] (enumerate matrix)
29
+ getLowPoints matrix =
30
+ foldl (\ acc (x, row) -> acc ++ getLowPointsFromRow x row) [] (enumerate matrix)
31
+ where
32
+ getLowPointsFromRow xa row =
33
+ map snd (filter (\ (ya, point) -> isPointLowest point (findSurrounding matrix (xa, ya))) (enumerate row))
34
+
35
+ floodFill :: [[Int ]] -> (Int , Int ) -> [(Int , Int )] -> Int
36
+ floodFill matrix point@ (x, y) visited =
37
+ if value < 9 && notVisited then 1 + foldl (\ acc p -> acc + floodFill matrix p (point : visited)) 0 positions else 0
38
+ where
39
+ value =
40
+ case getElementAt matrix point of
41
+ Nothing -> 9
42
+ (Just z) -> z
43
+ positions = [(x - 1 , y), (x + 1 , y), (x, y - 1 ), (x, y + 1 )]
44
+ notVisited = isNothing (find (== point) visited)
45
+
46
+ replaceMaxBasin :: (Int , Int , Int ) -> Int -> (Int , Int , Int )
47
+ replaceMaxBasin allb@ (b1, b2, b3) bn
48
+ | b1 < bn = (bn, b2, b3)
49
+ | b2 < bn = (b1, bn, b3)
50
+ | b3 < bn = (b1, b2, bn)
51
+ | otherwise = allb
52
+
53
+ getLargestBasins :: [[Int ]] -> (Int , Int , Int )
54
+ getLargestBasins matrix =
55
+ foldl (\ acc (x, row) -> foldl replaceMaxBasin acc (getBasins x row)) (0 , 0 , 0 ) (enumerate matrix)
28
56
where
29
- getLowPointsFromRow xa row = map snd (filter (\ (ya, point) -> isPointLowest point (findSurrounding matrix (xa, ya))) (enumerate row))
57
+ getBasins xa row =
58
+ map (\ (ya, point) -> if isPointLowest point (findSurrounding matrix (xa, ya)) then floodFill matrix (xa, ya) [] else 0 ) (enumerate row)
30
59
31
60
sumLowPoints :: [Int ] -> Int
32
61
sumLowPoints = foldl (\ acc c -> acc + c + 1 ) 0
33
62
34
63
main :: IO ()
35
64
main = do
36
- input <- readFile " input .txt"
65
+ input <- readFile " test .txt"
37
66
let heightmap = (linesToHeightmap . lines ) input
38
- print $ getLowPoints heightmap
67
+ let (b1, b2, b3) = getLargestBasins heightmap
68
+ print (b1, b2, b3)
69
+ print (b1 * b2 * b3)
70
+ print $ sumLowPoints $ getLowPoints heightmap
0 commit comments