(let [octopuses-2d (->> "day11/input.txt"
io/resource
slurp
str/split-lines
(mapv (fn [row] (mapv #(- (int %) 48) row))))
octopuses (into [] (mapcat identity octopuses-2d))
size-y (count octopuses-2d)
size-x (count (octopuses-2d 0))
pos-to-x (fn [pos] (mod pos size-x))
pos-to-y (fn [pos] (quot pos size-y))
pos-to-xy (juxt pos-to-x pos-to-y)
xy-to-pos (fn [[x y]] (+ (* y size-x) x))
neighbour-coors (fn [[x y]]
(->>
(map (fn [x-offs y-offs]
[(+ x x-offs) (+ y y-offs)]) [1 1 0 -1 -1 -1 0 1] [0 1 1 1 0 -1 -1 -1])
(filter (fn [[x y]] (and (>= x 0) (>= y 0) (< x size-x) (< y size-y))))))
neighbour-poss (memoize (fn [pos]
(->> pos
pos-to-xy
neighbour-coors
(map xy-to-pos))))
find-flash-positions (fn [octopuses]
(filter (fn [pos]
(> (octopuses pos) 9))
(range (count octopuses))))
flashed? (fn [energy] (= energy 0))
calc-flashes (fn calc-flashes [octopuses]
(let [next-flash-positions (find-flash-positions octopuses)]
(if (empty? next-flash-positions)
octopuses
(calc-flashes (reduce (fn [octopuses flash-position]
(-> octopuses
(assoc flash-position 0)
(#(reduce (fn [octopuses flash-neighbour-pos]
(update octopuses flash-neighbour-pos
(fn [energy]
(if (flashed? energy)
energy
(inc energy)))))
%
(neighbour-poss flash-position)))))
octopuses
next-flash-positions)))))
calc-next-octopuses (fn [octopuses]
(->> octopuses
(mapv inc)
calc-flashes))
part-1 (fn [octopuses-generations]
(->> octopuses-generations
(take 101)
(reduce (fn [total-sum octopuses]
(+ total-sum (count (filter flashed? octopuses)))) 0)))
part-2 (fn [octopuses-generations]
(reduce (fn [step octopuses]
(if (every? flashed? octopuses)
(reduced step)
(inc step)))
0
octopuses-generations))]
(->> (iterate calc-next-octopuses octopuses)
((juxt part-1 part-2))))