A src/aoc2021/day17.clj => src/aoc2021/day17.clj +77 -0
@@ 0,0 1,77 @@
+; --- Day 17: Trick Shot ---
+; https://adventofcode.com/2021/day/17
+
+(ns aoc2021.day17
+ (:require [clojure.java.io :as io]
+ [clojure.string :as str]
+ [clojure.edn :as edn]
+ [clojure.pprint :refer [pprint cl-format]]))
+
+(defn ceil [x] (long (Math/ceil x)))
+
+(defn sqrt [x] (Math/sqrt x))
+
+(defn abs-dec [n]
+ (cond
+ (> n 0) (dec n)
+ (< n 0) (inc n)
+ true n))
+
+(defn range' [^long a ^long b]
+ (if (> a b)
+ (reverse (range b (inc a)))
+ (range a (inc b))))
+
+(defn step [[x y] [dx dy]]
+ [[(+ x dx) (+ y dy)]
+ [(abs-dec dx) (dec dy)]])
+
+(defn hit? [[x y] [[x1 x2] [y1 y2]]]
+ (and (>= x x1) (<= x x2)
+ (>= y y1) (<= y y2)))
+
+(defn overshot? [[x y] [[x1 x2] [y1 y2]]]
+ (or (> x x2) (< y y1)))
+
+; returns: max height on hit, nil on miss
+(defn shoot [start-velocity bounds]
+ (loop [position [0 0]
+ velocity start-velocity
+ max-height 0]
+ (cond
+ (overshot? position bounds) nil
+ (hit? position bounds) max-height
+ true (let [[position' velocity'] (step position velocity)
+ max-height' (max max-height (second position'))]
+ (recur position' velocity' max-height')))))
+
+; all starting velocities which could possibly hit the target
+; using the power of high school math!
+(defn velocity-range [[[x1 x2] [y1 y2]]]
+ (let [dx-min (ceil (/ (+ -1 (sqrt (+ 1 (* 8 x1)))) 2))
+ dx-max (inc x2)
+ dy-min (dec y1)
+ dy-max (- y1)]
+ ; order by max dy first since we're looking for the highest shot
+ (for [dy (range' dy-max dy-min)
+ dx (range' dx-min dx-max)]
+ [dx dy])))
+
+(defn find-highest-shot [bounds]
+ (->> (velocity-range bounds)
+ (map #(shoot % bounds))
+ (remove nil?)
+ (first)))
+
+(defn count-hits [bounds]
+ (->> (velocity-range bounds)
+ (map #(shoot % bounds))
+ (remove nil?)
+ (count)))
+
+; target area: x=277..318, y=-92..-53
+
+(defn main []
+ (let [bounds [[277 318] [-92 -53]]]
+ (println (str "Part 1:" (time (find-highest-shot bounds))))
+ (println (str "Part 2:" (time (count-hits bounds))))))