1+ import Data.List
2+ import Data.Ix
3+ size = 10
4+
5+ indexFromCoords :: (Int , Int ) -> Int
6+ indexFromCoords (x, y) = (y * size + x)
7+
8+ exists :: (Int , Int ) -> Bool
9+ exists (x, y) = x < size && x > - 1 && y < size && y > - 1
10+
11+ octopus :: [Int ] -> (Int , Int ) -> Int
12+ octopus octopuses (x, y)
13+ | exists (x, y) = octopuses!! (indexFromCoords (x, y))
14+ | otherwise = - 1
15+
16+ set :: [Int ] -> (Int , Int ) -> Int -> [Int ]
17+ set octopuses coords value = (take $ indexFromCoords coords) octopuses
18+ ++ value: (drop (indexFromCoords coords + 1 ) octopuses)
19+
20+ neighbours :: [Int ] -> (Int , Int ) -> [Int ]
21+ neighbours octopuses (x, y) = map (octopus octopuses) (neighbourCoords (x, y))
22+
23+ neighbourCoords :: (Int , Int ) -> [(Int , Int )]
24+ neighbourCoords (x, y) = filter (\ (x_, y_) -> exists (x_, y_)) [
25+ (x', y') |
26+ (x', y') <- range ((x - 1 , y - 1 ), (x + 1 , y + 1 ))
27+ , not (x' == x && y' == y)
28+ ]
29+
30+ allCoords :: [(Int , Int )]
31+ allCoords = [(x, y) | (y, x) <- range((0 ,0 ), (size - 1 ,size - 1 ))]
32+
33+ increaseBy1 :: [Int ] -> (Int , Int ) -> [Int ]
34+ increaseBy1 octopuses coords = (take $ indexFromCoords coords) octopuses
35+ ++ (octopus octopuses coords + 1 ): (drop (indexFromCoords coords + 1 ) octopuses)
36+
37+ increaseAllBy1 :: [Int ] -> [Int ]
38+ increaseAllBy1 octopuses = map (+ 1 ) octopuses
39+
40+ -- maybeFlash :: [Int] -> [(Int, Int)] -> (Int, Int) -> [Int]
41+ -- maybeFlash octopuses alreadyFlashed coords
42+ -- | coords `elem` alreadyFlashed = octopuses
43+ -- | octopus octopuses coords > 9 = neighbourCoords octopuses coords
44+
45+ flashers :: [Int ] -> [(Int , Int )]
46+ flashers octopuses = [coords | coords <- allCoords, octopus octopuses coords > 9 ]
47+
48+ increaseNeighbours :: [Int ] -> (Int , Int ) -> [Int ]
49+ increaseNeighbours octopuses coords = [
50+ if (x, y) `elem` neighbourCoords coords
51+ then (octopus octopuses (x, y) + 1 )
52+ else (octopus octopuses (x, y)) |
53+ (x, y) <- allCoords
54+ ]
55+
56+ flashingStep :: [Int ] -> [(Int , Int )] -> [(Int , Int )] -> [Int ]
57+ flashingStep octopuses alreadyFlashed willFlash
58+ | willFlash == [] = map (\ x -> if x > 9 then 0 else x) octopuses
59+ | otherwise = flashingStep (increasedNeighbours) (firstCoord: alreadyFlashed) newWillFlash
60+ where firstCoord: coords = willFlash
61+ increasedNeighbours = increaseNeighbours octopuses firstCoord
62+ newWillFlash = [
63+ c | c <- flashers increasedNeighbours
64+ , c `notElem` willFlash
65+ , c `notElem` alreadyFlashed
66+ ] ++ coords
67+
68+ stepN :: [Int ] -> Int -> [Int ]
69+ stepN octopuses 0 = octopuses
70+ stepN octopuses n = stepN (flashingStep (increaseAllBy1 octopuses) [] (flashers (increaseAllBy1 octopuses))) (n - 1 )
71+
72+ countFlashesInNSteps :: [Int ] -> Int -> Int -> Int
73+ countFlashesInNSteps octopuses flashesSoFar n
74+ | n == 0 = flashesSoFar
75+ | otherwise = countFlashesInNSteps steppedOctopuses (flashesSoFar + (length $ filter (== 0 ) steppedOctopuses)) (n - 1 )
76+ where steppedOctopuses = flashingStep (increaseAllBy1 octopuses) [] (flashers $ increaseAllBy1 octopuses)
77+
78+ findFirstSync :: [Int ] -> Int -> Int
79+ findFirstSync octopuses steps
80+ | sum octopuses == 0 = steps
81+ | otherwise = findFirstSync (stepN octopuses 1 ) steps + 1
82+
83+ -- helpers for debugging
84+
85+ chunk :: Int -> [a ] -> [[a ]]
86+ chunk _ [] = []
87+ chunk n l
88+ | n > 0 = (take n l) : (chunk n (drop n l))
89+ | otherwise = error " Negative or zero n"
90+
91+ printOctopuses :: [Int ] -> IO ()
92+ printOctopuses octopuses = mapM_ putStrLn [ concat $ (intersperse " " [show o | o <- row ]) | row <- chunk size octopuses]
93+
94+ main = do
95+ contents <- readFile " day11/input.txt"
96+ let octopuses = concat $ [[read (item: " " ):: Int | item <- row] | row <- lines contents]
97+ putStrLn $ " Part 1: " ++ show (countFlashesInNSteps octopuses 0 100 )
98+ putStrLn $ " Part 2: " ++ show (findFirstSync octopuses 0 )
0 commit comments