Last active
December 22, 2020 17:26
-
-
Save zengxinhui/692fad81db75a073716d3527aac68863 to your computer and use it in GitHub Desktop.
Advent of Code 2020
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 y2020.core | |
(:require [clojure.edn :as edn] | |
[clojure.string :as str])) | |
;; 202001 | |
(let [input (->> (slurp "src/y2020/input202001") (re-seq #"\d+") (map edn/read-string) sort) | |
f (fn [sum xs] | |
(loop [[x & morex :as x'] xs | |
[y & morey :as y'] (reverse xs)] | |
(when (and x morex) | |
(cond | |
(= sum (+ x y)) (* x y) | |
(< sum (+ x y)) (recur x' morey) | |
:else (recur morex y')))))] | |
[(f 2020 input) | |
(loop [[a & more] input] | |
(if-let [ans (f (- 2020 a) more)] | |
(* a ans) | |
(recur more)))]) | |
;; [793524 61515678] | |
;; 202002 | |
(let [input (->> (slurp "src/y2020/input202002") (re-seq #"[\d\w]+") (partition 4)) | |
f (fn [[a b c s]] | |
(let [[a b] (map edn/read-string [a b])] | |
(if (<= a (count (filter (set c) s)) b) true))) | |
g (fn [[a b c s]] | |
(let [[a b] (map #(dec (edn/read-string %)) [a b]) | |
c (first c)] | |
(if (not= (= c (get s a)) (= c (get s b))) true)))] | |
(map #(count (keep % input)) [f g])) | |
;; (515 711) | |
;; 202003 | |
(let [input (->> (slurp "src/y2020/input202003") (re-seq #"[^\n]+")) | |
len (count (first input)) | |
f (fn [step] | |
(fn [line n] | |
(get line (mod (* step n) len)))) | |
g (fn [[x y]] ((frequencies (map (f x) (rest (take-nth y input)) (rest (range)))) \#))] | |
[(g [3 1]) (reduce * (map g [[1 1] [3 1] [5 1] [7 1] [1 2]]))]) | |
;; [167 736527114] | |
;; 202004 | |
(let [input (->> (slurp "src/y2020/input202004") (re-seq #"[\w#]+|\n\n") (partition-by #(= "\n\n" %))) | |
f (fn [xs] | |
(clojure.set/subset? #{"byr" "iyr" "eyr" "hgt" "hcl" "ecl" "pid"} (set (take-nth 2 xs)))) | |
g (fn [xs] | |
(let [{:strs [byr iyr eyr hgt hcl ecl pid]} (into {} (map vec (partition 2 xs)))] | |
(and byr (<= 1920 (edn/read-string byr) 2002) | |
iyr (<= 2010 (edn/read-string iyr) 2020) | |
eyr (<= 2020 (edn/read-string eyr) 2030) | |
hgt (if-let [[_ height unit] (re-matches #"(\d+)(cm|in)" hgt)] | |
(let [h (edn/read-string height)] | |
(or (and (= "cm" unit) (<= 150 h 193)) | |
(and (= "in" unit) (<= 59 h 76))))) | |
hcl (re-matches #"#[0-9a-f]{6}" hcl) | |
ecl (#{"amb" "blu" "brn" "gry" "grn" "hzl" "oth"} ecl) | |
pid (re-matches #"^\d{9}" pid)))) | |
input (filter f input)] | |
[(count input) (count (filter g input))]) | |
;; [260 153] | |
;; 202005 | |
(let [f #(if (= (inc %2) %1) %2 (reduced (inc %2))) | |
input (->> (str/escape (slurp "src/y2020/input202005") {\B \1 \F \0 \R \1 \L \0}) | |
(re-seq #"\w+") | |
(map #(Integer/parseInt % 2)) | |
sort | |
reverse)] | |
[(first input) (reduce f input)]) | |
;; [864 739] | |
;; 202006 | |
(let [input (-> (slurp "src/y2020/input202006") (str/split #"\n\n"))] | |
[(reduce + (map #(count (set (str/replace % "\n" ""))) input)) | |
(reduce + (map #(->> (map set (str/split-lines %)) (reduce clojure.set/intersection) count) input))]) | |
;; [6799 3354] | |
;; 202007 | |
(let [input (->> (slurp "src/y2020/input202007") | |
(re-seq #"[^\n]+") | |
(map #(re-seq #"(\d+ )?(\w+ \w+)(?= bag)" %))) | |
input1 (map (fn [[root & leaves]] | |
{:root (root 2) :leaves (set (map #(% 2) leaves))}) | |
input) | |
input2 (reduce (fn [m [root & leaves]] | |
(->> (into {} (for [[_ n name] leaves] | |
[name (if (nil? n) 0 (edn/read-string n))])) | |
(assoc m (root 2)))) | |
{"no other" 0} | |
input) | |
f (fn [color-set] | |
(set (for [{:keys [root leaves]} input1 | |
:when (seq (clojure.set/intersection color-set leaves))] | |
root))) | |
g (fn g [r [k v]] | |
(if (= "no other" k) 0 (+ r v (* v (reduce g 0 (input2 k))))))] | |
[(->> #{"shiny gold"} | |
(iterate f) | |
rest | |
(take-while seq) | |
(reduce clojure.set/union) | |
count) | |
(g -1 ["shiny gold" 1])]) | |
;; [128 20189] | |
;; 202008 | |
(let [input (->> (slurp "src/y2020/input202008") | |
(re-seq #"(\S+) (\S+)") | |
(mapv (fn [[_ op op1]] [op (edn/read-string op1)]))) | |
l (count input) | |
f (fn [input mode] | |
(->> {:i 0, :acc 0, :seen #{}} | |
(iterate (fn [{:keys [i acc seen] :as state}] | |
(cond (= i l) acc | |
(seen i) (if (= :loop mode) acc nil) | |
:else (let [[op op1] (input i)] | |
(merge (update state :seen #(conj % i)) | |
(condp = op | |
"nop" {:i (inc i)} | |
"acc" {:i (inc i) :acc (+ acc op1)} | |
"jmp" {:i (+ i op1)})))))) | |
(drop-while map?) | |
first))] | |
[(f input :loop) | |
(for [i (range (count input)) | |
:when (#{"nop" "jmp"} ((input i) 0)) | |
:let [input (update-in input [i 0] {"nop" "jmp", "jmp" "nop"}) | |
r (f input :bug)] | |
:when r] | |
r)]) | |
;; [1200 (1023)] | |
;; 202009 | |
(let [input (->> (slurp "src/y2020/input202009") (re-seq #"\d+") (mapv edn/read-string)) | |
p1 (loop [xs input] | |
(let [top25 (subvec xs 0 25) | |
top25-set (into #{} top25) | |
n26 (xs 25)] | |
(if (some #(and (top25-set %) (top25-set (- n26 %)) (not= % (- n26 %))) top25) | |
(recur (subvec xs 1)) | |
n26))) | |
p2 (loop [off0 0 | |
off1 2 | |
r (reduce + (subvec input off0 off1))] | |
(cond (= r p1) (->> (subvec input off0 off1) sort ((juxt first last)) (reduce +)) | |
(> r p1) (recur (inc off0) off1 (- r (input off0))) | |
(< r p1) (recur off0 (inc off1) (+ r (input off1)))))] | |
[p1 p2]) | |
;; [20874512 3012420] | |
;; 202010 | |
;; xxxxxx:() x x | |
;; xxxxx:(7) xxxxx x..xx x.x.x xx..x x.xxx xx.xx xxx.x | |
;; xxxx: (4) xxxx x..x x.xx xx.x | |
;; xxx: (2) xxx x.x | |
;; xx: (1) xx | |
(let [input (->> (slurp "src/y2020/input202010") | |
(re-seq #"\d+") | |
(map edn/read-string) | |
(cons 0) | |
sort | |
(partition 2 1) | |
(map #(- (second %) (first %)))) | |
{d1 1 d3 3} (frequencies input) | |
options {4 7, 3 4, 2 2, 1 1}] | |
[(* d1 (inc d3)) | |
(->> input | |
(partition-by #(= 3 %)) | |
(map #(if (= 3 (first %)) 1 (options (count %)))) | |
(reduce *))]) | |
;; [2484 15790581481472] | |
;; 202011 | |
(let [input (slurp "src/y2020/input202011") | |
input1 (str/split-lines input) | |
max-y (count input1) | |
max-x (count (first input1)) | |
seats-map (->> (re-seq #"." input) | |
(keep-indexed (fn [i cell] | |
(if (not= cell ".") | |
[[(mod i max-x) (quot i max-x)] cell]))) | |
(into {})) | |
seats (set (keys seats-map)) | |
adj-neighbors (fn [_ loc] | |
(for [a [-1 0 1] | |
b [-1 0 1] | |
:let [[x y :as xy] (map + loc [a b])] | |
:when (and (not= a b 0) | |
(<= 0 x (dec max-x)) | |
(<= 0 y (dec max-y)) | |
(seats xy))] | |
xy)) | |
star-neighbors (fn [seats-map loc] | |
(for [a [-1 0 1] | |
b [-1 0 1] | |
:when (not= a b 0) | |
:let [r (->> (rest (range)) | |
(keep #(let [[x y :as xy] (->> (map * [a b] [% %]) (mapv + loc)) | |
v (seats-map xy ".")] | |
(if (and (<= 0 x (dec max-x)) | |
(<= 0 y (dec max-y))) | |
(if (#{"#" "L"} v) xy nil) | |
-1))) | |
first)] | |
:when (not= -1 r)] | |
r)) | |
solution (fn [[neighbor-fn max-neighbor]] | |
(loop [seats-map seats-map] | |
(let [neighbor-coverage-map (->> seats | |
(filter #(= "#" (seats-map %))) | |
(mapcat #(neighbor-fn seats-map %)) | |
frequencies) | |
changed-seats (keep #(let [n (neighbor-coverage-map % 0)] | |
(cond (and (= "L" (seats-map %)) (= n 0)) [% "#"] | |
(and (= "#" (seats-map %)) (<= max-neighbor n)) [% "L"])) | |
seats)] | |
(if (seq changed-seats) | |
(recur (into seats-map changed-seats)) | |
(-> seats-map vals frequencies (get "#"))))))] | |
(pmap solution [[adj-neighbors 4] [star-neighbors 5]])) | |
;; (2334 2100) | |
;; 202012 | |
(let [input (->> (slurp "src/y2020/input202012") (re-seq #"\w+")) | |
dir-map {90 [1 0], 180 [0 -1], 270 [-1 0], 0 [0 1]} | |
f (fn [x] (reduce + (map #(Math/abs %) (:loc x)))) | |
ans1 (reduce (fn [{:keys [loc dir] :as state} instruction] | |
(let [v (edn/read-string (subs instruction 1))] | |
(case (first instruction) | |
\N (update state :loc #(map + % [0 v])) | |
\S (update state :loc #(map - % [0 v])) | |
\E (update state :loc #(map + % [v 0])) | |
\W (update state :loc #(map - % [v 0])) | |
\L (update state :dir #(mod (- % v) 360)) | |
\R (update state :dir #(mod (+ % v) 360)) | |
\F (update state :loc #(map + % (map * [v v] (dir-map dir))))))) | |
{:loc [0 0] :dir 90} | |
input) | |
ans2 (reduce (fn [{:keys [loc way] :as state} instruction] | |
(let [v (edn/read-string (subs instruction 1)) | |
[x y] way] | |
(case (first instruction) | |
\N (update state :way #(map + % [0 v])) | |
\S (update state :way #(map - % [0 v])) | |
\E (update state :way #(map + % [v 0])) | |
\W (update state :way #(map - % [v 0])) | |
\L (assoc state :way (case v | |
90 [(- y) x] | |
180 [(- x) (- y)] | |
270 [y (- x)])) | |
\R (assoc state :way (case v | |
270 [(- y) x] | |
180 [(- x) (- y)] | |
90 [y (- x)])) | |
\F (update state :loc #(map + % (map * way [v v])))))) | |
{:loc [0 0] :way [10 1]} | |
input)] | |
(map f [ans1 ans2])) | |
;; (1645 35292) | |
;; 202013 | |
(let [gcd (fn [a b] (if (= 0 b) a (recur b (mod a b)))) | |
lcm (fn [a b] (/ (* a b) (gcd a b))) | |
f (fn [n [_ s]] | |
(if s (let [x (edn/read-string s)] [x (mod (- (* x (quot n x)) n) x)]))) | |
[timestamp & buses1] (->> (slurp "src/y2020/input202013") (re-seq #"\d+") (map edn/read-string)) | |
buses2 (->> (slurp "src/y2020/input202013") | |
(re-seq #"(\d+)|x") | |
rest | |
(keep-indexed f))] | |
[(->> (map #(vector (- % (mod timestamp %)) %) buses1) sort first (reduce *)) | |
((reduce (fn [[factor1 t] [factor2 remainder2]] | |
(loop [t t] | |
(if (= (mod t factor2) remainder2) | |
[(lcm factor1 factor2) t] | |
(recur (+ t factor1))))) buses2) 1)]) | |
;; [2935 836024966345345] | |
;; 202014 | |
(let [input (->> (slurp "src/y2020/input202014") (re-seq #"(?:mask = (\w+))|(?:(\d+)\] = (\d+))")) | |
power-of-2 (vec (take 36 (iterate #(* 2 %) 1))) ;; [1 2 4 ... 2^35] | |
f (fn [{:keys [mask mem] :as state} [_ bitmask mem-address v]] | |
(if bitmask | |
(assoc state :mask [(edn/read-string (str "2r" (str/replace bitmask "X" "0"))) | |
(edn/read-string (str "2r" (str/escape bitmask {\1 \0, \X \1})))]) | |
(let [mem-address (edn/read-string mem-address) | |
pre-value (edn/read-string v) | |
post-value (bit-or (mask 0) (bit-and (mask 1) pre-value))] | |
(assoc-in state [:mem mem-address] post-value)))) | |
g (fn [{:keys [mask mem] :as state} [_ bitmask mem-address v]] | |
(if bitmask | |
(assoc state :mask [;; turn on 0 bits | |
(edn/read-string (str "2r" (-> bitmask (str/escape {\1 \0, \X \0, \0 \1})))) | |
;; turn on 1 bits | |
(edn/read-string (str "2r" (-> bitmask (str/replace "X" "0")))) | |
;; turn off X bits | |
(edn/read-string (str "2r" (-> bitmask (str/escape {\0 \1, \X \0})))) | |
;; bit-offset of X bits | |
(keep-indexed (fn [i x] (if (= x \X) (- 35 i))) bitmask)]) | |
(let [mem-address (edn/read-string mem-address) | |
value (edn/read-string v) | |
base-mem-address (bit-or (bit-and (mask 0) (bit-and (mask 2) mem-address)) | |
(bit-or (mask 1) (bit-and (mask 2) mem-address)))] | |
(->> (map power-of-2 (mask 3)) | |
(reduce (fn [base offset] (mapcat #(vector % (+ % offset)) base)) [0]) | |
(map (fn [offset] [(+ base-mem-address offset) value])) | |
(update state :mem into)))))] | |
(map #(->> input (reduce % {:mem {}}) :mem vals (reduce +)) [f g])) | |
;; (17765746710228 4401465949086) | |
;; 202015 | |
(let [input (->> (slurp "src/y2020/input202015") (re-seq #"\d+") (map edn/read-string)) | |
pos (reduce #(-> (update % :cnt inc) | |
(update %2 (fn [x] (if (nil? x) (list (:cnt %)) (take 2 (cons (:cnt %) x)))))) | |
{:cnt 1} | |
input) | |
f (fn [{:keys [pos n r]}] | |
(let [x (if (and (pos r) (= 1 (count (pos r)))) 0 (reduce - (pos r))) | |
n (inc n)] | |
{:pos (update pos x #(if (nil? %) (list n) (take 2 (cons n %)))) :n n :r x})) | |
g (fn [n] | |
(->> {:pos pos :r (last input) :n (count input)} | |
(iterate f) | |
(drop (- n (count input))) | |
first | |
:r))] | |
(mapv g [2020 30000000])) | |
;; [614 1065] | |
;; 202016 | |
(let [parse | |
(fn [{:keys [mode] :as state} line] | |
(cond (re-seq #"your" line) (assoc state :mode :your) | |
(re-seq #"nearby" line) (assoc state :mode :nearby) | |
(= mode :fields) (let [[_ field-name n1 n2 n3 n4] | |
(re-matches #"(.*): (\d+)-(\d+) or (\d+)-(\d+)" line) | |
[n1 n2 n3 n4] (map edn/read-string [n1 n2 n3 n4]) | |
x [field-name (into #{} (concat (range n1 (inc n2)) (range n3 (inc n4))))]] | |
(update state :fields conj x)) | |
(= mode :your) (->> (re-seq #"\d+" line) (mapv edn/read-string) (assoc state :your)) | |
(= mode :nearby) (->> (re-seq #"\d+" line) (map edn/read-string) (update state :nearby conj)))) | |
f (fn [{:keys [fields nearby]}] | |
(let [all-fields-set (reduce clojure.set/union (vals fields))] | |
(->> (flatten nearby) | |
(remove all-fields-set) | |
(reduce +)))) | |
g (fn [{:keys [fields your nearby]}] | |
(let [all-fields-set (reduce clojure.set/union (vals fields)) | |
valid-nearby (filter #(every? all-fields-set %) nearby) | |
field-samples (apply map vector valid-nearby)] | |
(loop [pending-nearby (mapcat (fn [[field range-set]] | |
(map-indexed #(vector field % (every? range-set %2)) | |
field-samples)) fields) | |
mapping {}] | |
(if (seq pending-nearby) | |
(let [new-mapping (into mapping | |
(keep #(let [v (val %)] | |
(if (= 1 ((frequencies (flatten v)) true)) | |
(->> v | |
(keep (fn [x] (if (= true (nth x 2)) [(x 0) (x 1)]))) | |
first))) | |
(group-by first pending-nearby))) | |
new-pending (remove #((set (vals new-mapping)) (% 1)) pending-nearby)] | |
(recur new-pending new-mapping)) | |
(reduce * (keep #(if (re-seq #"departure" (key %)) (your (val %))) mapping)))))) | |
input (->> (str/split (slurp "src/y2020/input202016") #"\n+") | |
(reduce parse {:fields {}, :your nil, :nearby [], :mode :fields}))] | |
[(f input) (g input)]) | |
;; [25895 5865723727753] | |
;; 202017 | |
(let [input (slurp "src/y2020/input202017") | |
input1 (str/split-lines input) | |
max-x (count (first input1)) | |
cube3-map (->> (re-seq #"." input) | |
(keep-indexed (fn [i cell] | |
(if (= cell "#") | |
[(mod i max-x) (quot i max-x) 0])))) | |
cube4-map (->> (re-seq #"." input) | |
(keep-indexed (fn [i cell] | |
(if (= cell "#") | |
[(mod i max-x) (quot i max-x) 0 0])))) | |
offsets3 (for [x [-1 0 1], y [-1 0 1], z [-1 0 1] :when (not= 0 x y z)] [x y z]) | |
offsets4 (for [x [-1 0 1], y [-1 0 1], z [-1 0 1], w [-1 0 1] :when (not= 0 x y z w)] [x y z w]) | |
neighbor3 (fn [pos] (map #(map + pos %) offsets3)) | |
neighbor4 (fn [pos] (map #(map + pos %) offsets4)) | |
f (fn [neighbor-fn] | |
(fn [s] | |
(let [neighbors (->> (mapcat neighbor-fn s) frequencies)] | |
(->> (keys neighbors) | |
(filter #(or (and (s %) (= 2 (neighbors %))) (= 3 (neighbors %)))) | |
set))))] | |
[(->> (set cube3-map) (iterate (f neighbor3)) (drop 6) first count) | |
(->> (set cube4-map) (iterate (f neighbor4)) (drop 6) first count)]) | |
;; [295 1972] | |
;; 202018 | |
(let [input (->> (slurp "src/y2020/input202018") str/split-lines (map #(edn/read-string (str "(" % ")")))) | |
f (fn f [xs] | |
(let [new-xs (map #(if (list? %) (f %) %) xs)] | |
(reduce (fn [r [op op1]] ((resolve op) r op1)) (first new-xs) (partition 2 (rest new-xs))))) | |
g (fn g [xs] | |
(->> (map #(if (list? %) (g %) %) xs) | |
(partition-by #{'*}) | |
(remove #{'(*)}) | |
(map #(reduce + (remove #{'+} %))) | |
(reduce *)))] | |
(map #(reduce + (map % input)) [f g])) | |
;; (280014646144 9966990988262) | |
;; 202021 | |
(let [input (->> (slurp "src/y2020/input202021") | |
(re-seq #"[^\n]+") | |
(map #(let [[ingredients _ allergens] (->> (re-seq #"\w+" %) (partition-by #{"contains"}))] | |
[(set ingredients) (set allergens)]))) | |
all-ingredients (reduce clojure.set/union (map first input)) | |
all-allergens (reduce clojure.set/union (map second input)) | |
possible-allergen-ingredient-mapping | |
(for [allergen all-allergens] | |
[allergen (reduce clojure.set/intersection (keep #(if ((second %) allergen) (first %)) input))]) | |
ingredients-with-allergen (reduce clojure.set/union (map second possible-allergen-ingredient-mapping)) | |
q1 (reduce + (map #(count (clojure.set/difference (first %) ingredients-with-allergen)) input)) | |
q2 (loop [possible-allergen-ingredient-mapping possible-allergen-ingredient-mapping | |
allergen-ingredient-mapping {}] | |
(if (seq possible-allergen-ingredient-mapping) | |
(let [found-mapping (keep (fn [[allergen ingredients]] | |
(if (= 1 (count ingredients)) [allergen (first ingredients)])) | |
possible-allergen-ingredient-mapping) | |
found-ingredients (set (map second found-mapping)) | |
new-possible-allergen-ingredient-mapping | |
(keep (fn [[allergen ingredients]] | |
(let [new-ingredients (clojure.set/difference ingredients found-ingredients)] | |
(if (seq new-ingredients) | |
[allergen new-ingredients]))) | |
possible-allergen-ingredient-mapping)] | |
(recur new-possible-allergen-ingredient-mapping | |
(into allergen-ingredient-mapping found-mapping))) | |
(->> (sort allergen-ingredient-mapping) (map second) (str/join ","))))] | |
[q1 q2]) | |
;; [1679 "lmxt,rggkbpj,mxf,gpxmf,nmtzlj,dlkxsxg,fvqg,dxzq"] | |
;; 202022 | |
(let [[p1 p2] (->> (str/split (slurp "src/y2020/input202022") #"\n\n") | |
(map #(into clojure.lang.PersistentQueue/EMPTY | |
(rest (map edn/read-string (re-seq #"\d+" %)))))) | |
score #(reduce + (map * % (range (count %) 0 -1))) | |
q1 (loop [p1 p1, p2 p2] | |
(let [head1 (peek p1) | |
head2 (peek p2)] | |
(cond | |
(nil? head1) p2 | |
(nil? head2) p1 | |
:else (cond (< head1 head2) (recur (pop p1) (conj (pop p2) head2 head1)) | |
:else (recur (conj (pop p1) head1 head2) (pop p2)))))) | |
q2 (((fn g [p1 p2] | |
(loop [p1 p1, p2 p2, seen #{}] | |
(let [head1 (peek p1) | |
head2 (peek p2) | |
p1' (pop p1) | |
p2' (pop p2)] | |
(cond | |
(seen [(seq p1) (seq p2)]) [:p1 p1] | |
(nil? head1) [:p2 p2] | |
(nil? head2) [:p1 p1] | |
(and (< head1 (count p1)) (< head2 (count p2))) | |
(let [[winner _] (g (into clojure.lang.PersistentQueue/EMPTY (take head1 p1')) | |
(into clojure.lang.PersistentQueue/EMPTY (take head2 p2')))] | |
(cond (= :p2 winner) | |
(recur p1' (conj p2' head2 head1) (conj seen [(seq p1) (seq p2)])) | |
:else | |
(recur (conj p1' head1 head2) p2' (conj seen [(seq p1) (seq p2)])))) | |
:else (cond (< head1 head2) | |
(recur p1' (conj p2' head2 head1) (conj seen [(seq p1) (seq p2)])) | |
:else | |
(recur (conj p1' head1 head2) p2' (conj seen [(seq p1) (seq p2)]))))))) | |
p1 p2) 1)] | |
(map score [q1 q2])) | |
;; (32815 30695) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment