[download] []
 
(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))))