| 1 | (ns adventofcode2019.day13 |
| 2 | [:require [adventofcode2019.lib :refer :all] |
| 3 | [adventofcode2019.intcode :as i] |
| 4 | [clojure.string :as str] |
| 5 | [clojure.core.match :refer [match]] |
| 6 | [lanterna.terminal :as ts]]) |
| 7 | |
| 8 | (defn find-bounds [tiles] |
| 9 | (let [x-list (map first (keys tiles)) |
| 10 | y-list (map second (keys tiles)) |
| 11 | min-x (reduce min x-list) |
| 12 | max-x (reduce max x-list) |
| 13 | min-y (reduce min y-list) |
| 14 | max-y (reduce max y-list)] |
| 15 | [[min-x max-x] [min-y max-y]])) |
| 16 | |
| 17 | (defn make-field [[dx dy]] |
| 18 | (let [line (vec (repeat dx \space))] |
| 19 | (mapv (constantly line) (range dy)))) |
| 20 | |
| 21 | (defn draw-tiles [tiles term] |
| 22 | (let [to-text #(case % |
| 23 | 0 \space |
| 24 | 1 \| |
| 25 | 2 \- |
| 26 | 3 \_ |
| 27 | 4 \O) |
| 28 | paint-tile (fn [[[x y] tile]] |
| 29 | (ts/move-cursor term x y) |
| 30 | (ts/put-character term (to-text tile)))] |
| 31 | (doall (map paint-tile tiles)))) |
| 32 | |
| 33 | (defn print-field [field score] |
| 34 | (run! (comp println str/join) field) |
| 35 | (println score) |
| 36 | (flush)) |
| 37 | |
| 38 | (defn find-next-paddle-contact [screen prev-ball ball paddle] |
| 39 | (let [db (map - ball prev-ball) |
| 40 | [dx dy] db |
| 41 | futr (take 50 (iterate (partial map + db) ball)) |
| 42 | rdtn (fn [screen [bx by]] |
| 43 | (if (or (= by paddle) (= dx 0)) |
| 44 | (reduced bx) |
| 45 | (reduced bx) |
| 46 | #_(case (screen [bx by]) |
| 47 | 1 (find-next-paddle-contact screen [bx by] [(- bx dx) by] paddle) |
| 48 | 2 (find-next-paddle-contact screen [bx by] [(+ bx dx) (dec by)] paddle) |
| 49 | screen))) |
| 50 | px (reduce rdtn screen futr)] |
| 51 | (if (= px screen) 0 px))) |
| 52 | |
| 53 | (defn run-game [game [[min-x max-x] [min-y max-y]]] |
| 54 | (let [wait-for-output #(= (count (:output %)) 3) |
| 55 | get-next-ic-output (partial i/intcode-until wait-for-output) |
| 56 | new-game (assoc game :input [0]) |
| 57 | field (ts/get-terminal :swing)] |
| 58 | #_(ts/start field) |
| 59 | (loop [program-state (get-next-ic-output new-game) |
| 60 | screen {} |
| 61 | score 0 |
| 62 | framerate 0 |
| 63 | ctr 0 |
| 64 | prev-ball nil |
| 65 | ball nil |
| 66 | paddle nil |
| 67 | joyst 1] |
| 68 | #_(Thread/sleep framerate) |
| 69 | (if (:exit program-state) |
| 70 | (do #_(ts/stop field) |
| 71 | score) |
| 72 | (let [[x y t] (:output program-state) |
| 73 | [new-score new-screen] (if (and (= x -1) (= y 0)) |
| 74 | (do #_(ts/move-cursor field x y) |
| 75 | #_(ts/put-string field (str t)) |
| 76 | [t {}]) |
| 77 | [score {[x y] t}]) |
| 78 | screen (merge screen new-screen) |
| 79 | #_(case (ts/get-key-blocking field {:interval framerate |
| 80 | :timeout framerate}) |
| 81 | \a -1 |
| 82 | \d 1 |
| 83 | 0) |
| 84 | paddle (if (= t 3) [x y] paddle) |
| 85 | prev-ball (if (= t 4) ball prev-ball) |
| 86 | ball (if (= t 4) [x y] ball) |
| 87 | joyst (if (and prev-ball ball paddle) |
| 88 | (compare (find-next-paddle-contact screen prev-ball ball (second paddle)) (first paddle)))] |
| 89 | #_(draw-tiles new-screen field) |
| 90 | (recur (get-next-ic-output (assoc program-state |
| 91 | :output [] |
| 92 | :input [joyst])) |
| 93 | screen new-score 0 #_(if (> ctr 814) 0 0) (inc ctr) |
| 94 | prev-ball ball paddle joyst)))))) |
| 95 | |
| 96 | (defn day13 [] |
| 97 | (let [input (i/get-program (input-file)) |
| 98 | output (:output (i/intcode (i/build-state input))) |
| 99 | draw-tiles (fn [screen [x y t]] (assoc screen [x y] t)) |
| 100 | screen (reduce draw-tiles {} (partition 3 output)) |
| 101 | game (assoc-in (i/build-state input {:step true}) [:memory 0] 2)] |
| 102 | (part1 (count (filter #(= % 2) (vals screen)))) |
| 103 | (part2 (run-game game (find-bounds screen))))) |