Skip to content

Instantly share code, notes, and snippets.

@antbbn
Last active December 27, 2021 18:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save antbbn/5907c3c2f733e5156140420b2cb49568 to your computer and use it in GitHub Desktop.
Save antbbn/5907c3c2f733e5156140420b2cb49568 to your computer and use it in GitHub Desktop.
(ns day10
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]))
(def small
"[({(<(())[]>[[{[]{<()<>>
[(()[<>])]({[<{<<[]>>(
{([(<{}[<>[]}>{[]{[(<()>
(((({<>}<{<{<>}{[]{[]{}
[[<[([]))<([[{}[[()]]]
[{[{({}]{}}([{[{{{}}([]
{<[[]]>}<{[{[{[]{()[[[]
[<(<(<(<{}))><([]([]()
<{([([[(<>()){}]>(<<{{
<{([{{}}[<[[[<>{}]]]>[]]")
(def large (slurp "/home/antbbn/aoc_2021/input_10.txt"))
(def points {\) 3 \] 57 \} 1197 \> 25137})
(def open->close {\( \) \[ \] \{ \} \< \>})
(def open? #{\( \[ \{ \<})
(defn illegal? [instructions]
(loop [stack '() line instructions]
(let [cur (peek line)]
(cond
(open? cur) (recur (conj stack cur) (pop line))
(= cur (open->close (peek stack))) (recur (pop stack) (pop line))
:else cur))))
(->> large;small
string/split-lines
(map #(apply list %))
(map illegal?)
(remove nil?)
(map points)
(reduce +))
(def part2-points {\) 1 \] 2 \} 3 \> 4})
(defn score [completion]
(reduce #(+ (* 5 %1) (part2-points %2)) 0 completion))
(score "])}>")
(defn complete [instructions]
(loop [stack '() line instructions]
(let [cur (peek line)]
(cond
(nil? cur) (map open->close stack)
(open? cur) (recur (conj stack cur) (pop line))
(= cur (open->close (peek stack))) (recur (pop stack) (pop line))))))
(illegal? (apply list "[({(<(())[]>[[{[]{<()<>>"))
(complete (apply list "[({(<(())[]>[[{[]{<()<>>"))
(defn get-middle [s]
(nth s (/ (count s) 2)))
(->> large;small
string/split-lines
(map #(apply list %))
(map complete)
(remove nil?)
(map score)
sort
get-middle
)
; regex approach
(defn fixed-point [f a]
(let [r (f a)]
(if (= r a)
a
(recur f r))))
(defn validate [line]
(let [fp (fixed-point #(string/replace % #"\(\)|\[\]|\{\}|\<\>" "") line)
invalid (re-find #"\)|\]|\}|\>" fp)]
[invalid (map open->close (string/reverse fp))]))
(def points {")" 3 "]" 57 "}" 1197 ">" 25137})
(->> large
string/split-lines
#_(map #(apply list %))
(map validate)
(map first)
(remove nil?)
(map points)
(reduce +))
(def part2-points {")" 1 "]" 2 "}" 3 ">" 4})
(->> large
string/split-lines
#_(map #(apply list %))
(map validate)
(filter (comp nil? first))
(map second)
(map score)
sort
get-middle)
(ns day11
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]
[clojure.test :refer [is are]]
[clojure.edn :as edn]
[clojure.set :refer [difference intersection union]]))
(def small "11111
19991
19191
19991
11111")
(def example "5483143223
2745854711
5264556173
6141336146
6357385478
4167524645
2176841721
6882881134
4846848554
5283751526")
(def input "4341347643
5477728451
2322733878
5453762556
2718123421
4237886115
5631617114
2217667227
4236581255
4482627641")
(defn parse [input]
(->> input
string/split-lines
(mapv (fn [row]
(mapv #(Integer/parseInt (str %)) row)))))
(parse small)
(defn get-neighbor-pos* [state [y x]]
(let [max-y (count state)
max-x (count (first state))]
(for [dx [-1 0 1]
dy [-1 0 1]
:let [new-x (+ dx x)
new-y (+ dy y)]
:when (and (not= [x y] [new-x new-y]) (< -1 new-x max-x) (< -1 new-y max-y))]
[new-y new-x])
))
(defn all-indexes [m]
(for [x (range (count (first m)))
y (range (count m))]
[y x]))
(defn inc-all [octopi]
(reduce (fn [octopi pos]
(update-in octopi pos inc))
octopi
(all-indexes octopi)))
(defn inc* [n]
(if (zero? n)
n
(inc n)))
(defn step [state]
(loop [octopi (inc-all state) positions (all-indexes state)]
(if-not (empty? positions)
(let [pos (first positions)
level (get-in octopi pos)
neighbors (get-neighbor-pos* octopi pos)]
(if (<= level 9)
(recur octopi (rest positions))
(recur (assoc-in (reduce #(update-in %1 %2 inc*) octopi neighbors) pos 0)
(concat (rest positions) neighbors))))
octopi)))
(defn count-flashed [octopi]
(reduce (fn [tot row]
(+ tot
(count (filter #(= 0 %) row))))
0
octopi))
(defn all-flashed? [octopi]
(= 0
(reduce + (map #(reduce + %) octopi))))
(-> small
parse
step)
(->> input
parse
(iterate step)
(take 101)
(map count-flashed)
(reduce +))
(->> input
parse
(iterate step)
(map-indexed #(vector %1 (all-flashed? %2)))
(filter second)
first)
(ns day12
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]
[clojure.test :refer [is are]]
[clojure.edn :as edn]
[clojure.set :refer [difference intersection union]]))
(def small "start-A
start-b
A-c
A-b
b-d
A-end
b-end")
(def example "dc-end
HN-start
start-kj
dc-start
dc-HN
LN-dc
HN-end
kj-sa
kj-HN
kj-dc")
(def example-large "fs-end
he-DX
fs-he
start-DX
pj-DX
end-zg
zg-sl
zg-pj
pj-he
RW-he
fs-DX
pj-RW
zg-RW
start-pj
he-WI
zg-he
pj-fs
start-RW")
(def input "xq-XZ
zo-yr
CT-zo
yr-xq
yr-LD
xq-ra
np-zo
end-LD
np-LD
xq-kq
start-ra
np-kq
LO-end
start-xq
zo-ra
LO-np
XZ-start
zo-kq
LO-yr
kq-XZ
zo-LD
kq-ra
XZ-yr
LD-ws
np-end
kq-yr")
(defn parse [input]
(->> input
string/split-lines
(map #(string/split % #"-"))))
(defn collect* [m [k v]]
(cond-> m
(and (not= "start" v) (not= "end" k)) (update k conj v)
(and (not= "start" k) (not= "end" v)) (update v conj k)))
(defn big-cave-or-once-small? [path-so-far move]
(or
(= move (string/upper-case move))
(> 0 (.indexOf path-so-far move))))
; return all the paths
(defn all-paths [allowed? connections path-so-far]
(if-let [next-moves (seq (filter #(allowed? path-so-far %)
(connections (peek path-so-far))))]
(mapcat #(all-paths allowed? connections (conj path-so-far %)) next-moves)
(list path-so-far)))
(def connections
(->> input
parse
(reduce collect* {})))
; part-1 solution
(count
(filter #(= "end" (last %))
(all-paths big-cave-or-once-small? connections ["start"])))
; part2
(defn upper-case? [^String s]
(= s (string/upper-case s)))
(defn lower-case? [^String s]
(= s (string/lower-case s)))
(defn big-cave-or-upto-twice-small? [path-so-far move]
(or
(upper-case? move)
(> 0 (.indexOf path-so-far move))
(->> path-so-far
(filter lower-case?)
frequencies
vals
(apply max)
(>= 1))))
(time
(count
(filter #(= "end" (peek %))
(all-paths big-cave-or-upto-twice-small? connections ["start"]))))
(ns day13
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]
[clojure.test :refer [is are]]
[clojure.edn :as edn]
[clojure.set :refer [difference intersection union]]))
(def example "6,10
0,14
9,10
0,3
10,4
4,11
6,0
6,12
4,1
0,13
10,12
3,4
3,0
8,4
1,10
2,14
8,10
9,0
fold along y=7
fold along x=5")
(def input (slurp "/home/antbbn/aoc_2021/input_13.txt"))
(def dots (first (string/split input #"\n\n")))
(def folds (second (string/split input #"\n\n")))
(defn parse-dots [input]
(->> input
string/split-lines
(map #(string/split % #","))
(map (fn [[x y]]
{:x (Integer/parseInt x) :y (Integer/parseInt y)}))
(set)))
(parse-dots dots)
(defn parse-folds [input]
(->> input
string/split-lines
(map #(string/split % #"="))
(map (fn [[along line]]
{:along (if (= along "fold along x") :x :y) :line (Integer/parseInt line)}))))
(parse-folds folds)
(defn fold-coord [coord line]
(if (> coord line)
(- (* line 2) coord)
coord))
(fold-coord 5 4)
(defn fold-once [dots {:keys [along line]}]
(set (map #(update % along fold-coord line) dots)))
(def first-fold (first (parse-folds folds)))
(-> dots
parse-dots
(fold-once first-fold)
count)
(def result
(reduce fold-once (parse-dots dots) (parse-folds folds)))
(def display
(->> \space
(repeat (inc (apply max (map :x result))))
vec
(repeat (inc (apply max (map :y result))))
vec))
display
(map #(prn (string/join %))
(reduce #(assoc-in %1 [(:y %2) (:x %2)] \#) display result))
(ns day14
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]
[clojure.test :refer [is are]]
[clojure.edn :as edn]
[clojure.set :refer [difference intersection union]]))
(def example "NNCB
CH -> B
HH -> N
CB -> H
NH -> C
HB -> C
HC -> B
HN -> C
NN -> C
BH -> H
NC -> B
NB -> B
BN -> B
BB -> N
BC -> B
CC -> N
CN -> C")
(def large (slurp "/home/antbbn/aoc_2021/input_14.txt"))
(defn get-template [input]
(-> input
string/split-lines
first))
(defn get-template [input]
(-> input
string/split-lines
first))
(defn parse [line]
[((juxt first second) line)
[((juxt first last) line) ((juxt last second) line)]])
(defn get-insertions [input]
(->> input
string/split-lines
(drop 2)
(map parse)
(into {})))
(defn process [insertions pairs]
(apply merge-with +
(mapcat (fn [[pair occurrences]]
(let [[pair-1 pair-2] (insertions pair)]
[{pair-1 occurrences} {pair-2 occurrences}]))
pairs)))
(defn count-letters [result]
(apply merge-with +
(mapcat (fn [[[a b] occurr]]
[{a occurr} {b occurr}])
result)))
(let [insertions (get-insertions example)
template (get-template example)]
(->>
(->
(->> template
(partition 2 1)
frequencies
(iterate (partial process insertions))
(drop 40)
first
count-letters)
(update (first template) inc)
(update (last template) inc))
vals
(apply (juxt max min))
(apply -)
(* 1/2)))
(ns day15
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]))
(def example "1163751742
1381373672
2136511328
3694931569
7463417111
1319128137
1359912421
3125421639
1293138521
2311944581")
(def input (slurp "/home/antbbn/aoc_2021/input_15.txt"))
(defn parse-grid [input]
(let [lines (string/split-lines input)
max-y (count lines)
max-x (count (first lines))]
{:start [0 0]
:end [(dec max-x) (dec max-y)]
:grid (into {} (for [y (range max-y)
x (range max-x)]
[[x y] (Integer/parseInt (str (get-in lines [y x])))]))}))
(defn get-neighbors [grid [x y]]
(filter #(grid %)
[[(inc x) y] [x (inc y)] [(dec x) y] [x (dec y)]]))
(defn change-smaller [old new]
(cond
(nil? old) new
(< old new) old
:else new))
(defn update-tds [grid visited tds node distance]
(reduce (fn [tds neighbor]
(update tds neighbor change-smaller (+ (grid neighbor) distance)))
(dissoc tds node)
(->> node
(get-neighbors grid)
(remove visited))))
; part1, the gas in the loop is just used to prevent infinite recursion while developing :D
(time
(let [{:keys [start end grid]} (parse-grid input)]
(loop [visited #{} tds {start 0} gas 10000]
(if (= 0 gas)
(sort-by val < tds)
(let [[node distance] (apply min-key val tds)]
(if (= node end)
(tds end)
(recur (conj visited node) (update-tds grid visited tds node distance) (dec gas))))))))
; horrible large grid creation :D
(defn parse-grid-part2 [input]
(let [lines (string/split-lines input)
max-y (count lines)
max-x (count (first lines))]
{:start [0 0]
:end [(dec (* max-x 5)) (dec (* max-y 5))]
:grid (into {}
(apply concat
(for [tile-x (range 5)
tile-y (range 5)]
(for [y (range max-y)
x (range max-x)]
[[(+ x (* tile-x max-x)) (+ y (* tile-y max-y))]
(-> [y x]
(#(get-in lines %))
str
Integer/parseInt
(+ tile-x tile-y)
dec
(mod 9)
inc)]))))}))
; part 2, best i could do is 15s
(time
(let [{:keys [start end grid]} (parse-grid-part2 input)]
(loop [visited #{} tds {start 0} gas 1000000]
(if (= 0 gas)
(sort-by val < tds)
(let [[node distance] (apply min-key val tds)]
(if (= node end)
(tds end)
(recur (conj visited node) (update-tds grid visited tds node distance) (dec gas))))))))
(ns day16
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]
[clojure.test :refer [is are]]
[clojure.edn :as edn]
[clojure.set :refer [difference intersection union]]))
(defn zero-pad [s]
(let [pad (- 4 (count s))]
(string/join (concat (repeat pad \0) s))))
(defn hex-char-to-bin [ch]
(-> ch
(Character/digit 16)
Integer/toBinaryString
zero-pad))
(declare parse-packet parse-many-packets)
(defn decode-lv [s]
(let [[bin-digits rest-payload]
(loop [bd [] rp s]
(let [[continue & d] (take 5 rp)]
(case continue
\0 [(concat bd d) (drop 5 rp)]
\1 (recur (concat bd d) (drop 5 rp)))))]
{:value (-> bin-digits string/join (Long/parseLong 2))
:rest (string/join rest-payload)}))
(defn decode-op [s]
(let [len-type (first s)
len (case len-type
\0 (Integer/parseInt (subs s 1 16) 2)
\1 (Integer/parseInt (subs s 1 12) 2))
payload (case len-type
\0 (subs s 16)
\1 (subs s 12))]
(case len-type
\0 (assoc (parse-many-packets len-type len (subs payload 0 len))
:rest (subs payload len))
\1 (parse-many-packets len-type len payload))))
(defn parse-packet [s]
(let [version (Integer/parseInt (subs s 0 3) 2)
type (Integer/parseInt (subs s 3 6) 2)
payload (subs s 6)]
(merge {:version version :type type}
(case type
4 (decode-lv payload)
(decode-op payload)))))
(defn parse-many-packets [len-type len s]
(loop [packets [] rp s]
(cond
(and (= len-type \0) (<= (count rp) 6)) {:packets packets}
(and (= len-type \1) (= len (count packets))) {:packets packets :rest rp}
:else (let [packet (parse-packet rp)]
(recur (conj packets (dissoc packet :rest)) (:rest packet))))))
(defn sum-versions [packet]
(loop [tot (:version packet) [p & ps] (:packets packet)]
(if p
(recur (+ tot (:version p)) (concat ps (:packets p)))
tot)))
(declare get-value do-op)
(defn do-op [{:keys [type packets]}]
(let [values (map get-value packets)]
(case type
0 (apply + values)
1 (apply * values)
2 (apply min values)
3 (apply max values)
5 (if (apply > values) 1 0)
6 (if (apply < values) 1 0)
7 (if (apply = values) 1 0))))
(defn get-value [{value :value :as packet}]
(or value (do-op packet)))
; part 1
(->> "/home/antbbn/aoc_2021/input_16.txt"
slurp
(map hex-char-to-bin)
string/join
parse-packet
sum-versions)
; part 2
(->> "/home/antbbn/aoc_2021/input_16.txt"
slurp
(map hex-char-to-bin)
string/join
parse-packet
get-value)
;; another way to finish part 2
(def get-op {
0 +
1 *
2 min
3 max
5 (comp {true 1 false 0} > )
6 (comp {true 1 false 0} < )
7 (comp {true 1 false 0} = )
})
(def get-op-sym {0 '+
1 '*
2 'min
3 'max
5 '(comp {true 1 false 0} >)
6 '(comp {true 1 false 0} <)
7 '(comp {true 1 false 0} =)})
(defn evaluate [{:keys [value type packets]}]
(if value
value
(apply (get-op type) (map evaluate packets))))
(defn evaluate-clj [{:keys [value type packets]}]
(if value
value
(apply list (get-op-sym type) (map evaluate-clj packets))))
;; and generate valid clojure source code
(->> "/home/antbbn/aoc_2021/input_16.txt"
slurp
(map hex-char-to-bin)
string/join
parse-packet
evaluate-clj
(spit "/home/antbbn/test.clj"))
;; contents of test.clj
$ cat test.clj
(+ (* ((comp {true 1, false 0} =) (+ 15 11 11) (+ 11 4 8)) 950084558) (* 114 242) (max 714157) (* ((comp {true 1, false 0} >) 660 40462) 1080380611) 121 (* 3740 ((comp {true 1, false 0} <) (+ 7 2 2) (+ 6 9 6))) (max 3271506590 6272) (* (+ 12 4 8) (+ 6 12 12) (+ 7 8 3)) (+ (* 9 7 10) (* 10 11 8) (* 9 3 14)) (* ((comp {true 1, false 0} >) 94 62) 254437741) (* ((comp {true 1, false 0} <) (+ 9 14 6) (+ 9 3 14)) 99) (* 142) (* ((comp {true 1, false 0} >) 17101 17101) 6) (max 25155 29315 14 25644) (* ((comp {true 1, false 0} <) 10 2250) 210) (* 185 ((comp {true 1, false 0} >) (+ 14 5 10) (+ 11 11 10))) 200005975 (max 476232 18866 19030 93 8) (* 12061 ((comp {true 1, false 0} >) (+ 5 12 7) (+ 14 5 6))) (* 245 ((comp {true 1, false 0} <) 620 620)) 1347 (+ 25318691444 33 194723139 13089283) (* 234 178 119 24) 1890688474 (* ((comp {true 1, false 0} <) 114 48) 120881) (* 2637497622 ((comp {true 1, false 0} =) 45204 119)) (* ((comp {true 1, false 0} >) 150 307806) 5975) (* ((comp {true 1, false 0} =) 1475578868 1475578868) 4016) (* 3157690802 ((comp {true 1, false 0} =) 4 505034)) (+ 24 3006) 15 (max 48062 100 222) (min 489983 143 61 193) 675828 10008144 72 (* 13 ((comp {true 1, false 0} <) 99110207 99110207)) (+ 244 1324 228 405 147273) (+ 187) (* 296 ((comp {true 1, false 0} <) 32859158782 1934)) (min 2684 12) 11 (* ((comp {true 1, false 0} >) 11720 11720) 799974) (min 1 2232031364 49284 1046 79) (* 249 ((comp {true 1, false 0} <) 13768 70390)) (+ 9 10602673884261 1924897270) 75 (* 249 231 12 9 183) (* (min (+ (* (* (max (max (* (+ (min (min (min (* (min (+ (* (max (+ (* (+ 613)))))))))))))))))))) (min 7) (* 45452 ((comp {true 1, false 0} >) 25672 3897)) (min 42 308362 3) (* 74 11 30))
(ns day17
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]
[clojure.test :refer [is are]]
[clojure.edn :as edn]
[clojure.set :refer [difference intersection union]]))
; exampl
(def x-range [20 30])
(def y-range [-10 -5])
; input
(def x-range [150 171])
(def y-range [-129 -70])
; part1
; max height = v0*(v0+1)/2
; at y=0 v = -v0
; max reach from y=0 is -v0-1
; to be within the area and maximize v0 we shoot for the min
(def v0 (* -1 (inc (apply min y-range))))
v0
(/ (* v0 (inc v0)) 2)
; part2
(def x-vel-range [(int (Math/ceil (/ (+ -1 (Math/sqrt (+ 1 (* 8 (apply min x-range))))) 2)))
(inc (apply max x-range))])
x-vel-range
(def y-vel-range [(apply min y-range)
(inc v0)])
y-vel-range
(defn x-at [v0 s]
(reduce + (map #(- v0 %) (range (min s v0)))))
(defn y-at [v0 s]
(reduce + (map #(- v0 %) (range s))))
(defn between [b [a c]]
(<= a b c))
(def max-s (first (filter #(> (apply min y-range) (y-at (apply max y-vel-range) %)) (range))))
(loop [candidates (for [x (apply range x-vel-range)
y (apply range y-vel-range)]
[x y])
valid #{}
step 1]
(if (= step max-s)
(count valid)
(let [{inside true outside false}
(group-by (fn [[v0x v0y]]
(and (between (x-at v0x step) x-range)
(between (y-at v0y step) y-range)))
candidates)]
(recur outside (apply conj valid inside) (inc step)))))
(time
(count
(:valid
(reduce (fn [{:keys [candidates valid]} step]
(let [{inside true outside false}
(group-by (fn [[v0x v0y]]
(and (between (x-at v0x step) x-range)
(between (y-at v0y step) y-range)))
candidates)]
{:candidates outside
:valid (apply conj valid inside)}))
{:candidates (for [x (apply range x-vel-range)
y (apply range y-vel-range)]
[x y])
:valid #{}}
(range 1 max-s)))))
(ns day18
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]))
(defn parse [number-input]
(reduce (fn [{nl :nest-level :as m} ch]
(case ch
\, m
\[ (update m :nest-level inc)
\] (update m :nest-level dec)
(update m :queue conj [(Character/digit ch 10) nl])))
{:queue clojure.lang.PersistentQueue/EMPTY :nest-level 0}
number-input))
(parse "[[[[1,2],[3,4]],[[5,6],[7,8]]],9]")
(defn split [v nl]
[[(quot v 2) (inc nl)]
[(+ (quot v 2) (rem v 2)) (inc nl)]])
(defn conj* [coll val]
(if val
(conj coll val)
coll))
(defn merge+ [v1 v2 nl]
(when (and v1 v2)
[(+ v1 v2) nl]))
(merge+ 1 nil 3)
(conj* [1 2 3] nil)
(defn snail-reduce
([queue]
(snail-reduce (clojure.lang.PersistentQueue/EMPTY)
nil
queue))
([front [buf-val buf-nl :as buf] back]
(let [[next-val next-nl] (peek back)]
;; (clojure.pprint/pprint front)
;; (prn buf-val buf-nl)
;; (clojure.pprint/pprint back)
(cond
(and (nil? buf-val) (empty? back)) front
(and (some? buf-val) (< 4 buf-nl)) (recur (clojure.lang.PersistentQueue/EMPTY)
nil
(into (conj* front (merge+ buf-val next-val next-nl)) (pop back)))
(and (some? next-nl) (< 4 next-nl)) (recur (conj (conj* front (merge+ buf-val next-val buf-nl)) [0 (dec next-nl)])
(peek (pop back))
(pop (pop back)))
(and (some? buf-val) (< 9 buf-val)) (recur (clojure.lang.PersistentQueue/EMPTY)
nil
(into (apply conj front (split buf-val buf-nl)) back))
:else (recur (conj* front buf) [next-val next-nl] (pop back))))))
(snail-reduce (clojure.lang.PersistentQueue/EMPTY)
nil
(:queue (parse "[[[[[4,3],4],4],[7,[[8,4],9]]],[1,1]]")))
(snail-reduce (:queue (parse "[[[[[4,3],4],4],[7,[[8,4],9]]],[1,1]]")))
(defn snail-add [a b]
(->> b
(into a)
(map (fn [[v nl]] [v (inc nl)]))
(into (clojure.lang.PersistentQueue/EMPTY))))
(defn magnitude [a]
(loop [stack [(peek a)]
[v nl] (peek (pop a))
queue (pop (pop a))]
;(prn stack v nl)
(if (and (empty? queue) (empty? stack))
v
(let [[pv pnl] (peek stack)]
(if (not= pnl nl)
(recur (conj stack [v nl]) (peek queue) (pop queue))
(recur (pop stack) [(+ (* 3 pv) (* 2 v)) (dec nl)] queue))))))
(def example1 "[1,1]
[2,2]
[3,3]
[4,4]
[5,5]
[6,6]")
(def larger-example "[[[0,[4,5]],[0,0]],[[[4,5],[2,6]],[9,5]]]
[7,[[[3,7],[4,3]],[[6,3],[8,8]]]]
[[2,[[0,8],[3,4]]],[[[6,7],1],[7,[1,6]]]]
[[[[2,4],7],[6,[0,5]]],[[[6,8],[2,8]],[[2,1],[4,5]]]]
[7,[5,[[3,8],[1,4]]]]
[[2,[2,2]],[8,[8,1]]]
[2,9]
[1,[[[9,3],9],[[9,0],[0,7]]]]
[[[5,[7,4]],7],1]
[[[[4,2],2],6],[8,7]]")
(def larger-example2 "[[[0,[5,8]],[[1,7],[9,6]]],[[4,[1,2]],[[1,4],2]]]
[[[5,[2,8]],4],[5,[[9,9],0]]]
[6,[[[6,2],[5,6]],[[7,6],[4,7]]]]
[[[6,[0,7]],[0,9]],[4,[9,[9,0]]]]
[[[7,[6,4]],[3,[1,3]]],[[[5,5],1],9]]
[[6,[[7,3],[3,2]]],[[[3,8],[5,7]],4]]
[[[[5,4],[7,7]],8],[[8,3],8]]
[[9,3],[[9,9],[6,[4,9]]]]
[[2,[[7,7],7]],[[5,8],[[9,3],[0,2]]]]
[[[[5,2],5],[8,[3,7]]],[[5,[7,5]],[4,4]]]")
(->> larger-example2
string/split-lines
(map (comp :queue parse))
(reduce (comp snail-reduce snail-add))
magnitude)
(time
(->> "/home/antbbn/aoc_2021/input_18.txt"
slurp
string/split-lines
(map (comp :queue parse))
(reduce (comp snail-reduce snail-add))
magnitude))
(defn get-candidates [coll]
(for [a coll
b coll
:when (not= a b)]
(magnitude (snail-reduce (snail-add a b)))))
(time
(->> "/home/antbbn/aoc_2021/input_18.txt"
slurp
string/split-lines
(map (comp :queue parse))
get-candidates
(apply max)))
(->> larger-example2
string/split-lines
(map (comp :queue parse))
get-candidates
(apply max))
(ns day19
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]))
(def scanners-input
(-> ;"/home/antbbn/aoc_2021/example_19.txt"
"/home/antbbn/aoc_2021/input_19.txt"
slurp
(string/split #"\n\n")))
(defn parse-scanner [single-scanner-input]
(let [[scanner-name & beacons] (string/split-lines single-scanner-input)]
[(Integer/parseInt (re-find #"\d+" scanner-name))
(mapv
(fn [beacon]
(into [] (map #(Integer/parseInt %)) (re-seq #"-?\d+" beacon)))
beacons)]))
(def scanners
(into {} (map parse-scanner scanners-input)))
(count scanners)
(defn change-coordinates [[[x0 y0 z0] [ox oy oz] rotation] [x' y' z']]
(let [[nx' ny' nz'] (case rotation
0 [x' y' z']
1 [x' z' y']
2 [y' x' z']
3 [y' z' x']
4 [z' x' y']
5 [z' y' x'])]
[(+ x0 (* ox nx'))
(+ y0 (* oy ny'))
(+ z0 (* oz nz'))]))
(defn possible-coordinate-systems [[x y z] [x' y' z'] rotation]
(for [ox [1 -1]
oy [1 -1]
oz [1 -1]]
[[(- x (* ox x'))
(- y (* oy y'))
(- z (* oz z'))]
[ox oy oz]
rotation]))
(defn possible-coordinate-systems-with-rotations [p [x' y' z']]
(mapcat #(possible-coordinate-systems p %1 %2)
[[x' y' z']
[x' z' y']
[y' x' z']
[y' z' x']
[z' x' y']
[z' y' x']]
(range 6)))
(defn num-overlapping-points [scanner candidate-scanner coordinate-system]
(count
(clojure.set/intersection
(set scanner)
(set (map #(change-coordinates coordinate-system %) candidate-scanner)))))
(defn check-point [point scanner candidate-scanner]
(mapcat (fn [candidate-point]
#_(not-empty)
(remove #(> 12 (num-overlapping-points scanner candidate-scanner %))
(possible-coordinate-systems-with-rotations point candidate-point)))
candidate-scanner))
(time
(def result2
(into []
(remove #(empty? (nth % 2))
(for [scanner (keys scanners)
other-scanner (keys scanners)
:when (not= scanner other-scanner)]
[scanner other-scanner
(first
(mapcat #(check-point % (scanners scanner) (scanners other-scanner))
(scanners scanner)))])))))
result1
(defn find-path [available reach visited]
#_(prn reach visited)
(cond (zero? reach) visited
((set (map second visited)) reach) '()
:else (remove empty? (map #(find-path available (first %) (conj visited %))
(filter #(= (second %) reach) available)))))
(defn get-one-path [paths]
(first
(filter vector?
(tree-seq (complement vector?) identity paths)))
)
(get-one-path (find-path result1 3 []))
(count
(into #{}
(mapcat (fn [[id beacons]]
(if (zero? id)
beacons
(reduce (fn [bs [_ _ cs]]
(map #(change-coordinates cs %) bs))
beacons
(get-one-path (find-path result1 id [])))))
scanners)))
(def scanner-locations
(into #{}
(map (fn [[id _]]
(if (zero? id)
[0 0 0]
(get-in (reduce (fn [[_ _ [p _ _]] [_ _ cs2]]
[_ _ [(change-coordinates cs2 p) _ _]])
(get-one-path (find-path result1 id [])))
[2 0])))
scanners)))
(defn manhattan [p1 p2]
(apply +
(map #(Math/abs (apply - (sort < [%1 %2]))) p1 p2)))
(apply max
(for [p1 scanner-locations
p2 scanner-locations]
(manhattan p1 p2)))(ns day19
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]))
(def scanners-input
(-> ;"/home/antbbn/aoc_2021/example_19.txt"
"/home/antbbn/aoc_2021/input_19.txt"
slurp
(string/split #"\n\n")))
(defn parse-scanner [single-scanner-input]
(let [[scanner-name & beacons] (string/split-lines single-scanner-input)]
[(Integer/parseInt (re-find #"\d+" scanner-name))
(mapv
(fn [beacon]
(into [] (map #(Integer/parseInt %)) (re-seq #"-?\d+" beacon)))
beacons)]))
(def scanners
(into {} (map parse-scanner scanners-input)))
(count scanners)
(defn change-coordinates [[[x0 y0 z0] [ox oy oz] rotation] [x' y' z']]
(let [[nx' ny' nz'] (case rotation
0 [x' y' z']
1 [x' z' y']
2 [y' x' z']
3 [y' z' x']
4 [z' x' y']
5 [z' y' x'])]
[(+ x0 (* ox nx'))
(+ y0 (* oy ny'))
(+ z0 (* oz nz'))]))
(defn possible-coordinate-systems [[x y z] [x' y' z'] rotation]
(for [ox [1 -1]
oy [1 -1]
oz [1 -1]]
[[(- x (* ox x'))
(- y (* oy y'))
(- z (* oz z'))]
[ox oy oz]
rotation]))
(defn possible-coordinate-systems-with-rotations [p [x' y' z']]
(mapcat #(possible-coordinate-systems p %1 %2)
[[x' y' z']
[x' z' y']
[y' x' z']
[y' z' x']
[z' x' y']
[z' y' x']]
(range 6)))
(defn num-overlapping-points [scanner candidate-scanner coordinate-system]
(count
(clojure.set/intersection
(set scanner)
(set (map #(change-coordinates coordinate-system %) candidate-scanner)))))
(defn check-point [point scanner candidate-scanner]
(mapcat (fn [candidate-point]
#_(not-empty)
(remove #(> 12 (num-overlapping-points scanner candidate-scanner %))
(possible-coordinate-systems-with-rotations point candidate-point)))
candidate-scanner))
(time
(def result2
(into []
(remove #(empty? (nth % 2))
(for [scanner (keys scanners)
other-scanner (keys scanners)
:when (not= scanner other-scanner)]
[scanner other-scanner
(first
(mapcat #(check-point % (scanners scanner) (scanners other-scanner))
(scanners scanner)))])))))
result1
(defn find-path [available reach visited]
#_(prn reach visited)
(cond (zero? reach) visited
((set (map second visited)) reach) '()
:else (remove empty? (map #(find-path available (first %) (conj visited %))
(filter #(= (second %) reach) available)))))
(defn get-one-path [paths]
(first
(filter vector?
(tree-seq (complement vector?) identity paths)))
)
(get-one-path (find-path result1 3 []))
(count
(into #{}
(mapcat (fn [[id beacons]]
(if (zero? id)
beacons
(reduce (fn [bs [_ _ cs]]
(map #(change-coordinates cs %) bs))
beacons
(get-one-path (find-path result1 id [])))))
scanners)))
(def scanner-locations
(into #{}
(map (fn [[id _]]
(if (zero? id)
[0 0 0]
(get-in (reduce (fn [[_ _ [p _ _]] [_ _ cs2]]
[_ _ [(change-coordinates cs2 p) _ _]])
(get-one-path (find-path result1 id [])))
[2 0])))
scanners)))
(defn manhattan [p1 p2]
(apply +
(map #(Math/abs (apply - (sort < [%1 %2]))) p1 p2)))
(apply max
(for [p1 scanner-locations
p2 scanner-locations]
(manhattan p1 p2)))
(ns day20
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]))
(def example "..#.#..#####.#.#.#.###.##.....###.##.#..###.####..#####..#....#..#..##..###..######.###...####..#..#####..##..#.#####...##.#.#..#.##..#.#......#.###.######.###.####...#.##.##..#..#..#####.....#.#....###..#.##......#.....#..#..#..##..#...##.######.####.####.#.#...#.......#..#.#.#...####.##.#......#..#...##.#.##..#...##.#.##..###.#......#.#.......#.#.#.####.###.##...#.....####.#..#..#.##.#....##..#.####....##...##..#...#......#.#.......#.......##..####..#...#.#.#...##..#.#..###..#####........#..####......#..#
#..#.
#....
##..#
..#..
..###")
(def bright->bin {\. 0 \# 1})
(defn bin->int [coll]
(reduce #(+ (bit-shift-left %1 1) %2) 0 coll))
(def input
(slurp "/home/antbbn/aoc_2021/input_20.txt"))
(defn parse [input]
(let [[algo image]
(-> input
(string/split #"\n\n"))]
{:algorithm (mapv bright->bin algo)
:image (mapv #(mapv bright->bin %) (string/split-lines image))}))
(defn add-border [b image]
(let [width (count (peek image))
top-bottom-border (into [] (repeat 2 (into [] (repeat width b))))]
(mapv #(apply conj (into [b b] %) [b b])
(apply conj (into top-bottom-border image) top-bottom-border))))
(defn get-neighbors [[y x]]
(for [dy [-1 0 1]
dx [-1 0 1]]
[(+ y dy) (+ x dx)]))
(defn process [algorithm image]
(let [width (count (peek image))
height (count image)
border (get-in image [0 0])]
(add-border (algorithm ([0 511] border))
(into []
(for [y (range 1 (dec height))]
(into []
(for [x (range 1 (dec width))]
(->> [y x]
get-neighbors
(map #(get-in image %))
bin->int
algorithm))))))))
(defn count-lit [image]
(reduce (fn [tot row]
(+ tot (reduce + row)))
0
image))
(time
(let [{:keys [algorithm image]} (parse input)]
(->> image
(add-border 0)
(iterate (partial process algorithm))
(drop 50)
first
count-lit)))
(ns day21
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]))
(def example-initial
{:active {:score 0 :pos 4}
:waiting {:score 0 :pos 8}
:turns 0})
(def input-initial
{:active {:score 0 :pos 8}
:waiting {:score 0 :pos 9}
:turns 0})
(defn turn [{:keys [score pos]} roll]
(let [new-pos (inc (mod (dec (+ pos roll)) 10))]
{:score (+ score new-pos)
:pos new-pos}))
(def full-game
(reduce (fn [{:keys [active waiting turns]} roll]
(let [played (turn active roll)]
(if (<= 1000 (:score played))
(reduced {:winner played :loser waiting :turns (inc turns)})
{:active waiting :waiting played :turns (inc turns)})))
input-initial
#_example-initial
(map #(apply + %) (partition 3 (cycle (range 1 101))))))
(def dirac-roll-universes
(for [a [1 2 3]
b [1 2 3]
c [1 2 3]]
(+ a b c)))
(def dirac-roll-freq
(frequencies dirac-roll-universes))
(defn possible-rolls [[player outer-freq]]
(map (fn [[roll freq]]
[(turn player roll) (* freq outer-freq)])
dirac-roll-freq))
(defn possible-rolls [[player outer-freq]]
(into {}
(map (fn [[roll freq]]
[(turn player roll) (* freq outer-freq)])
dirac-roll-freq)))
(def example
(possible-rolls [{:score 0 :pos 4} 1]))
(def example2
(apply merge-with + (map possible-rolls example)))
(map possible-rolls example)
(time
(do (apply merge-with + (map possible-rolls example2))
nil))
(time
(do
(merge-add-vec (mapcat possible-rolls example2))
nil))
(defn merge-add-vec [coll]
(reduce #(update %1 (first %2) (fnil + 0) (second %2))
{}
coll))
(reduce-kv)
; part 2
(defn check [player-states]
(loop [states player-states wins []]
(let [new-states (merge-add-vec (mapcat possible-rolls states))
{winning-states true other-states false}
(group-by #(<= 21 (:score (first %))) new-states)
turn-wins (apply + (map second winning-states))
turn-others (apply + (map second other-states)) ]
(prn (count new-states))
(if (empty? other-states)
(conj wins [turn-wins turn-others])
(recur other-states (conj wins [turn-wins turn-others]))))))
(def player1 {:score 0 :pos 4})
(def player2 {:score 0 :pos 8})
(def player1 {:score 0 :pos 8})
(def player2 {:score 0 :pos 9})
(ns day22
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]))
(def example "on x=-20..26,y=-36..17,z=-47..7
on x=-20..33,y=-21..23,z=-26..28
on x=-22..28,y=-29..23,z=-38..16
on x=-46..7,y=-6..46,z=-50..-1
on x=-49..1,y=-3..46,z=-24..28
on x=2..47,y=-22..22,z=-23..27
on x=-27..23,y=-28..26,z=-21..29
on x=-39..5,y=-6..47,z=-3..44
on x=-30..21,y=-8..43,z=-13..34
on x=-22..26,y=-27..20,z=-29..19
off x=-48..-32,y=26..41,z=-47..-37
on x=-12..35,y=6..50,z=-50..-2
off x=-48..-32,y=-32..-16,z=-15..-5
on x=-18..26,y=-33..15,z=-7..46
off x=-40..-22,y=-38..-28,z=23..41
on x=-16..35,y=-41..10,z=-47..6
off x=-32..-23,y=11..30,z=-14..3
on x=-49..-5,y=-3..45,z=-29..18
off x=18..30,y=-20..-8,z=-3..13
on x=-41..9,y=-7..43,z=-33..15
on x=-54112..-39298,y=-85059..-49293,z=-27449..7877
on x=967..23432,y=45373..81175,z=27513..53682")
(def example-long (slurp "/home/antbbn/aoc_2021/example_22.txt"))
(def input (slurp "/home/antbbn/aoc_2021/input_22.txt"))
(defn parse-instruction [s]
(let [[_ switch intervals] (re-matches #"(on|off) (.*)" s)]
[(keyword switch)
(->> intervals
(re-seq #"-?\d+")
(map #(Integer/parseInt %))
(partition 2))]))
(defn is-init-instruction? [[_ intervals]]
(every? #(<= -50 % 50) (apply concat intervals)))
;; part 1 dumb version
(defn range* [[a b]]
(range a (inc b)))
(defn init-grid [instructions]
(reduce (fn [grid [switch [xrange yrange zrange]]]
(let [op (case switch
:on conj
:off disj)]
(apply op grid
(for [x (range* xrange)
y (range* yrange)
z (range* zrange)]
[x y z]))))
#{}
instructions))
(->> input
string/split-lines
(map parse-instruction)
(filter is-init-instruction?)
init-grid
count)
;; part 2
(defn cuboid-size [[[x1 x2] [y1 y2] [z1 z2]]]
(* (- x2 x1 -1) (- y2 y1 -1) (- z2 z1 -1)))
(cuboid-size [[0 2] [0 2] [0 2]])
;; a and b
;; c and d
;; a -- c -- b -- d -> c b
;; c -- a -- d -- b -> a d
;; a -- c -- d -- b -> c d
;; c -- a -- b -- d -> a b
;; max a c, min b d
(defn cuboid-overlap [c1 c2]
(let [overlap
(map (fn [[a b] [c d]]
[(max a c) (min b d)]) c1 c2)]
(when (every? #(apply <= %) overlap)
overlap)))
(time
(cuboid-overlap [[0 2] [0 2] [0 2]]
[[-10 -5] [1 3] [1 3]]))
(time
(cuboid-overlap [[0 2] [0 2] [0 2]]
[[1 3] [1 3] [1 3]]))
(defn flipped-sign [n]
(cond
(< 0 n) -1
;; (= 0 n) 0
:else 1))
(time
(flipped-sign 1))
(defn get-overlaps [cuboid cuboids]
(keep (fn [old-cuboid]
(when-let [overlap-coords (cuboid-overlap (:coords cuboid)
(:coords old-cuboid))]
{:coords overlap-coords
:size (* (flipped-sign (:size old-cuboid))
(cuboid-size overlap-coords))}))
cuboids))
(defn process-instructions [instructions]
(reduce (fn [cuboids [switch coords]]
(let [cuboid {:coords coords :size (cuboid-size coords)}
with-overlaps (get-overlaps cuboid cuboids)]
(prn (count cuboids))
(case switch
:on (conj with-overlaps cuboid)
:off with-overlaps)))
[]
instructions))
(defn get-overlaps-f [cuboid]
(keep (fn [old-cuboid]
(when-let [overlap-coords (cuboid-overlap (:coords cuboid)
(:coords old-cuboid))]
{:coords overlap-coords
:size (* (flipped-sign (:size old-cuboid))
(cuboid-size overlap-coords))}))))
(defn process-instructions [instructions]
(reduce (fn [cuboids [switch coords]]
(let [cuboid {:coords coords :size (cuboid-size coords)}
with-overlaps (into cuboids (get-overlaps-f cuboid) cuboids)]
#_(prn (count cuboids))
(case switch
:on (conj with-overlaps cuboid)
:off with-overlaps)))
[]
instructions))
(time
(->> input
string/split-lines
(map parse-instruction)
#_(filter is-init-instruction?) ;; only for part1
process-instructions
;;count
(map :size)
(reduce +)))
(def favorite-rooms {:amber 2
:bronze 4
:copper 6
:desert 8})
(def favorite-pods {2 :amber
4 :bronze
6 :copper
8 :desert})
(def energy-multiplier {:amber 1
:bronze 10
:copper 100
:desert 1000})
(def example-burrow
[:empty
:empty
[:bronze :amber]
:empty
[:copper :desert]
:empty
[:bronze :copper]
:empty
[:desert :amber]
:empty
:empty])
(def example-burrow-large
[:empty
:empty
[:bronze :desert :desert :amber]
:empty
[:copper :copper :bronze :desert]
:empty
[:bronze :bronze :amber :copper]
:empty
[:desert :amber :copper :amber]
:empty
:empty])
(def input-burrow
[:empty
:empty
[:bronze :bronze]
:empty
[:amber :copper]
:empty
[:amber :desert]
:empty
[:desert :copper]
:empty
:empty])
(def input-burrow-large
[:empty
:empty
[:bronze :desert :desert :bronze]
:empty
[:amber :copper :bronze :copper]
:empty
[:amber :bronze :amber :desert]
:empty
[:desert :amber :copper :copper]
:empty
:empty])
(def finish
[:empty
:empty
[:amber :amber]
:empty
[:bronze :bronze]
:empty
[:copper :copper]
:empty
[:desert :desert]
:empty
:empty])
(def finish-large
[:empty
:empty
[:amber :amber :amber :amber]
:empty
[:bronze :bronze :bronze :bronze]
:empty
[:copper :copper :copper :copper]
:empty
[:desert :desert :desert :desert]
:empty
:empty])
(defn possible-hallway-moves [state from]
(let [left (drop-while #(<= from %) [10 9 7 5 3 1 0])
right (drop-while #(<= % from) [0 1 3 5 7 9 10])]
(concat (take-while #(= :empty (state %)) left)
(take-while #(= :empty (state %)) right))))
(possible-hallway-moves example-burrow 3)
(example-burrow 2)
(defn move-steps [hallway room room-pos]
(cond
(< hallway room) (+ (- room hallway) (inc room-pos))
:else (+ (- hallway room) (inc room-pos))))
(defn move-energy [pod hallway room room-pos]
(* (energy-multiplier pod)
(move-steps hallway room room-pos)))
(move-energy :desert 0 2 1)
(defn get-movable [idx [v & vals] favorite]
(assert (keyword? favorite))
(cond
(nil? v) nil
(= :empty v) (recur (inc idx) vals favorite)
(and (seq? vals) (every? #(= favorite v %) vals)) nil
(and (nil? vals) (= v favorite)) nil
:else [idx v]))
(get-movable 0 [:desert :desert :desert :desert] :copper)
(get-movable 0 [:empty :empty :copper :desert] :desert)
(get-movable 0 (finish 2) :amber)
(get-movable 0 [:desert :amber] :amber)
(get-movable 0 [:copper :desert] :desert)
(defn move [state hallway hallway-val room room-pos room-val]
#_(assert (<= 0 hallway))
#_(assert (<= hallway 10))
#_(assert (<= 0 room-pos) (str state "," hallway "," hallway-val "," room "," room-pos "," room-val))
#_(assert (<= room-pos 1) (str state "," hallway "," hallway-val "," room "," room-pos "," room-val))
(-> state
(assoc-in [room room-pos] room-val)
(assoc hallway hallway-val)))
(get-movable 0 [:empty :empty] (favorite-pods 4))
(defn possible-room->hallway-moves [state room]
(if-let [[room-pos pod] (get-movable 0 (state room) (favorite-pods room))]
(do
(assert (not= :empty pod))
(map #(vector
(move state % pod room room-pos :empty)
(move-energy pod % room room-pos))
(possible-hallway-moves state room)))
[]))
(possible-room->hallway-moves [:empty
:empty
[:empty :desert]
:empty
[:copper :desert]
:empty
[:bronze :copper]
:empty
[:empty :amber]
:empty
:desert]
2)
(defn get-empty-spot [idx [v & vals] favorite]
(cond
(nil? v) (dec idx)
(= :empty v) (recur (inc idx) vals favorite)
(and (seq vals) (every? #(= favorite v %) vals)) (dec idx)
(and (nil? vals) (= favorite v)) (dec idx)
:else nil))
(get-empty-spot 0 [:amber :copper] :copper)
(get-empty-spot 0 [:desert :amber] :desert)
(defn can-reach [state hallway room]
(if (< hallway room)
(every? #(or (= :empty %) (vector? %))
(subvec state (inc hallway) (inc room)))
(every? #(or (= :empty %) (vector? %)) (subvec state room hallway))))
(defn possible-hallway->room-move [state hallway]
(let [pod (state hallway)
room (favorite-rooms pod)]
(if (can-reach state hallway room)
(if-let [room-pos (get-empty-spot 0 (state room) pod)]
[[(move state hallway :empty room room-pos pod)
(move-energy pod hallway room room-pos)]]
[])
[])))
(can-reach [:empty
:empty
[:copper :amber]
:empty
[:empty :desert]
:something
[:bronze :copper]
:empty
[:desert :amber]
:something
:something] 0 2)
(possible-hallway-moves [:amber
:empty
[:copper :amber]
:empty
[:empty :desert]
:empty
[:bronze :copper]
:empty
[:desert :amber]
:amber
:empty] 9)
(defn possible-next-states [state energy]
(mapcat (fn [pos]
(map #(update % 1 + energy)
(cond
(= :empty (state pos)) []
(keyword? (state pos)) (possible-hallway->room-move state pos)
:else (possible-room->hallway-moves state pos))))
(range 11)))
(possible-next-states finish 1)
(def min-energy (atom ##Inf))
@min-energy
(defn well [state finish energy]
(loop [stack [[state energy]]]
(if (empty? stack)
@min-energy
(if (<= @min-energy energy)
(recur (pop stack))
(let [[state energy] (peek stack)
next-states (possible-next-states state energy)]
(if (empty? next-states)
(do
(when (= state finish) (swap! min-energy min energy))
(recur (pop stack)))
(recur (into (pop stack) next-states))))))))
(well example-burrow finish 0)
(well input-burrow finish 0)
@min-energy
(reset! min-energy ##Inf)
(well example-burrow-large finish-large 0)
@min-energy
(reset! min-energy ##Inf)
(well input-burrow-large finish-large 0)
@min-energy
(ns day24
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]))
(def monad-input (slurp "/home/antbbn/aoc_2021/input_24.txt"))
(defn run [stop input init-state program]
(reduce
(fn [[stop mem input] [op reg val]]
(if (zero? stop)
(reduced mem)
(if (nil? val)
[(dec stop) (assoc mem reg (Integer/parseInt (first input))) (rest input)]
(let [val (try (Integer/parseInt val)
(catch NumberFormatException _ (mem val)))]
[stop (case op
"add" (update mem reg + val)
"mul" (update mem reg * val)
"div" (update mem reg #(int (Math/floor (/ %1 %2))) val)
"mod" (update mem reg rem val)
"eql" (update mem reg #({true 1 false 0} (= %1 %2)) val))
input]))))
[stop
init-state
input]
program))
(def monad-program
(->> monad-input
string/split-lines
(map #(string/split % #" "))))
(run 15 ["9" "9" "1" "9" "6" "9" "9" "7" "9" "8" "5" "9" "4" "2" "-1"]
{"w" 0 "x" 0 "y" 0 "z" 0} monad-program)
(run 15 ["8" "4" "1" "9" "1" "5" "2" "1" "3" "1" "1" "6" "1" "1" "-1"]
{"w" 0 "x" 0 "y" 0 "z" 0} monad-program)
(defn get-max-vals [[digit1-pos digit-offset] mod-offset digit2-pos]
(loop [digit1 9]
(let [digit2 (+ digit1 digit-offset mod-offset)]
(if (<= 1 digit2 9)
[[digit1-pos digit1] [digit2-pos digit2]]
(recur (dec digit1))))))
(defn get-min-vals [[digit1-pos digit-offset] mod-offset digit2-pos]
(loop [digit1 1]
(let [digit2 (+ digit1 digit-offset mod-offset)]
(if (<= 1 digit2 9)
[[digit1-pos digit1] [digit2-pos digit2]]
(recur (inc digit1))))))
(defn find [f program]
(->> program
(partition 18)
(reduce (fn [[stack result digit] routine]
(let [[_ _ mod-offset-in] (nth routine 5)
mod-offset (Integer/parseInt mod-offset-in)
[_ _ digit-offset-in] (nth routine 15)
digit-offset (Integer/parseInt digit-offset-in)]
(if (< 0 mod-offset)
[(conj stack [digit digit-offset]) result (inc digit)]
[(pop stack)
(apply conj result (f (peek stack) mod-offset digit))
(inc digit)])))
[[] [] 1])
second
sort
(map second)
string/join))
(find get-max-vals monad-program)
(find get-min-vals monad-program)
(ns day25
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]))
(def example "v...>>.vv>
.vv>>.vv..
>>.>v>...v
>>v>>.>.v.
v>v.vv.v..
>.>>..v...
.vv..>.>v.
v.v..>>v.v
....v..v.>")
(def input (slurp "/home/antbbn/aoc_2021/input_25.txt"))
(defn parse [input]
(update
(reduce (fn [[sea-floor x y] cucumber]
(case cucumber
\newline [sea-floor 0 (inc y)]
[(update sea-floor cucumber conj [x y]) (inc x) y]))
[{\> #{} \v #{} \. #{}} 0 0]
(string/trim input))
2 inc))
(defn in-front-> [max-x _ [x y]]
[(mod (inc x) max-x) y])
(defn in-front-v [_ max-y [x y]]
[x (mod (inc y) max-y)])
(defn get-moves [sea-floor max-x max-y type ]
(let [move (case type
\> (partial in-front-> max-x max-y)
\v (partial in-front-v max-x max-y))]
(keep (fn [from]
(when-let [to ((sea-floor \.) (move from))]
[from to]))
(sea-floor type))
))
(defn step-one-type [sea-floor max-x max-y type]
(reduce (fn [sea-floor [from to]]
(-> sea-floor
(update type disj from)
(update type conj to)
(update \. disj to)
(update \. conj from)))
sea-floor
(get-moves sea-floor max-x max-y type)
))
(let [[sea-floor max-x max-y] (parse input)]
(loop [sea-floor sea-floor
count 1]
(let [new-sea-floor
(-> sea-floor
(step-one-type max-x max-y \>)
(step-one-type max-x max-y \v))]
(if (= new-sea-floor sea-floor)
count
(recur new-sea-floor (inc count)))))
)
(defn print-grid [sea-floor max-x max-y]
(string/join \newline
(for [y (range max-y)]
(string/join
(for [x (range max-x)]
(cond
((sea-floor \>) [x y]) \>
((sea-floor \v) [x y]) \v
:else \.))))))
(ns day3
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]
[clojure.test :refer [is are]]
[clojure.edn :as edn]))
(def small (->> "00100
11110
10110
10111
10101
01111
00111
11100
10000
11001
00010
01010"
string/split-lines))
(def large (->> "/home/antbbn/aoc_2021/input_3.txt"
slurp
string/split-lines))
(defn rate [key-compare input]
(->> input
(apply map vector)
(map frequencies)
(map #(key (apply key-compare val %)))
string/join))
(def gamma (rate max-key large))
(def epsilon (rate min-key large))
(* (Integer/parseInt gamma 2) (Integer/parseInt epsilon 2))
; part 2
(defn rate [comparison input]
(loop [pos 0
input input]
(if (= 1 (count input))
(first input)
(let [{zeros \0 ones \1} (group-by #(nth % pos) input)]
(if (comparison (count zeros) (count ones))
(recur (inc pos) ones)
(recur (inc pos) zeros))))))
(def oxygen (rate <= large))
(def co2 (rate > large))
(* (Integer/parseInt oxygen 2) (Integer/parseInt co2 2))
(ns day4
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.set :refer [difference intersection union]]))
(def small "7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1
22 13 17 11 0
8 2 23 4 24
21 9 14 16 7
6 10 3 18 5
1 12 20 15 19
3 15 0 2 22
9 18 13 17 5
19 8 7 25 23
20 11 10 24 4
14 21 16 12 6
14 21 17 24 4
10 16 15 9 19
18 8 23 26 20
22 11 13 6 5
2 0 12 3 7")
(def large (slurp "/home/antbbn/aoc_2021/input_4.txt"))
(defn split-input [input]
(-> input
(string/split #"\n\n")))
(def bingo-sequence
(->> large;small
split-input
first
(re-seq #"\d+")))
(defn get-cols [card]
(apply map vector card))
(defn parse-card [s]
(let [ls (string/split-lines s)
rows (map #(re-seq #"\d+" %) ls)]
(map set (concat rows (get-cols rows)))))
(def bingo-cards
(->> large;small
split-input
rest
(map #(parse-card %))))
(defn mark-card [card draw]
(map #(difference % #{draw}) card))
(defn mark-all-cards [cards draw]
(map #(mark-card % draw) cards))
(defn winning-card? [card]
(not-empty
(filter empty? card)))
(defn calc-winner-points [winner draw]
(* (Integer/parseInt draw)
(reduce +
(map #(Integer/parseInt %) (apply union winner)))))
(defn calc-points [winners draw]
(map #(calc-winner-points % draw) winners))
(def game
(reduce
(fn [{:keys [winners cards]} draw]
(let [marked-cards (mark-all-cards cards draw)]
{:winners (conj winners
(calc-points (filter winning-card? marked-cards) draw))
:cards (remove winning-card? marked-cards)}))
{:winners [] :cards bingo-cards}
bingo-sequence))
; part 1
(first (:winners game))
; part 2
(last (:winners game))
(ns day5
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]))
(def small "0,9 -> 5,9
8,0 -> 0,8
9,4 -> 3,4
2,2 -> 2,1
7,0 -> 7,4
6,4 -> 2,0
0,9 -> 2,9
3,4 -> 1,4
0,0 -> 8,8
5,5 -> 8,2")
(def large (slurp "/home/antbbn/aoc_2021/input_5.txt"))
(defn parse [input]
(->> input
string/split-lines
(map #(re-matches #"(\d+),(\d+) -> (\d+),(\d+)" %))
(map (fn [match]
(partition 2
(map #(Integer/parseInt %) (rest match)))))))
(defn range* [a b]
(cond
(< a b) (range a (inc b))
(> a b) (range a (dec b) -1)
:else (repeat a)))
(defn expand [[[x1 y1] [x2 y2]]]
(map vector (range* x1 x2) (range* y1 y2))
)
; part 1
(->> large
parse
(filter (fn [[[x1 y1] [x2 y2]]]
(or (= x1 x2) (= y1 y2))))
(map expand)
(apply concat)
frequencies
(filter #(< 1 (val % )))
count)
; part 2
(->> large
parse
(map expand)
(apply concat)
frequencies
(filter #(< 1 (val %)))
count)
(ns day6
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]))
(def small "3,4,3,1,2")
(def large (slurp "/home/antbbn/aoc_2021/input_6.txt"))
(defn parse [input]
(as-> input _
(re-seq #"\d+" _)
(map #(Integer/parseInt %) _)
(frequencies _)
(mapv #(get _ % 0) (range 9))))
(defn step [fishes]
(conj (update (subvec fishes 1) 6 + (first fishes))
(first fishes)))
(reduce + (nth (iterate step (parse small)) 80))
(reduce + (nth (iterate step (parse large)) 80))
(reduce + (nth (iterate step (parse small)) 256))
(reduce + (nth (iterate step (parse large)) 256))
(ns day7
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]
[clojure.test :refer [is are]]
[clojure.edn :as edn]
[clojure.set :refer [difference intersection union]]))
(def large (slurp "/home/antbbn/aoc_2021/input_7.txt"))
(defn parse [input]
(map #(Integer/parseInt %)
(-> input
string/trim-newline
(string/split #","))))
; found the median property on the wikipedia page for Geometric Median
; @temistoklik showed me how the average could solve part 2
(defn median [l]
(let [len (count l)
middle (/ len 2)
sorted (sort l)]
[(nth sorted (dec middle)) (nth sorted middle)]))
(defn avg* [crabs]
(/ (apply + crabs) (count crabs)))
(defn avg [crabs]
(let [res (avg* crabs)]
[(int res) (inc (int res))]))
; type hints make the brute force quite fast :)
(defn cost-1 [^long pos ^long crab]
(Math/abs (- pos crab)))
(defn cost-2 [^long pos ^long crab]
(let [dist (Math/abs (- pos crab))]
(/ (* dist (inc dist)) 2)))
(defn min-cost [crabs cost candidates]
(apply min
(map (fn [pos]
(reduce + (map #(cost pos %) crabs)))
candidates)))
(def crabs (parse large))
(min-cost crabs cost-1 (median crabs))
(min-cost crabs cost-2 (avg crabs))
; original solution brute force :)
(defn brute-min-cost [crabs cost]
(let [far (apply max crabs)]
(apply min
(map (fn [pos]
(reduce +
(map #(cost pos %) crabs)))
(range (inc far))))))
(time (brute-min-cost crabs cost-1))
(brute-min-cost crabs cost-2)
(ns day8
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]
[clojure.test :refer [is are]]
[clojure.edn :as edn]
[clojure.set :refer [difference intersection union]]))
(def small "be cfbegad cbdgef fgaecd cgeb fdcge agebfd fecdb fabcd edb | fdgacbe cefdb cefbgd gcbe
edbfga begcd cbg gc gcadebf fbgde acbgfd abcde gfcbed gfec | fcgedb cgb dgebacf gc
fgaebd cg bdaec gdafb agbcfd gdcbef bgcad gfac gcb cdgabef | cg cg fdcagb cbg
fbegcd cbd adcefb dageb afcb bc aefdc ecdab fgdeca fcdbega | efabcd cedba gadfec cb
aecbfdg fbg gf bafeg dbefa fcge gcbea fcaegb dgceab fcbdga | gecf egdcabf bgf bfgea
fgeab ca afcebg bdacfeg cfaedg gcfdb baec bfadeg bafgc acf | gebdcfa ecba ca fadegcb
dbcfg fgd bdegcaf fgec aegbdf ecdfab fbedc dacgb gdcebf gf | cefg dcbef fcge gbcadfe
bdfegc cbegaf gecbf dfcage bdacg ed bedf ced adcbefg gebcd | ed bcgafe cdgba cbgef
egadfb cdbfeg cegd fecab cgb gbdefca cg fgcdab egfdb bfceg | gbdfcae bgc cg cgb
gcafb gcf dcaebfg ecagb gf abcdeg gaef cafbge fdbac fegbdc | fgae cfgab fg bagce")
(def input-small
(->> small
string/split-lines
(map #(string/split % #" \| "))
(map
(fn [[signals digits]]
[(string/split signals #" ") (string/split digits #" ")]))))
input-small
(def input
(->> "/home/antbbn/aoc_2021/input_8.txt"
slurp
#_small
string/split-lines
(map #(string/split % #" \| "))
(map
(fn [[signals digits]]
[(string/split signals #" ") (string/split digits #" ")]))))
;part1
(->> input
first
second
(map count)
(filter #{1 4 7 8})
count
(+ 1))
;; :top
;; :top dddd
;; e a
;; :top-left e a :top-right
;; :middle ffff
;; g b
;; :bottom-left g b :bottom-right
;; cccc
;; :bottom
(def digits->segments
{0 #{:top :top-left :top-right :bottom-left :bottom-right :bottom}
1 #{:top-right :bottom-right}
2 #{:top :top-right :middle :bottom-left :bottom}
3 #{:top :top-right :middle :bottom-right :bottom}
4 #{:top-left :top-right :middle :bottom-right}
5 #{:top :top-left :middle :bottom-right :bottom}
6 #{:top :top-left :middle :bottom-left :bottom-right :bottom}
7 #{:top :top-right :bottom-right}
8 #{:top :top-left :top-right :middle :bottom-left :bottom-right :bottom}
9 #{:top :top-left :top-right :middle :bottom-right :bottom}})
(def segments->digits (into {} (map (fn [[k v]] [v k]) digits->segments)))
(def segment-frequency
(frequencies (mapcat val digits->segments)))
(def freq->possible-segments
(reduce-kv (fn [m segment freq]
(update m freq (fnil conj #{}) segment))
{}
segment-frequency))
(defn ambiguity-fn [signals]
(let [[four-signal] (filter #(= 4 (count %)) signals)]
(fn [letter possible-segments]
(cond
(possible-segments :top )
(if (string/includes? four-signal (str letter))
:top-right
:top)
(possible-segments :middle)
(if (string/includes? four-signal (str letter))
:middle
:bottom)
:else (first possible-segments)))))
(defn get-letter->segment [test-signals]
(let [solve-ambig (ambiguity-fn test-signals)]
(->> test-signals
string/join
frequencies
(map (fn [[letter freq]]
[letter (freq->possible-segments freq)]))
(map (fn [[letter segments]]
[letter (solve-ambig letter segments)]))
(into {}))))
(defn get-digit [signal letter->segment]
(->> signal
(map letter->segment)
(into #{})
segments->digits))
(defn get-number [[test-signals signals]]
(let [letter->segment (get-letter->segment test-signals)]
(reduce #(+ (* 10 %1) %2)
(map #(get-digit % letter->segment) signals))))
(reduce + (map get-number input))
(ns day9
(:require [clojure.repl :refer [source apropos dir pst doc find-doc]]
[clojure.string :as string]
[clojure.test :refer [is are]]
[clojure.edn :as edn]
[clojure.set :refer [difference intersection union]]))
(def small
"2199943210
3987894921
9856789892
8767896789
9899965678")
(def large (slurp "/home/antbbn/aoc_2021/input_9.txt"))
(defn parse [input]
(->> input
string/split-lines
(mapv (fn [row]
(mapv #(Integer/parseInt (str %)) row)))))
(parse small)
(def heightmap-small (parse small))
(def heightmap-large (parse large))
; part 1
(def neighbors [[0 1] [0 -1] [1 0] [-1 0]])
(defn get-neighbor-pos [[y x]]
(map (fn [[step-x step-y]]
[(+ y step-y) (+ x step-x)])
neighbors))
(defn get-locations [heightmap]
(for [x (range (count (first heightmap)))
y (range (count heightmap))]
[y x]))
(defn get-risk-level [heightmap point]
(let [height (get-in heightmap point)
neighbors (map #(get-in heightmap %) (get-neighbor-pos point))
lowest-neighbor (apply min (remove nil? neighbors))]
(if (< height lowest-neighbor)
(+ height 1)
0)))
(reduce +
(map #(get-risk-level heightmap-small %)
(get-locations heightmap-small)))
(reduce +
(map #(get-risk-level heightmap-large %)
(get-locations heightmap-large)))
; part 2
(defn low-point? [heightmap point]
(let [height (get-in heightmap point)
neighbors (map #(get-in heightmap %) (get-neighbor-pos point))
lowest-neighbor (apply min (remove nil? neighbors))]
(< height lowest-neighbor)))
(defn get-basin-neighbors [heightmap point]
(let [height (get-in heightmap point)
neighbors (get-neighbor-pos point)]
(filter (fn [neighbor]
(let [neighbor-height (get-in heightmap neighbor)]
(when-not (or (nil? neighbor-height)
(= 9 neighbor-height)
(> height neighbor-height))
neighbor)))
neighbors)))
; this doesn't work, gives stackoverflow
(defn get-basin [heightmap points]
(into #{}
(mapcat (fn [point]
(let [basin-neighbors (get-basin-neighbors heightmap point)]
(if (empty? basin-neighbors)
[point]
(conj (get-basin heightmap basin-neighbors) point))))
points)))
(defn get-basin-new [heightmap points]
(loop [ret #{} points points]
(if (empty? points)
ret
(let [cur-point (first points)
basin-neighbors (remove ret (get-basin-neighbors heightmap cur-point))]
(recur (conj ret cur-point) (concat (rest points) basin-neighbors))))))
(->> heightmap-small
get-locations
(filter (partial low-point? heightmap-small))
(map #(count (get-basin heightmap-small [%])))
(sort >)
(take 3)
(reduce *))
(->> heightmap-small
get-locations
(filter (partial low-point? heightmap-small))
(map #(count (get-basin-new heightmap-small [%])))
(sort >)
(take 3)
(reduce *))
(->> heightmap-large
get-locations
(filter (partial low-point? heightmap-large))
(map #(count (get-basin-new heightmap-large [%])))
(sort >)
(take 3)
(reduce *)
)
@themistoklik
Copy link

(require ['clojure.string :as 'str])


(def input 
  (->>
    "input.txt"
    slurp
    str/split-lines
    (map #(apply list %))))

(defn transpose
  [l]
  (apply map list l))


(defn pick-oxygen-bit
  [m]
  (if (>= (m \1) (m \0)) \1 \0))

(defn pick-scrubber-bit
  [m]
  (if (<= (m \0) (m \1)) \0 \1))

(defn frequent-bit-in-row
  [r index pick]
  (->>
  r
  transpose
  (#(nth % index))
  frequencies
  pick))

; part 1
(->>
  input
  transpose
  (map frequencies)
  (map #(vector (apply max-key val %) (apply min-key val %)))
  (map #(map first %))
  transpose
  (map str/join)
  (map #(Integer/parseInt % 2))
  (reduce * 1)
  println)

; part2
(defn part2
  [input picker]
  (loop [numbers input
       index 0]
  (let [bit (frequent-bit-in-row numbers index picker)
        keepers (filter #(= bit (nth % index)) numbers)]
  
  (if (= 1 (count keepers))
    ;; (Integer/parseInt (str/join keepers))
    (Integer/parseInt (str/join (first keepers)) 2)
    (recur keepers (inc index))))))


(println (* (part2 input pick-oxygen-bit) (part2 input pick-scrubber-bit)))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment