(ns edition2022.day9
  (:require [clojure.string :as str]))

(def example "R 4\nU 4\nL 3\nD 1\nR 4\nD 1\nL 5\nR 2")
(def example-lines (str/split-lines example))

(def example-lines2 (str/split-lines "R 5\nU 8\nL 8\nD 3\nR 17\nD 10\nL 25\nU 20\n"))

(defn expand-move [[direction times]]
  (->> (cond
         (= direction "R") [1 0]
         (= direction "L") [-1 0]
         (= direction "U") [0 1]
         (= direction "D") [0 -1])
       (iterate identity)
       (take (Integer/parseInt times))))

(defn expand-moves [lines]
  (->> lines
       (map #(str/split % #" "))
       (map expand-move)
       (apply concat)
       ))

(defn update-position [pos move]
  [(+ (first move) (first pos)) (+ (second move) (second pos))])

(defn tail-head-distance [tail head]
  (->> (interleave head tail)
       (partition 2)
       (map #(reduce - %))
       (into [])))

(defn update-tail [tail new-head]
  (let [new-distance (tail-head-distance tail new-head)]
    (cond
      (or (= [2 1] new-distance) (= [1 2] new-distance) (= [2 2] new-distance)) (update-position tail [1 1])
      (or (= [-2 1] new-distance) (= [-1 2] new-distance) (= [-2 2] new-distance)) (update-position tail [-1 1])
      (or (= [2 -1] new-distance) (= [1 -2] new-distance) (= [2 -2] new-distance)) (update-position tail [1 -1])
      (or (= [-2 -1] new-distance) (= [-1 -2] new-distance) (= [-2 -2] new-distance)) (update-position tail [-1 -1])
      (= [2 0] new-distance) (update-position tail [1 0])
      (= [0 2] new-distance) (update-position tail [0 1])
      (= [-2 0] new-distance) (update-position tail [-1 0])
      (= [0 -2] new-distance) (update-position tail [0 -1])
      :else tail)))

(defn move-rope
  ([rope move] (move-rope rope move 0))
  ([rope move knot]
   (if (= (count rope) (inc knot))
     rope
     (let [head (nth rope knot)
           tail (nth rope (inc knot))
           new-head (if (= knot 0) (update-position head move) head)
           new-tail (update-tail tail new-head)
           ]
       (recur (assoc rope knot new-head (inc knot) new-tail) move (inc knot))))
   ))

(defn execute-moves ([lines] (execute-moves lines 2))
  ([lines rope-length]
   (loop [moves (expand-moves lines)
          rope (into [] (repeat rope-length [0 0]))
          tail-visited (vector)]
     (if (empty? moves)
       tail-visited
       (let [next-move (first moves)
             new-rope (move-rope rope next-move)]
         (recur (rest moves) new-rope (conj tail-visited (last new-rope))))))))

(comment
  (tail-head-distance [0 1] [0 3])
  (tail-head-distance [-2 -1] [-1 -2])
  (expand-move ["U" 3])
  (expand-moves example-lines)
  (count (set (execute-moves example-lines)))
  (count (set (execute-moves (str/split-lines (slurp "resources/day9.in")))))
  (count (set (execute-moves example-lines2 10)))
  (count (set (execute-moves (str/split-lines (slurp "resources/day9.in")) 10)))
  (set (execute-moves example-lines2 10))
  (execute-moves ["R 4" "U 4"] 10)
  (execute-moves [(first example-lines2) (second example-lines2) (nth example-lines2 3)] 10)
  (execute-moves ["R 5" "U 8" "L 8" "D 3" "R 17"] 10)
  example-lines2
  (move-rope [[5 2] [5 1] [3 0] [2 0] [1 0] [0 0] [0 0] [0 0] [0 0] [0 0]] [0 1])
  )