Last active
December 25, 2018 20:09
-
-
Save exupero/454072fb8b4e5f0dadab28de2b95a7f4 to your computer and use it in GitHub Desktop.
Advent of Code 2018
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns exupero.advent-of-code.2018 | |
(:require [clojure.string :as string] | |
[clojure.set :refer [intersection rename-keys map-invert]] | |
[clojure.zip :as zip] | |
[net.cgrand.xforms :as xf])) | |
(def parse-int #(Integer/parseInt %)) | |
; Day 1, puzzle 1 | |
(def input-1 (map parse-int (string/split-lines (slurp "./resources/advent-of-code/2018/day-1-input.txt")))) | |
(reduce + input-1) | |
; Day 1, puzzle 2 | |
(->> input-1 cycle (reductions +) | |
(reduce (fn [seen item] | |
(if (seen item) | |
(reduced item) | |
(conj seen item))) | |
#{})) | |
; Day 2, puzzle 1 | |
(def input-2 (string/split-lines (slurp "./resources/advent-of-code/2018/day-2-input.txt"))) | |
(sequence | |
(comp | |
(map frequencies) | |
(xf/by-key (comp (partial intersection #{2 3}) set vals) xf/count) | |
(xf/into {}) | |
(map (fn [{twos #{2} threes #{3} both #{2 3}}] | |
(* (+ twos both) (+ threes both))))) | |
input-2) | |
; Day 2, puzzle 2 | |
(sequence | |
(comp | |
(map (partial apply map vector)) | |
(filter (comp #{1} count (partial filter (partial apply not=)))) | |
(map (partial transduce | |
(comp | |
(filter (partial apply =)) | |
(map first)) | |
str))) | |
(for [a input-2, b input-2 :while (not= a b)] | |
[a b])) | |
; Day 3, puzzle 1 | |
(def input-3 | |
(map | |
(fn [s] | |
(let [[_ id x y w h] (re-matches #"#(\d+) @ (\d+),(\d+): (\d+)x(\d+)" s)] | |
{:id id | |
:x (parse-int x) | |
:y (parse-int y) | |
:width (parse-int w) | |
:height (parse-int h)})) | |
(string/split-lines (slurp "./resources/advent-of-code/2018/day-3-input.txt")))) | |
(sequence | |
(comp | |
(mapcat (fn [{:keys [x y width height]}] | |
(for [x' (range width), y' (range height)] | |
[(+ x x') (+ y y')]))) | |
(xf/by-key identity xf/count) | |
(filter (comp #(< 1 %) second)) | |
xf/count) | |
input-3) | |
; Day 3, puzzle 2 | |
(defn intersects? [{x1 :x y1 :y w1 :width h1 :height} | |
{x2 :x y2 :y w2 :width h2 :height}] | |
(not (or (< (+ x1 w1) x2) | |
(< (+ y1 h1) y2) | |
(< (+ x2 w2) x1) | |
(< (+ y2 h2) y1)))) | |
(sequence | |
(comp | |
(remove (fn [box] | |
(some #(and (not= (:id box) (:id %)) | |
(intersects? box %)) | |
input-3))) | |
(map :id)) | |
input-3) | |
; Day 4, puzzle 1 | |
(defn spillover [k f] | |
(fn [rf] | |
(let [value (volatile! nil)] | |
(fn | |
([] (rf)) | |
([result] (rf result)) | |
([result item] | |
(if-let [v (k item)] | |
(do (vreset! value v) | |
(rf result item)) | |
(rf result (f item @value)))))))) | |
(def input-4 | |
(sequence | |
(comp | |
(map (fn [s] | |
(let [[_ year month day hour minute event guard-id] | |
(re-matches #"\[(\d+)-(\d+)-(\d+) (\d+):(\d+)\] (Guard #(\d+) begins shift|wakes up|falls asleep)" s)] | |
{:year (parse-int year) | |
:month (parse-int month) | |
:day (parse-int day) | |
:hour (parse-int hour) | |
:minute (parse-int minute) | |
:event (cond | |
(re-find #"begins shift" event) :begins-shift | |
(= event "falls asleep") :falls-asleep | |
(= event "wakes up") :wakes-up) | |
:guard-id (when guard-id (parse-int guard-id))}))) | |
(xf/sort-by (juxt :year :month :day :hour :minute)) | |
(spillover :guard-id #(assoc %1 :guard-id %2))) | |
(string/split-lines (slurp "./resources/advent-of-code/2018/day-4-input.txt")))) | |
(defn spans [k v t] | |
(fn [rf] | |
(let [start (volatile! nil)] | |
(fn | |
([] (rf)) | |
([result] (rf result)) | |
([result item] | |
(condp = (t item) | |
:start (do (vreset! start (v item)) result) | |
:end (let [s @start] | |
(vreset! start nil) | |
(rf result | |
{:key (k item) | |
:start s | |
:end (v item)})))))))) | |
(def guard-minutes-asleep | |
(sequence | |
(comp | |
(remove (comp #{:begins-shift} :event)) | |
(spans :guard-id :minute (comp {:falls-asleep :start, :wakes-up :end} :event)) | |
(mapcat (fn [{k :key :keys [guard-id start end]}] | |
(for [m (range start end)] | |
{:guard-id k :minute m})))) | |
input-4)) | |
(sequence | |
(comp | |
(xf/by-key :guard-id (comp | |
(xf/by-key :minute xf/count) | |
(xf/sort-by second >) | |
(xf/into []))) | |
(xf/sort-by #(reduce + (map second (second %))) >) | |
(map (fn [[id [[minute]]]] | |
(* id minute))) | |
(take 1)) | |
guard-minutes-asleep) | |
; Day 4, puzzle 2 | |
(sequence | |
(comp | |
(xf/by-key (juxt :guard-id :minute) xf/count) | |
(xf/sort-by second >) | |
(map first) | |
(map (partial apply *)) | |
(take 1)) | |
guard-minutes-asleep) | |
; Day 5, puzzle 1 | |
(def input-5 (string/trim (slurp "./resources/advent-of-code/2018/day-5-input.txt"))) | |
(defn reactive? [a b] | |
(and a b (not= a b) | |
(= (string/lower-case a) (string/lower-case b)))) | |
(defn react [input] | |
(loop [loc (-> input vec zip/vector-zip zip/next)] | |
(if (zip/end? loc) | |
(zip/root loc) | |
(let [cur (zip/node loc) | |
left (some-> loc zip/left zip/node) | |
right (some-> loc zip/right zip/node)] | |
(cond | |
(reactive? left cur) (recur (-> loc zip/remove zip/remove)) | |
(reactive? cur right) (recur (-> loc zip/right zip/remove zip/remove)) | |
:else (recur (zip/next loc))))))) | |
(count (react input-5)) | |
; Day 5, puzzle 2 | |
(sequence | |
(comp | |
(distinct) | |
(map (fn [c] | |
(->> input-5 | |
(remove #{(first (string/lower-case c)) | |
(first (string/upper-case c))}) | |
react | |
count))) | |
(xf/sort-by identity) | |
(take 1)) | |
input-5) | |
; Day 6, puzzle 1 | |
(def input-6 (map | |
(fn [s] | |
(let [[_ x y] (re-matches #"(\d+), (\d+)" s)] | |
(mapv parse-int [x y]))) | |
(string/split-lines (slurp "./resources/advent-of-code/2018/day-6-input.txt")))) | |
(def extent (juxt (partial apply min) (partial apply max))) | |
(defn manhattan-distance [a b] | |
(reduce + (map (comp #(Math/abs %) -) a b))) | |
(defn boundary? [[x y] [min-x max-x] [min-y max-y]] | |
(or (<= x min-x) | |
(<= y min-y) | |
(>= x max-x) | |
(>= y max-y))) | |
(defn bests-by [f comp] | |
(let [best (volatile! [[] nil])] | |
(fn [rf] | |
(fn | |
([] (rf)) | |
([result] (rf (rf result (first @best)))) | |
([result item] | |
(let [[_ v] @best | |
k (f item)] | |
(cond | |
(= k v) | |
(do (vswap! best update 0 conj item) | |
result) | |
(or (nil? v) (comp k v)) | |
(do (vreset! best [[item] k]) | |
result)))))))) | |
(defn largest-manhattan-area [coords] | |
(let [[min-x max-x] (extent (map first coords)) | |
[min-y max-y] (extent (map second coords))] | |
(sequence | |
(comp | |
(map (fn [location] | |
{:location location | |
:closest-coords (sequence | |
(bests-by #(manhattan-distance location %) <) | |
coords)})) | |
(filter (comp #{1} count :closest-coords)) | |
(xf/by-key (comp first :closest-coords) :location (xf/into [])) | |
(remove (fn [[_ locations]] | |
(some #(boundary? % [min-x max-x] [min-y max-y]) | |
locations))) | |
(map (comp count second)) | |
(xf/sort-by identity >) | |
(take 1) | |
) | |
(for [x (range min-x (inc max-x)) | |
y (range min-y (inc max-y))] | |
[x y])))) | |
(largest-manhattan-area input-6) | |
; Day 6, puzzle 2 | |
(defn locations-within-total-distance [coords distance-cutoff] | |
(let [[min-x max-x] (extent (map first coords)) | |
[min-y max-y] (extent (map second coords))] | |
(sequence | |
(comp | |
(map (fn [location] | |
(transduce | |
(map #(manhattan-distance location %)) | |
+ coords))) | |
(filter #(< % distance-cutoff)) | |
xf/count) | |
(for [x (range min-x (inc max-x)) | |
y (range min-y (inc max-y))] | |
[x y])))) | |
(locations-within-total-distance input-6 10000) | |
; Day 7, puzzle 1 | |
(def input-7 (map | |
(fn [s] | |
(let [[_ a b] (re-matches #"Step (.) must be finished before step (.) can begin." s)] | |
[a b])) | |
(string/split-lines (slurp "./resources/advent-of-code/2018/day-7-input.txt")))) | |
(defn dependencies [input] | |
(into (zipmap (distinct (mapcat seq input)) | |
(repeat #{})) | |
(xf/by-key second first (xf/into #{})) | |
input)) | |
(def no-dependencies | |
(comp | |
(filter (comp empty? val)) | |
(map key) | |
(xf/sort-by name))) | |
(defn excise-dependency [dependency] | |
(comp | |
(remove (comp #{dependency} key)) | |
(map #(update % 1 disj dependency)))) | |
(defn topological-sort [graph] | |
(when-let [[step] (seq (sequence no-dependencies graph))] | |
(concat [step] | |
(topological-sort | |
(into {} (excise-dependency step) graph))))) | |
(apply str (topological-sort (dependencies input-7))) | |
; Day 7, puzzle 2 | |
(defn gantt-time [graph time-to-do workers] | |
(loop [t 0 | |
graph graph | |
workers (repeat workers nil)] | |
(let [[step] (sequence | |
(comp | |
no-dependencies | |
(remove (set (map first workers)))) | |
graph)] | |
(cond | |
(empty? graph) | |
t | |
(and step (some nil? workers)) | |
(recur t | |
graph | |
(assoc (vec workers) | |
(.indexOf workers nil) | |
[step (+ t (time-to-do step))])) | |
:else | |
(let [[s t'] (apply min-key second (remove nil? workers))] | |
(recur t' | |
(into {} (excise-dependency s) graph) | |
(map (fn [[_ done :as worker]] | |
(when-not (= t' done) | |
worker)) | |
workers))))))) | |
(gantt-time | |
(dependencies input-7) | |
(into {} (map vector (map str "ABCDEFGHIJKLMNOPQRSTUVWXYZ") (iterate inc 61))) | |
5) | |
; Day 8, puzzle 1 | |
(def input-8 (-> (slurp "./resources/advent-of-code/2018/day-8-input.txt") | |
string/trim | |
(string/split #" ") | |
(->> (map parse-int)))) | |
(defn parse-tree | |
([values] (ffirst (parse-tree values 1))) | |
([values n] | |
(loop [n n | |
values values | |
nodes []] | |
(if (pos? n) | |
(let [[node-count metadata-count & values] values | |
[children values] (parse-tree values node-count) | |
[metadata values] (split-at metadata-count values)] | |
(recur (dec n) | |
values | |
(conj nodes {:children children :metadata metadata}))) | |
[nodes values])))) | |
(->> (parse-tree input-8) | |
(tree-seq map? :children) | |
(mapcat :metadata) | |
(reduce +)) | |
; Day 8, puzzle 2 | |
(defn node-value [{:keys [children metadata]}] | |
(if (seq children) | |
(->> metadata | |
(map (fn [i] | |
(node-value (get children (dec i) {})))) | |
(reduce +)) | |
(reduce + metadata))) | |
(node-value (parse-tree input-8)) | |
; Day 9, puzzle 1 | |
(def input-9 (let [s (string/trim (slurp "./resources/advent-of-code/2018/day-9-input.txt")) | |
[_ players marbles] (re-matches #"(\d+) players; last marble is worth (\d+) points" s)] | |
{:players (parse-int players) | |
:highest-marble (parse-int marbles)})) | |
(defn zip-right-wrap [loc] | |
(or (zip/right loc) (zip/leftmost loc))) | |
(defn zip-left-wrap [loc] | |
(or (zip/left loc) (zip/rightmost loc))) | |
(defn place-marble [marbles marble] | |
(-> marbles zip-right-wrap (zip/insert-right marble) zip/right)) | |
(defn remove-scoring-marble [marbles] | |
(let [marbles (nth (iterate zip-left-wrap marbles) 7)] | |
[(zip/node marbles) | |
(-> marbles zip/remove zip-right-wrap)])) | |
(defn top-marble-score [players highest-marble] | |
(loop [marbles (zip/down (zip/vector-zip [0])) | |
scores {} | |
players (cycle (range 1 (inc players))) | |
marble 1] | |
(cond | |
(< highest-marble marble) (apply max-key val scores) | |
(zero? (mod marble 23)) (let [[scoring-marble marbles] (remove-scoring-marble marbles)] | |
(recur marbles | |
(update scores (first players) (fnil + 0) marble scoring-marble) | |
(next players) | |
(inc marble))) | |
:else (recur (place-marble marbles marble) | |
scores | |
(next players) | |
(inc marble))))) | |
(second (top-marble-score (input-9 :players) (input-9 :highest-marble))) | |
; Day 9, puzzle 2 | |
(second (top-marble-score (input-9 :players) (* 100 (input-9 :highest-marble)))) | |
; Day 10, puzzle 1 | |
(defn parse-input-10 [line] | |
(let [[_ x y dx dy] (re-matches #"position=<\s*(-?\d+),\s*(-?\d+)> velocity=<\s*(-?\d+),\s*(-?\d+)>" line)] | |
{:position [(parse-int x) (parse-int y)] | |
:velocity [(parse-int dx) (parse-int dy)]})) | |
(def input-10 (map parse-input-10 (string/split-lines (slurp "./resources/advent-of-code/2018/day-10-input.txt")))) | |
(defn advance-light [n {:keys [velocity] :as point}] | |
(update point :position (partial mapv #(+ %1 (* n %2))) velocity)) | |
(defn drawable? [coords] | |
(let [[top bottom] (extent (map second coords))] | |
(> 80 (- bottom top)))) | |
(defn draw-lights [coords] | |
(let [[left right] (extent (map first coords)) | |
[top bottom] (extent (map second coords))] | |
(transduce | |
(comp | |
(map string/join) | |
(interpose "\n")) | |
str | |
(for [y (range top (inc bottom))] | |
(for [x (range left (inc right))] | |
(if (contains? coords [x y]) \# \space)))))) | |
(def pattern | |
(ffirst | |
(sequence | |
(comp | |
(map (partial map :position)) | |
(map set) | |
(drop-while (complement drawable?)) | |
(take-while drawable?) | |
(bests-by height <)) | |
(iterate #(map (partial advance-light 1) %) input-10)))) | |
(println (draw-lights pattern)) | |
; Day 10, puzzle 2 | |
(sequence | |
(comp | |
(map (partial map :position)) | |
(map set) | |
(take-while (partial not= pattern)) | |
xf/count) | |
(iterate #(map (partial advance-light 1) %) input-10)) | |
; Day 11, puzzle 1 | |
(def input-11 (parse-int (string/trim (slurp "./resources/advent-of-code/2018/day-11-input.txt")))) | |
(defn cell-power [grid-serial-number x y] | |
(let [rack-id (+ x 10)] | |
(-> (* y rack-id) | |
(+ grid-serial-number) | |
(* rack-id) | |
(as-> $ (mod (int (/ $ 100)) 10)) | |
(- 5)))) | |
(defn block-power [grid-serial-number size x y] | |
(reduce + (for [dx (range size), dy (range size)] | |
(cell-power grid-serial-number (+ x dx) (+ y dy))))) | |
(let [grid-serial-number input-11] | |
(sequence | |
(comp | |
(bests-by #(apply block-power grid-serial-number 3 %) >) | |
(map first)) | |
(for [x (range 1 299), y (range 1 299)] | |
[x y]))) | |
; Day 11, puzzle 2 | |
(defn summed-area-table [f table [x y]] | |
(assoc table [x y] | |
(+ (f x y) | |
(table [(dec x) y] 0) | |
(table [x (dec y)] 0) | |
(- (table [(dec x) (dec y)] 0))))) | |
(defn area [summed-area-table [x y size]] | |
(let [x (dec x), y (dec y)] | |
(+ (summed-area-table [(+ x size) (+ y size)]) | |
(- (summed-area-table [(+ x size) y] 0)) | |
(- (summed-area-table [x (+ y size)] 0)) | |
(summed-area-table [x y] 0)))) | |
(let [cells (for [x (range 1 301), y (range 1 301)] [x y]) | |
table (reduce (partial summed-area-table #(cell-power input-11 %1 %2)) {} cells)] | |
(sequence | |
(bests-by (partial area table) >) | |
(for [[x y] cells, size (range 1 (- 300 (max x y)))] | |
[x y size]))) | |
; Day 12, puzzle 1 | |
(defn parse-input-12 [s] | |
(let [[initial _ & rules] (string/split-lines s) | |
[_ initial] (string/split initial #": ") | |
plant? #(= \# %)] | |
{:initial (into {} (map #(do [%1 (plant? %2)]) (range) initial)) | |
:rules (into {} | |
(map (fn [s] | |
(let [[_ input output] (re-matches #"([.#]{5}) => ([.#])" s)] | |
[(mapv plant? input) (plant? (first output))]))) | |
rules)})) | |
(def input-12 (parse-input-12 (slurp "./resources/advent-of-code/2018/day-12-input.txt"))) | |
(defn advance-plants [rules plants] | |
(let [[low high] (extent (map key (filter val plants)))] | |
(into {} | |
(map (fn [i] | |
(let [s (mapv #(plants % false) | |
(range (- i 2) (+ i 3)))] | |
[i (rules s false)]))) | |
(range (- low 2) (+ high 3))))) | |
(defn generations [{:keys [initial rules]}] | |
(map #(reduce + (map first (filter val %))) | |
(iterate (partial advance-plants rules) initial))) | |
(sequence | |
(comp | |
(drop 20) | |
(take 1)) | |
(generations input-12)) | |
; Day 12, puzzle 2 | |
(let [sample 1000 | |
target 50e9 | |
[[x dx]] (sequence | |
(comp | |
(xf/partition 2 1 (xf/into [])) | |
(map (fn [[a b]] [b (- b a)])) | |
(take sample) | |
xf/last) | |
(generations input-12))] | |
(java.math.BigDecimal. (+ x (* dx (- target sample))))) | |
; Day 13, puzzle 1 | |
(def input-13 (slurp "./resources/advent-of-code/2018/day-13-input.txt")) | |
(def e [1 0]) | |
(def w [-1 0]) | |
(def n [0 -1]) | |
(def s [0 1]) | |
(def left [0 -1]) | |
(def right [0 1]) | |
(def straight [1 0]) | |
(def next-turn {left straight, straight right, right left}) | |
(defn ** [[r1 i1] [r2 i2]] | |
[(+ (* r1 r2) (- (* i1 i2))) | |
(+ (* r1 i2) (* r2 i1))]) | |
(defn parse-grid [input] | |
(into {} | |
(comp | |
(map-indexed (fn [row line] | |
(map-indexed | |
#(do [[%1 row] %2]) | |
line))) | |
(mapcat seq)) | |
(string/split-lines input))) | |
(defn parse-track [input] | |
(let [grid (parse-grid input)] | |
{:track (into {} (remove (comp #{\space \< \> \v \^ \- \|} val)) grid) | |
:carts (into {} | |
(comp | |
(filter (comp #{\< \> \^ \v} val)) | |
(map (fn [[location c]] | |
{:location location | |
:direction (get {\< [-1 0], \> [1 0], \^ [0 -1], \v [0 1]} c) | |
:next-turn left})) | |
(map (juxt :location identity))) | |
grid)})) | |
(defn advance-cart [track {[x y :as location] :location turn :next-turn :keys [direction] :as cart}] | |
(let [new-location (mapv + location direction) | |
cart (assoc cart :location new-location) | |
new-track (track new-location)] | |
(cond | |
(nil? new-track) cart | |
(= \+ new-track) (assoc cart :direction (** direction turn), :next-turn (next-turn turn)) | |
(and (= \\ new-track) (#{n s} direction)) (update cart :direction ** left) | |
(and (= \\ new-track) (#{e w} direction)) (update cart :direction ** right) | |
(and (= \/ new-track) (#{n s} direction)) (update cart :direction ** right) | |
(and (= \/ new-track) (#{e w} direction)) (update cart :direction ** left)))) | |
(def grid-order (comp vec reverse)) | |
(defn advance-carts-until-collision [track carts] | |
(let [carts (reduce | |
(fn [carts cart] | |
(let [cart' (advance-cart track cart)] | |
(if (carts (cart' :location)) | |
(reduced {:collision cart'}) | |
(-> carts | |
(dissoc (cart :location)) | |
(assoc (cart' :location) cart'))))) | |
carts (sort-by (comp grid-order :location) (vals carts)))] | |
(if-let [collision (:collision carts)] | |
(reduced collision) | |
carts))) | |
(defn carts [advance-carts input] | |
(let [{:keys [track carts]} (parse-track input)] | |
(iterate (partial advance-carts track) carts))) | |
(sequence | |
(comp | |
(filter reduced?) | |
(map deref) | |
(map :location) | |
(take 1)) | |
(carts advance-carts-until-collision input-13)) | |
; Day 13, puzzle 2 | |
(defn advance-carts-removing-collisions [track carts] | |
(reduce | |
(fn [carts cart] | |
(if (carts (cart :location)) | |
(let [cart' (advance-cart track cart)] | |
(if (carts (cart' :location)) | |
(dissoc carts (cart :location) (cart' :location)) | |
(-> carts | |
(dissoc (cart :location)) | |
(assoc (cart' :location) cart')))) | |
carts)) | |
carts (sort-by (comp grid-order :location) (vals carts)))) | |
(sequence | |
(comp | |
(drop-while (comp (partial < 1) count)) | |
(take 1) | |
(map keys)) | |
(carts advance-carts-removing-collisions input-13)) | |
; Day 14, puzzle 1 | |
(def input-14 (string/trim (slurp "./resources/advent-of-code/2018/day-14-input.txt"))) | |
(def divmod (juxt (comp int /) mod)) | |
(defn next-recipes [[indices recipies]] | |
(let [total (transduce (map recipies) + indices) | |
recipies (if (<= 10 total) | |
(into recipies (divmod total 10)) | |
(conj recipies total)) | |
c (count recipies)] | |
[(map #(mod (+ 1 % (recipies %)) c) indices) | |
recipies])) | |
(defn eventual-recipies [to-take next-n [initial-indices initial-recipies]] | |
(sequence | |
(comp | |
(drop to-take) ; more than necessary, but faster than checking each iteration | |
(take 1) | |
(map (comp #(subvec % to-take (+ to-take next-n)) second))) | |
(iterate next-recipes [initial-indices initial-recipies]))) | |
(->> [[0 1] [3 7]] | |
(eventual-recipies (parse-int input-14) 10) | |
first | |
(apply str) | |
time) | |
; Day 14, puzzle 2 | |
(-> (iterate next-recipes [[0 1] [3 7]]) | |
(nth 20e6) | |
second | |
(->> (apply str)) | |
(.indexOf input-14)) | |
; Day 16, puzzle 1 | |
(defn parse-input-16 [s] | |
(let [[a b] (string/split s #"\n\n\n\n")] | |
{:examples (map | |
(fn [s] | |
(let [[before operation after] (string/split-lines s) | |
[_ before-registers] (re-matches #"Before: \[(.*)\]" before) | |
[_ after-registers] (re-matches #"After: \[(.*)\]" after)] | |
{:operation (map parse-int (re-seq #"\d+" operation)) | |
:before (mapv parse-int (string/split before-registers #", ")) | |
:after (mapv parse-int (string/split after-registers #", "))})) | |
(string/split a #"\n\n")) | |
:program (map | |
(fn [s] | |
(map parse-int (re-seq #"\d+" s))) | |
(string/split-lines b))})) | |
(def input-16 (parse-input-16 (slurp "./resources/advent-of-code/2018/day-16-input.txt"))) | |
(defn op [f] | |
(fn [registers [_ a b c]] | |
(assoc registers c (f registers a b)))) | |
(def ops | |
{:addr (op (fn [r a b] (+ (r a) (r b)))) | |
:addi (op (fn [r a b] (+ (r a) b))) | |
:mulr (op (fn [r a b] (* (r a) (r b)))) | |
:muli (op (fn [r a b] (* (r a) b))) | |
:banr (op (fn [r a b] (bit-and (r a) (r b)))) | |
:bani (op (fn [r a b] (bit-and (r a) b))) | |
:borr (op (fn [r a b] (bit-or (r a) (r b)))) | |
:bori (op (fn [r a b] (bit-or (r a) b))) | |
:setr (op (fn [r a _] (r a))) | |
:seti (op (fn [r a _] a)) | |
:gtir (op (fn [r a b] (if (> a (r b)) 1 0))) | |
:gtri (op (fn [r a b] (if (> (r a) b) 1 0))) | |
:gtrr (op (fn [r a b] (if (> (r a) (r b)) 1 0))) | |
:eqir (op (fn [r a b] (if (= a (r b)) 1 0))) | |
:eqri (op (fn [r a b] (if (= (r a) b) 1 0))) | |
:eqrr (op (fn [r a b] (if (= (r a) (r b)) 1 0)))}) | |
(defn valid-operations [{:keys [operation before after]}] | |
(filter | |
(comp #{after} #(% before operation) second) | |
ops)) | |
(sequence | |
(comp | |
(filter (comp (partial <= 3) count valid-operations)) | |
xf/count) | |
(input-16 :examples)) | |
; Day 16, puzzle 2 | |
(defn deduce-ops [examples] | |
(reduce | |
(fn [op->n example] | |
(let [valid-ops (sequence | |
(comp | |
(map key) | |
(remove op->n)) | |
(valid-operations example))] | |
(if (= 1 (count valid-ops)) | |
(assoc op->n (first valid-ops) (-> example :operation first)) | |
op->n))) | |
{} | |
examples)) | |
(let [{:keys [examples program]} input-16 | |
n->op (map-invert (deduce-ops examples))] | |
(reduce | |
(fn [registers [n :as operation]] | |
(let [f (ops (n->op n))] | |
(f registers operation))) | |
[0 0 0 0] | |
program)) | |
; Day 18, puzzle 1 | |
(def input-18 (parse-grid (slurp "./resources/advent-of-code/2018/day-18-input.txt"))) | |
(defn next-forest [cells] | |
(into {} | |
(map (fn [[location c]] | |
[location | |
(let [neighbors (frequencies (for [dx [-1 0 1] | |
dy [-1 0 1] | |
:when (not= [0 0] [dx dy])] | |
(cells (mapv + location [dx dy]))))] | |
(cond | |
(and (= c \.) (<= 3 (neighbors \| 0))) \| | |
(and (= c \|) (<= 3 (neighbors \# 0))) \# | |
(and (= c \#) (<= 1 (neighbors \# 0)) (<= 1 (neighbors \| 0))) \# | |
(= c \#) \. | |
:else c))])) | |
cells)) | |
(defn resource-value [forest] | |
(let [summary (frequencies (vals forest))] | |
(* (summary \|) (summary \#)))) | |
(resource-value (nth (iterate next-forest input-18) 10)) | |
; Day 18, puzzle 2 | |
(defn take-until-repeat [rf] | |
(let [seen (volatile! #{})] | |
(fn | |
([] (rf)) | |
([result] (rf result)) | |
([result item] | |
(if (@seen item) | |
(reduced result) | |
(do (vswap! seen conj item) | |
(rf result item))))))) | |
(defn predict-from-eventual-cycle [n xs] | |
(let [c (count xs)] | |
(if (< n c) | |
(nth xs n) | |
(let [repeats (->> xs reverse (sequence take-until-repeat) reverse)] | |
(nth repeats (mod (- n c) (count repeats))))))) | |
(predict-from-eventual-cycle | |
1000000000 | |
(sequence | |
(comp | |
(map resource-value) | |
(take 1000)) | |
(iterate next-forest input-18))) | |
; Day 20, puzzle 1 | |
(def input-20 (string/trim (slurp "./resources/advent-of-code/2018/day-20-input.txt"))) | |
(defn construct-map [directions] | |
(let [set-conj (fnil conj #{})] | |
(:doors | |
(reduce | |
(fn [{:keys [doors room checkpoints] :as walk} c] | |
(let [add-room (fn [direction] | |
(let [room' (mapv + room direction)] | |
(-> walk | |
(update-in [:doors room] set-conj room') | |
(update-in [:doors room'] set-conj room) | |
(assoc :room room'))))] | |
(condp = c | |
\^ walk | |
\$ walk | |
\E (add-room e) | |
\N (add-room n) | |
\W (add-room w) | |
\S (add-room s) | |
\( (update walk :checkpoints conj room) | |
\) (update walk :checkpoints pop) | |
\| (assoc walk :room (peek checkpoints))))) | |
{:doors {} :room [0 0] :checkpoints []} | |
directions)))) | |
(defn maze-walk [doors location] | |
(loop [new-locations [[location 0]] | |
walk {location 0}] | |
(if (seq new-locations) | |
(let [new-locations (into {} | |
(comp | |
(map #(update % 1 inc)) | |
(mapcat (fn [[location steps]] | |
(map #(do [% steps]) (doors location)))) | |
(remove (comp walk first))) | |
new-locations)] | |
(recur new-locations (merge-with min walk new-locations))) | |
walk))) | |
(apply max (vals (maze-walk (construct-map input-20) [0 0]))) | |
; Day 20, puzzle 2 | |
(->> (maze-walk (construct-map input-20) [0 0]) | |
vals | |
(filter (partial <= 1000)) | |
count) | |
; Day 23, puzzle 1 | |
(defn parse-day-23 [s] | |
(when-let [[_ x y z r] (re-matches #"pos=<(-?\d+),(-?\d+),(-?\d+)>, r=(\d+)" s)] | |
{:position (mapv parse-int [x y z]) | |
:radius (parse-int r)})) | |
(def input-23 (map parse-day-23 (string/split-lines (slurp "./resources/advent-of-code/2018/day-23-input.txt")))) | |
(let [bots input-23 | |
[{:keys [position radius]}] (sort-by :radius > bots)] | |
(sequence | |
(comp | |
(map (comp (partial manhattan-distance position) :position)) | |
(filter (partial >= radius)) | |
xf/count) | |
bots)) | |
; Day 25, puzzle 1 | |
(defn parse-input-25 [s] | |
(mapv parse-int (re-seq #"-?\d+" s))) | |
(def input-25 (map parse-input-25 (string/split-lines (slurp "./resources/advent-of-code/2018/day-25-input.txt")))) | |
(defn constellations [pts] | |
(let [connected? (fn [pts pt] | |
(some #(>= 3 (manhattan-distance % pt)) pts))] | |
(reduce | |
(fn [cs pt] | |
(let [{connected true not-connected false} (group-by (comp boolean #(connected? % pt)) cs)] | |
(if (seq connected) | |
(conj not-connected (into #{pt} (mapcat seq) connected)) | |
(conj cs #{pt})))) | |
() pts))) | |
(count (constellations input-25)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment