├── resources ├── day22_sample_data1.txt ├── day25_sample_data.txt ├── day11_data.txt ├── day11_sample_data.txt ├── day15_sample_data.txt ├── day13_sample_data.txt ├── day14_sample_data.txt ├── day12_data.txt ├── day10_sample_data.txt ├── day04_sample_data.txt ├── day20_sample_data.txt ├── day06_data.txt ├── day22_sample_data2.txt ├── day08_sample_data.txt ├── day16_data.txt ├── day14_data.txt ├── day19_sample_data.txt ├── day22_sample_data3.txt ├── day24_data.txt ├── day18_data.txt ├── day07_data.txt ├── day10_data.txt └── day09_data.txt ├── .gitignore ├── project.clj ├── test └── advent_2021_clojure │ ├── day25_test.clj │ ├── day07_test.clj │ ├── day06_test.clj │ ├── day24_test.clj │ ├── day08_test.clj │ ├── day11_test.clj │ ├── day01_test.clj │ ├── day10_test.clj │ ├── day15_test.clj │ ├── day09_test.clj │ ├── day02_test.clj │ ├── day01_windowed_test.clj │ ├── day14_test.clj │ ├── day03_test.clj │ ├── day05_test.clj │ ├── day19_test.clj │ ├── point_test.clj │ ├── day22_test.clj │ ├── day20_test.clj │ ├── day23_test.clj │ ├── day12_test.clj │ ├── day13_test.clj │ ├── day16_test.clj │ ├── day04_test.clj │ ├── day17_test.clj │ ├── day18_test.clj │ └── day21_test.clj ├── src └── advent_2021_clojure │ ├── day01_windowed.clj │ ├── day01.clj │ ├── day04_all_solo.clj │ ├── day07.clj │ ├── day05.clj │ ├── day06.clj │ ├── day08_joepittsy.clj │ ├── utils.clj │ ├── day14.clj │ ├── day11.clj │ ├── sorted_value_map.clj │ ├── day02.clj │ ├── day10.clj │ ├── day25.clj │ ├── day13.clj │ ├── day09.clj │ ├── day03.clj │ ├── day15.clj │ ├── point.clj │ ├── day20.clj │ ├── day24.clj │ ├── day08.clj │ ├── day12.clj │ ├── day17.clj │ ├── day04.clj │ ├── day18.clj │ ├── day22.clj │ ├── day21.clj │ ├── day16.clj │ ├── day19.clj │ └── day23.clj ├── docs ├── day24.md ├── day06.md ├── day07.md ├── day14.md ├── day25.md ├── day01.md ├── day13.md ├── day09.md ├── day20.md ├── day11.md ├── day12.md └── day02.md └── README.md /resources/day22_sample_data1.txt: -------------------------------------------------------------------------------- 1 | on x=10..12,y=10..12,z=10..12 2 | on x=11..13,y=11..13,z=11..13 3 | off x=9..11,y=9..11,z=9..11 4 | on x=10..10,y=10..10,z=10..10 -------------------------------------------------------------------------------- /resources/day25_sample_data.txt: -------------------------------------------------------------------------------- 1 | v...>>.vv> 2 | .vv>>.vv.. 3 | >>.>v>...v 4 | >>v>>.>.v. 5 | v>v.vv.v.. 6 | >.>>..v... 7 | .vv..>.>v. 8 | v.v..>>v.v 9 | ....v..v.> -------------------------------------------------------------------------------- /resources/day11_data.txt: -------------------------------------------------------------------------------- 1 | 4658137637 2 | 3277874355 3 | 4525611183 4 | 3128125888 5 | 8734832838 6 | 4175463257 7 | 8321423552 8 | 4832145253 9 | 8286834851 10 | 4885323138 -------------------------------------------------------------------------------- /resources/day11_sample_data.txt: -------------------------------------------------------------------------------- 1 | 5483143223 2 | 2745854711 3 | 5264556173 4 | 6141336146 5 | 6357385478 6 | 4167524645 7 | 2176841721 8 | 6882881134 9 | 4846848554 10 | 5283751526 -------------------------------------------------------------------------------- /resources/day15_sample_data.txt: -------------------------------------------------------------------------------- 1 | 1163751742 2 | 1381373672 3 | 2136511328 4 | 3694931569 5 | 7463417111 6 | 1319128137 7 | 1359912421 8 | 3125421639 9 | 1293138521 10 | 2311944581 11 | -------------------------------------------------------------------------------- /resources/day13_sample_data.txt: -------------------------------------------------------------------------------- 1 | 6,10 2 | 0,14 3 | 9,10 4 | 0,3 5 | 10,4 6 | 4,11 7 | 6,0 8 | 6,12 9 | 4,1 10 | 0,13 11 | 10,12 12 | 3,4 13 | 3,0 14 | 8,4 15 | 1,10 16 | 2,14 17 | 8,10 18 | 9,0 19 | 20 | fold along y=7 21 | fold along x=5 -------------------------------------------------------------------------------- /resources/day14_sample_data.txt: -------------------------------------------------------------------------------- 1 | NNCB 2 | 3 | CH -> B 4 | HH -> N 5 | CB -> H 6 | NH -> C 7 | HB -> C 8 | HC -> B 9 | HN -> C 10 | NN -> C 11 | BH -> H 12 | NC -> B 13 | NB -> B 14 | BN -> B 15 | BB -> N 16 | BC -> B 17 | CC -> N 18 | CN -> C -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pom.xml 2 | pom.xml.asc 3 | *.jar 4 | *.class 5 | *.iml 6 | *.idea 7 | /lib/ 8 | /classes/ 9 | /target/ 10 | /checkouts/ 11 | .lein-deps-sum 12 | .lein-repl-history 13 | .lein-plugins/ 14 | .lein-failures 15 | .nrepl-port 16 | .cpcache/ 17 | -------------------------------------------------------------------------------- /resources/day12_data.txt: -------------------------------------------------------------------------------- 1 | KF-sr 2 | OO-vy 3 | start-FP 4 | FP-end 5 | vy-mi 6 | vy-KF 7 | vy-na 8 | start-sr 9 | FP-lh 10 | sr-FP 11 | na-FP 12 | end-KF 13 | na-mi 14 | lh-KF 15 | end-lh 16 | na-start 17 | wp-KF 18 | mi-KF 19 | vy-sr 20 | vy-lh 21 | sr-mi -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject advent-2021-clojure "0.1.0-SNAPSHOT" 2 | :dependencies [[org.clojure/clojure "1.10.1"]] 3 | :target-path "target/%s" 4 | :profiles {:uberjar {:aot :all 5 | :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}}) 6 | -------------------------------------------------------------------------------- /resources/day10_sample_data.txt: -------------------------------------------------------------------------------- 1 | [({(<(())[]>[[{[]{<()<>> 2 | [(()[<>])]({[<{<<[]>>( 3 | {([(<{}[<>[]}>{[]{[(<()> 4 | (((({<>}<{<{<>}{[]{[]{} 5 | [[<[([]))<([[{}[[()]]] 6 | [{[{({}]{}}([{[{{{}}([] 7 | {<[[]]>}<{[{[{[]{()[[[] 8 | [<(<(<(<{}))><([]([]() 9 | <{([([[(<>()){}]>(<<{{ 10 | <{([{{}}[<[[[<>{}]]]>[]] -------------------------------------------------------------------------------- /resources/day04_sample_data.txt: -------------------------------------------------------------------------------- 1 | 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 2 | 3 | 22 13 17 11 0 4 | 8 2 23 4 24 5 | 21 9 14 16 7 6 | 6 10 3 18 5 7 | 1 12 20 15 19 8 | 9 | 3 15 0 2 22 10 | 9 18 13 17 5 11 | 19 8 7 25 23 12 | 20 11 10 24 4 13 | 14 21 16 12 6 14 | 15 | 14 21 17 24 4 16 | 10 16 15 9 19 17 | 18 8 23 26 20 18 | 22 11 13 6 5 19 | 2 0 12 3 7 -------------------------------------------------------------------------------- /test/advent_2021_clojure/day25_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day25-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day25 :refer :all])) 4 | 5 | (def test-input (slurp "resources/day25_sample_data.txt")) 6 | (def puzzle-input (slurp "resources/day25_data.txt")) 7 | 8 | (deftest part1-test 9 | (are [expected input] (= expected (part1 input)) 10 | 58 test-input 11 | 414 puzzle-input)) -------------------------------------------------------------------------------- /resources/day20_sample_data.txt: -------------------------------------------------------------------------------- 1 | ..#.#..#####.#.#.#.###.##.....###.##.#..###.####..#####..#....#..#..##..###..######.###...####..#..#####..##..#.#####...##.#.#..#.##..#.#......#.###.######.###.####...#.##.##..#..#..#####.....#.#....###..#.##......#.....#..#..#..##..#...##.######.####.####.#.#...#.......#..#.#.#...####.##.#......#..#...##.#.##..#...##.#.##..###.#......#.#.......#.#.#.####.###.##...#.....####.#..#..#.##.#....##..#.####....##...##..#...#......#.#.......#.......##..####..#...#.#.#...##..#.#..###..#####........#..####......#..# 2 | 3 | #..#. 4 | #.... 5 | ##..# 6 | ..#.. 7 | ..### -------------------------------------------------------------------------------- /src/advent_2021_clojure/day01_windowed.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day01-windowed 2 | (:require [clojure.string :as str] 3 | [advent-2021-clojure.utils :refer [parse-int]])) 4 | 5 | (defn parse-measurements [input] 6 | (->> (str/split-lines input) 7 | (map parse-int))) 8 | 9 | (defn solve [input window-size] 10 | (->> (parse-measurements input) 11 | (partition window-size 1) 12 | (map (juxt first last)) 13 | (filter (partial apply <)) 14 | count)) 15 | 16 | (defn part1 [input] (solve input 2)) 17 | (defn part2 [input] (solve input 4)) -------------------------------------------------------------------------------- /resources/day06_data.txt: -------------------------------------------------------------------------------- 1 | 2,3,1,3,4,4,1,5,2,3,1,1,4,5,5,3,5,5,4,1,2,1,1,1,1,1,1,4,1,1,1,4,1,3,1,4,1,1,4,1,3,4,5,1,1,5,3,4,3,4,1,5,1,3,1,1,1,3,5,3,2,3,1,5,2,2,1,1,4,1,1,2,2,2,2,3,2,1,2,5,4,1,1,1,5,5,3,1,3,2,2,2,5,1,5,2,4,1,1,3,3,5,2,3,1,2,1,5,1,4,3,5,2,1,5,3,4,4,5,3,1,2,4,3,4,1,3,1,1,2,5,4,3,5,3,2,1,4,1,4,4,2,3,1,1,2,1,1,3,3,3,1,1,2,2,1,1,1,5,1,5,1,4,5,1,5,2,4,3,1,1,3,2,2,1,4,3,1,1,1,3,3,3,4,5,2,3,3,1,3,1,4,1,1,1,2,5,1,4,1,2,4,5,4,1,5,1,5,5,1,5,5,2,5,5,1,4,5,1,1,3,2,5,5,5,4,3,2,5,4,1,1,2,4,4,1,1,1,3,2,1,1,2,1,2,2,3,4,5,4,1,4,5,1,1,5,5,1,4,1,4,4,1,5,3,1,4,3,5,3,1,3,1,4,2,4,5,1,4,1,2,4,1,2,5,1,1,5,1,1,3,1,1,2,3,4,2,4,3,1 -------------------------------------------------------------------------------- /test/advent_2021_clojure/day07_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day07-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day07 :refer :all])) 4 | 5 | (def test-input "16,1,2,0,4,2,7,1,2,14") 6 | (def puzzle-input (slurp "resources/day07_data.txt")) 7 | 8 | (deftest part1-test 9 | (are [expected input] (= expected (part1 input)) 10 | 37 test-input 11 | 349769 puzzle-input)) 12 | 13 | (deftest part2-test 14 | (are [expected input] (= expected (part2 input)) 15 | 168 test-input 16 | 99540554 puzzle-input)) -------------------------------------------------------------------------------- /test/advent_2021_clojure/day06_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day06-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day06 :refer :all])) 4 | 5 | (def test-input "3,4,3,1,2") 6 | (def puzzle-input (slurp "resources/day06_data.txt")) 7 | 8 | (deftest part1-test 9 | (are [expected input] (= expected (day1 input)) 10 | 5934 test-input 11 | 358214 puzzle-input)) 12 | 13 | (deftest part2-test 14 | (are [expected input] (= expected (day2 input)) 15 | 26984457539 test-input 16 | 1622533344325 puzzle-input)) -------------------------------------------------------------------------------- /test/advent_2021_clojure/day24_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day24-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day24 :refer :all])) 4 | 5 | (def puzzle-input (slurp "resources/day24_data.txt")) 6 | 7 | (deftest magic-function-test 8 | (is (= 112 (magic-function [1 2 3] 4 5))) 9 | (is (= 4 (magic-function [1 2 3] 4 6))) 10 | (is (= 268 (magic-function [3 2 3] 30 5))) 11 | (is (= 10 (magic-function [3 2 3] 30 6)))) 12 | 13 | (deftest part1-test 14 | (is (= 36969794979199 (part1 puzzle-input)))) 15 | 16 | (deftest part2-test 17 | (is (= 11419161313147 (part2 puzzle-input)))) 18 | 19 | -------------------------------------------------------------------------------- /test/advent_2021_clojure/day08_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day08-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day08 :refer :all])) 4 | 5 | (def test-input (slurp "resources/day08_sample_data.txt")) 6 | (def puzzle-input (slurp "resources/day08_data.txt")) 7 | 8 | (deftest part1-test 9 | (are [expected input] (= expected (part1 input)) 10 | 26 test-input 11 | 470 puzzle-input)) 12 | 13 | (deftest part2-test 14 | (are [expected input] (= expected (part2 input)) 15 | 61229 test-input 16 | 989396 puzzle-input)) -------------------------------------------------------------------------------- /test/advent_2021_clojure/day11_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day11-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day11 :refer :all])) 4 | 5 | (def test-input (slurp "resources/day11_sample_data.txt")) 6 | (def puzzle-input (slurp "resources/day11_data.txt")) 7 | 8 | (deftest part1-test 9 | (are [expected input] (= expected (part1 input)) 10 | 1656 test-input 11 | 1686 puzzle-input)) 12 | 13 | (deftest part2-test 14 | (are [expected input] (= expected (part2 input)) 15 | 195 test-input 16 | 360 puzzle-input)) -------------------------------------------------------------------------------- /test/advent_2021_clojure/day01_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day01-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day01 :refer :all])) 4 | 5 | (def test-input "199\n200\n208\n210\n200\n207\n240\n269\n260\n263\n") 6 | (def puzzle-input (slurp "resources/day01_data.txt")) 7 | 8 | (deftest part1-test 9 | (are [expected input] (= expected (part1 input)) 10 | 7 test-input 11 | 1233 puzzle-input)) 12 | 13 | (deftest part2-test 14 | (are [expected input] (= expected (part2 input)) 15 | 5 test-input 16 | 1275 puzzle-input)) -------------------------------------------------------------------------------- /test/advent_2021_clojure/day10_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day10-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day10 :refer :all])) 4 | 5 | (def test-input (slurp "resources/day10_sample_data.txt")) 6 | (def puzzle-input (slurp "resources/day10_data.txt")) 7 | 8 | (deftest part1-test 9 | (are [expected input] (= expected (part1 input)) 10 | 26397 test-input 11 | 388713 puzzle-input)) 12 | 13 | (deftest part2-test 14 | (are [expected input] (= expected (part2 input)) 15 | 288957 test-input 16 | 3539961434 puzzle-input)) -------------------------------------------------------------------------------- /test/advent_2021_clojure/day15_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day15-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day15 :refer :all])) 4 | 5 | (def test-input (slurp "resources/day15_sample_data.txt")) 6 | (def puzzle-input (slurp "resources/day15_data.txt")) 7 | 8 | (deftest part1-test 9 | (are [expected input] (= expected (part1 input)) 10 | 40 test-input 11 | 398 puzzle-input)) 12 | 13 | (deftest part2-test 14 | (are [expected input] (= expected (part2 input)) 15 | 315 test-input 16 | 2817 puzzle-input)) 17 | -------------------------------------------------------------------------------- /test/advent_2021_clojure/day09_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day09-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day09 :refer :all])) 4 | 5 | (def test-input "2199943210\n3987894921\n9856789892\n8767896789\n9899965678") 6 | (def puzzle-input (slurp "resources/day09_data.txt")) 7 | 8 | (deftest part1-test 9 | (are [expected input] (= expected (part1 input)) 10 | 15 test-input 11 | 444 puzzle-input)) 12 | 13 | (deftest part2-test 14 | (are [expected input] (= expected (part2 input)) 15 | 1134 test-input 16 | 1168440 puzzle-input)) -------------------------------------------------------------------------------- /test/advent_2021_clojure/day02_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day02-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day02 :refer :all])) 4 | 5 | (def test-input "forward 5\ndown 5\nforward 8\nup 3\ndown 8\nforward 2") 6 | (def puzzle-input (slurp "resources/day02_data.txt")) 7 | 8 | (deftest part1-test 9 | (are [expected input] (= expected (part1 input)) 10 | 150 test-input 11 | 1484118 puzzle-input)) 12 | 13 | (deftest part2-test 14 | (are [expected input] (= expected (part2 input)) 15 | 900 test-input 16 | 1463827010 puzzle-input)) -------------------------------------------------------------------------------- /test/advent_2021_clojure/day01_windowed_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day01-windowed-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day01-windowed :refer :all])) 4 | 5 | (def test-input "199\n200\n208\n210\n200\n207\n240\n269\n260\n263\n") 6 | (def puzzle-input (slurp "resources/day01_data.txt")) 7 | 8 | (deftest part1-test 9 | (are [expected input] (= expected (part1 input)) 10 | 7 test-input 11 | 1233 puzzle-input)) 12 | 13 | (deftest part2-test 14 | (are [expected input] (= expected (part2 input)) 15 | 5 test-input 16 | 1275 puzzle-input)) -------------------------------------------------------------------------------- /test/advent_2021_clojure/day14_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day14-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day14 :refer :all])) 4 | 5 | (def test-input (slurp "resources/day14_sample_data.txt")) 6 | (def puzzle-input (slurp "resources/day14_data.txt")) 7 | 8 | (deftest part1-test 9 | (are [expected input] (= expected (part1 input)) 10 | 1588 test-input 11 | 3058 puzzle-input)) 12 | 13 | (deftest part2-test 14 | (are [expected input] (= expected (part2 input)) 15 | 2188189693529 test-input 16 | 3447389044530 puzzle-input)) 17 | -------------------------------------------------------------------------------- /test/advent_2021_clojure/day03_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day03-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day03 :refer :all])) 4 | 5 | (def test-input "00100\n11110\n10110\n10111\n10101\n01111\n00111\n11100\n10000\n11001\n00010\n01010") 6 | (def puzzle-input (slurp "resources/day03_data.txt")) 7 | 8 | (deftest part1-test 9 | (are [expected input] (= expected (part1 input)) 10 | 198 test-input 11 | 3277364 puzzle-input)) 12 | 13 | (deftest part2-test 14 | (are [expected input] (= expected (part2 input)) 15 | 230 test-input 16 | 5736383 puzzle-input)) -------------------------------------------------------------------------------- /src/advent_2021_clojure/day01.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day01 2 | (:require [clojure.string :as str] 3 | [advent-2021-clojure.utils :refer [parse-int]])) 4 | 5 | (defn parse-measurements [input] 6 | (->> (str/split-lines input) 7 | (map parse-int))) 8 | 9 | (defn solve [input mapping] 10 | (->> (parse-measurements input) 11 | (mapping) 12 | (partition 2 1) 13 | (filter (partial apply <)) 14 | count)) 15 | 16 | (defn three-measurement-sliding-window [measurements] 17 | (->> (partition 3 1 measurements) 18 | (map (partial apply +)))) 19 | 20 | (defn part1 [input] (solve input identity)) 21 | (defn part2 [input] (solve input three-measurement-sliding-window)) -------------------------------------------------------------------------------- /src/advent_2021_clojure/day04_all_solo.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day04-all-solo 2 | (:require [advent-2021-clojure.day04 :as day04])) 3 | 4 | (defn solo-results [game] 5 | (->> (iterate day04/take-turn game) 6 | (keep-indexed (fn [idx g] (when-let [score (day04/final-score g)] 7 | {:turns idx :score score}))) 8 | first)) 9 | 10 | (defn solve-all [game] 11 | (let [solo-games (map #(assoc game :boards [%]) (:boards game))] 12 | (->> solo-games 13 | (map solo-results) 14 | (sort-by :turns)))) 15 | 16 | (defn part1 [input] (-> input day04/parse-game solve-all first :score)) 17 | (defn part2 [input] (-> input day04/parse-game solve-all last :score)) -------------------------------------------------------------------------------- /test/advent_2021_clojure/day05_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day05-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day05 :refer :all])) 4 | 5 | (def test-input "0,9 -> 5,9\n8,0 -> 0,8\n9,4 -> 3,4\n2,2 -> 2,1\n7,0 -> 7,4\n6,4 -> 2,0\n0,9 -> 2,9\n3,4 -> 1,4\n0,0 -> 8,8\n5,5 -> 8,2") 6 | (def puzzle-input (slurp "resources/day05_data.txt")) 7 | 8 | (deftest part1-test 9 | (are [expected input] (= expected (part1 input)) 10 | 5 test-input 11 | 4993 puzzle-input)) 12 | 13 | (deftest part2-test 14 | (are [expected input] (= expected (part2 input)) 15 | 12 test-input 16 | 21101 puzzle-input)) -------------------------------------------------------------------------------- /test/advent_2021_clojure/day19_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day19-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day19 :refer :all])) 4 | 5 | (def test-input (slurp "resources/day19_sample_data.txt")) 6 | (def puzzle-input (slurp "resources/day19_data.txt")) 7 | 8 | ; Again, don't run the tests today. The puzzle data takes about 10 minutes to run each time. 9 | #_(deftest part1-test 10 | (are [expected input] (= expected (part1 input)) 11 | 79 test-input 12 | 440 puzzle-input)) 13 | 14 | #_(deftest part2-test 15 | (are [expected input] (= expected (part2 input)) 16 | 3621 test-input 17 | 13382 puzzle-input)) 18 | -------------------------------------------------------------------------------- /src/advent_2021_clojure/day07.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day07 2 | (:require [advent-2021-clojure.utils :as utils :refer [parse-int]] 3 | [clojure.string :as str])) 4 | 5 | (defn parse-crabs [input] 6 | (map parse-int (str/split input #","))) 7 | 8 | (defn total-crab-distance-to [f crabs pos] 9 | (->> crabs 10 | (map #(-> % (- pos) utils/abs f)) 11 | (apply +))) 12 | 13 | (defn solve [f input] 14 | (let [crabs (parse-crabs input) 15 | [first-crab last-crab] (apply (juxt min max) crabs)] 16 | (->> (range first-crab (inc last-crab)) 17 | (map (partial total-crab-distance-to f crabs)) 18 | (apply min)))) 19 | 20 | (defn part1 [input] (solve identity input)) 21 | (defn part2 [input] (solve utils/summation input)) 22 | -------------------------------------------------------------------------------- /test/advent_2021_clojure/point_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.point-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.point :refer :all])) 4 | 5 | (deftest inclusive-line-between-test 6 | (are [p1 p2 expected] (= expected (inclusive-line-between p1 p2)) 7 | [0 0] [0 0] '([0 0]) 8 | [2 3] [2 6] '([2 3] [2 4] [2 5] [2 6]) 9 | [2 6] [2 3] '([2 6] [2 5] [2 4] [2 3]) 10 | [2 3] [5 3] '([2 3] [3 3] [4 3] [5 3]) 11 | [5 3] [2 3] '([5 3] [4 3] [3 3] [2 3]) 12 | [2 3] [4 5] '([2 3] [3 4] [4 5]) 13 | [2 3] [4 1] '([2 3] [3 2] [4 1]) 14 | [2 3] [0 5] '([2 3] [1 4] [0 5]) 15 | [2 3] [0 1] '([2 3] [1 2] [0 1]))) -------------------------------------------------------------------------------- /docs/day24.md: -------------------------------------------------------------------------------- 1 | # Day Twenty-Four: Arithmetic Logic Unit 2 | 3 | * [Problem statement](https://adventofcode.com/2021/day/24) 4 | * No link to my solution 5 | 6 | --- 7 | 8 | ## An exercise in reading machine code 9 | 10 | Every year we get one or two problems that aren't so much about designing a solution to a problem, but which depend on 11 | reading the puzzle input and working the solution from there instead of doing it from the problem statement. This is 12 | not worth my time, and since my approach to these annoying problems each year is to just migrate code from one 13 | language into Clojure, I decided the source I would plagarize is my good friend Todd Ginsberg. So if you want to read 14 | his approach, please go to [his write-up](https://todd.ginsberg.com/post/advent-of-code/2021/day24/). My code is 15 | instruction-by-instruction his solution. 16 | -------------------------------------------------------------------------------- /resources/day22_sample_data2.txt: -------------------------------------------------------------------------------- 1 | on x=-20..26,y=-36..17,z=-47..7 2 | on x=-20..33,y=-21..23,z=-26..28 3 | on x=-22..28,y=-29..23,z=-38..16 4 | on x=-46..7,y=-6..46,z=-50..-1 5 | on x=-49..1,y=-3..46,z=-24..28 6 | on x=2..47,y=-22..22,z=-23..27 7 | on x=-27..23,y=-28..26,z=-21..29 8 | on x=-39..5,y=-6..47,z=-3..44 9 | on x=-30..21,y=-8..43,z=-13..34 10 | on x=-22..26,y=-27..20,z=-29..19 11 | off x=-48..-32,y=26..41,z=-47..-37 12 | on x=-12..35,y=6..50,z=-50..-2 13 | off x=-48..-32,y=-32..-16,z=-15..-5 14 | on x=-18..26,y=-33..15,z=-7..46 15 | off x=-40..-22,y=-38..-28,z=23..41 16 | on x=-16..35,y=-41..10,z=-47..6 17 | off x=-32..-23,y=11..30,z=-14..3 18 | on x=-49..-5,y=-3..45,z=-29..18 19 | off x=18..30,y=-20..-8,z=-3..13 20 | on x=-41..9,y=-7..43,z=-33..15 21 | on x=-54112..-39298,y=-85059..-49293,z=-27449..7877 22 | on x=967..23432,y=45373..81175,z=27513..53682 -------------------------------------------------------------------------------- /test/advent_2021_clojure/day22_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day22-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day22 :refer :all])) 4 | 5 | (def test-input1 (slurp "resources/day22_sample_data1.txt")) 6 | (def test-input2 (slurp "resources/day22_sample_data2.txt")) 7 | (def test-input3 (slurp "resources/day22_sample_data3.txt")) 8 | (def puzzle-input (slurp "resources/day22_data.txt")) 9 | 10 | (deftest part1-test 11 | (are [expected input] (= expected (part1 input)) 12 | 39 test-input1 13 | 590784 test-input2 14 | 590467 puzzle-input)) 15 | 16 | (deftest part2-test 17 | (are [expected input] (= expected (part2 input)) 18 | 2758514936282235 test-input3 19 | 1225064738333321 puzzle-input)) 20 | -------------------------------------------------------------------------------- /resources/day08_sample_data.txt: -------------------------------------------------------------------------------- 1 | be cfbegad cbdgef fgaecd cgeb fdcge agebfd fecdb fabcd edb | fdgacbe cefdb cefbgd gcbe 2 | edbfga begcd cbg gc gcadebf fbgde acbgfd abcde gfcbed gfec | fcgedb cgb dgebacf gc 3 | fgaebd cg bdaec gdafb agbcfd gdcbef bgcad gfac gcb cdgabef | cg cg fdcagb cbg 4 | fbegcd cbd adcefb dageb afcb bc aefdc ecdab fgdeca fcdbega | efabcd cedba gadfec cb 5 | aecbfdg fbg gf bafeg dbefa fcge gcbea fcaegb dgceab fcbdga | gecf egdcabf bgf bfgea 6 | fgeab ca afcebg bdacfeg cfaedg gcfdb baec bfadeg bafgc acf | gebdcfa ecba ca fadegcb 7 | dbcfg fgd bdegcaf fgec aegbdf ecdfab fbedc dacgb gdcebf gf | cefg dcbef fcge gbcadfe 8 | bdfegc cbegaf gecbf dfcage bdacg ed bedf ced adcbefg gebcd | ed bcgafe cdgba cbgef 9 | egadfb cdbfeg cegd fecab cgb gbdefca cg fgcdab egfdb bfceg | gbdfcae bgc cg cgb 10 | gcafb gcf dcaebfg ecagb gf abcdeg gaef cafbge fdbac fegbdc | fgae cfgab fg bagce -------------------------------------------------------------------------------- /src/advent_2021_clojure/day05.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day05 2 | (:require 3 | [advent-2021-clojure.point :as p] 4 | [advent-2021-clojure.utils :refer [parse-int]] 5 | [clojure.string :as str])) 6 | 7 | (defn parse-line [line] 8 | (let [[x1 y1 x2 y2] (->> (re-matches #"(\d+),(\d+) -> (\d+),(\d+)" line) 9 | rest 10 | (map parse-int))] 11 | [[x1 y1] [x2 y2]])) 12 | 13 | (defn parse-vents [input] (->> input str/split-lines (map parse-line))) 14 | 15 | (defn solve [input pred] 16 | (->> (parse-vents input) 17 | (filter pred) 18 | (map p/inclusive-line-between) 19 | (apply concat) 20 | frequencies 21 | (filter #(> (val %) 1)) 22 | count)) 23 | 24 | (defn part1 [input] (solve input (some-fn p/horizontal-line? p/vertical-line?))) 25 | (defn part2 [input] (solve input identity)) 26 | -------------------------------------------------------------------------------- /test/advent_2021_clojure/day20_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day20-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day20 :refer :all])) 4 | 5 | (def test-input (slurp "resources/day20_sample_data.txt")) 6 | (def puzzle-input (slurp "resources/day20_data.txt")) 7 | 8 | (deftest part1-test 9 | (are [expected input] (= expected (part1 input)) 10 | 35 test-input 11 | 5571 puzzle-input)) 12 | 13 | (deftest part2-test 14 | (are [expected input] (= expected (part2 input)) 15 | 3351 test-input 16 | 17965 puzzle-input)) 17 | 18 | (defn print-image [image] 19 | (->> (group-by (comp second first) image) 20 | sort 21 | (map (fn [[_ points]] (apply str (map (comp {1 light-pixel, 0 dark-pixel} second) 22 | (sort-by ffirst points))))) 23 | (run! println))) -------------------------------------------------------------------------------- /test/advent_2021_clojure/day23_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day23-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day23 :refer :all])) 4 | 5 | (def test-input [:b :a :c :d :b :c :d :a]) 6 | (def puzzle-input [:b :d :b :c :d :a :a :c]) 7 | 8 | (deftest room-exiting?-test 9 | (let [burrow {:rooms {:a [:a :a] :b [:b :c] :c [] :d [:c :d]}}] 10 | (is (not (room-exiting? burrow :a))) 11 | (is (some? (room-exiting? burrow :b))) 12 | (is (not (room-exiting? burrow :c))) 13 | (is (some? (room-exiting? burrow :d))))) 14 | 15 | ; Don't run this; it takes about 100 minutes to run on the puzzle data set 16 | #_(deftest part1-test 17 | (are [expected input] (= expected (part1 input)) 18 | 12521 test-input 19 | 15412 puzzle-input)) 20 | 21 | ; Part 2 took 8 minutes for part 1, and 6 minutes for part 2 22 | #_(deftest part2-test 23 | (are [expected input] (= expected (part2 input)) 24 | 44169 test-input 25 | 52358 puzzle-input)) 26 | -------------------------------------------------------------------------------- /src/advent_2021_clojure/day06.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day06 2 | (:require [advent-2021-clojure.utils :refer [parse-int]])) 3 | 4 | (def delivery-timer 0) 5 | (def post-delivery-timer 6) 6 | (def new-fish-timer 8) 7 | (def no-fish {0 0, 1 0, 2 0, 3 0, 4 0, 5 0, 6 0, 7 0, 8 0}) 8 | 9 | (defn parse-fish [input] 10 | (->> (re-seq #"\d" input) 11 | (map parse-int) 12 | frequencies 13 | (merge no-fish))) 14 | 15 | (defn next-timer [timer] 16 | (if (= timer delivery-timer) post-delivery-timer (dec timer))) 17 | 18 | (defn next-generation [fish] 19 | (let [deliveries (fish delivery-timer)] 20 | (-> (reduce-kv (fn [m k v] (update m (next-timer k) + v)) no-fish fish) 21 | (assoc new-fish-timer deliveries)))) 22 | 23 | (defn nth-generation [n fish] 24 | (-> (iterate next-generation fish) 25 | (nth n))) 26 | 27 | (defn solve [input num-days] 28 | (->> (parse-fish input) 29 | (nth-generation num-days) 30 | vals 31 | (apply +))) 32 | 33 | (defn day1 [input] (solve input 80)) 34 | (defn day2 [input] (solve input 256)) -------------------------------------------------------------------------------- /test/advent_2021_clojure/day12_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day12-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day12 :refer :all])) 4 | 5 | (def simple-test "start-A\nstart-b\nA-c\nA-b\nb-d\nA-end\nb-end") 6 | (def test-input "dc-end\nHN-start\nstart-kj\ndc-start\ndc-HN\nLN-dc\nHN-end\nkj-sa\nkj-HN\nkj-dc") 7 | (def large-test "fs-end\nhe-DX\nfs-he\nstart-DX\npj-DX\nend-zg\nzg-sl\nzg-pj\npj-he\nRW-he\nfs-DX\npj-RW\nzg-RW\nstart-pj\nhe-WI\nzg-he\npj-fs\nstart-RW") 8 | (def puzzle-input (slurp "resources/day12_data.txt")) 9 | 10 | (deftest part1-test 11 | (are [expected input] (= expected (part1 input)) 12 | 10 simple-test 13 | 19 test-input 14 | 226 large-test 15 | 4885 puzzle-input)) 16 | 17 | (deftest part2-test 18 | (are [expected input] (= expected (part2 input)) 19 | 36 simple-test 20 | 103 test-input 21 | 3509 large-test 22 | 117095 puzzle-input)) -------------------------------------------------------------------------------- /test/advent_2021_clojure/day13_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day13-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day13 :refer :all])) 4 | 5 | (def test-input (slurp "resources/day13_sample_data.txt")) 6 | (def puzzle-input (slurp "resources/day13_data.txt")) 7 | 8 | (deftest part1-test 9 | (are [expected input] (= expected (part1 input)) 10 | 17 test-input 11 | 684 puzzle-input)) 12 | 13 | (deftest fold-up-test 14 | ; Three lines, blank in the middle 15 | ; ##... ###.# 16 | ; ##### -> 17 | ; #.#.# 18 | (is (= (fold #{[0 0] [1 0] [0 2] [2 2] [4 2]} [:y 1]) 19 | #{[0 0] [1 0] [2 0] [4 0]})) 20 | 21 | ; Four lines 22 | ; ##... ##... 23 | ; .#.#. -> ####. 24 | ; ..... 25 | ; #.#.. 26 | (is (= (fold #{[0 0] [1 0] [1 1] [ 3 1] [0 3] [2 3] } [:y 2]) 27 | #{[0 0] [1 0] [0 1] [1 1] [2 1] [3 1]}))) 28 | 29 | (deftest part2-test 30 | (println "This is not a test case, but requires visual inspection") 31 | 32 | ; The output should look like JRZBLGKH 33 | (part2 puzzle-input)) 34 | -------------------------------------------------------------------------------- /test/advent_2021_clojure/day16_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day16-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day16 :refer :all])) 4 | 5 | (def puzzle-input (slurp "resources/day16_data.txt")) 6 | 7 | (deftest part1-test 8 | (are [expected input] (= expected (part1 input)) 9 | 16 "8A004A801A8002F478" 10 | 12 "620080001611562C8802118E34" 11 | 23 "C0015000016115A2E0802F182340" 12 | 31 "A0016C880162017C3686B18A3D4780" 13 | 883 puzzle-input)) 14 | 15 | (deftest part2-test 16 | (are [expected input] (= expected (part2 input)) 17 | 3 "C200B40A82" 18 | 54 "04005AC33890" 19 | 7 "880086C3E88112" 20 | 9 "CE00C43D881120" 21 | 1 "D8005AC2A8F0" 22 | 0 "F600BC2D8F" 23 | 0 "9C005AC2F8F0" 24 | 1 "9C0141080250320F1802104A08" 25 | 1675198555015 puzzle-input)) 26 | -------------------------------------------------------------------------------- /test/advent_2021_clojure/day04_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day04-test 2 | (:require 3 | [clojure.test :refer :all] 4 | [advent-2021-clojure.day04 :as day04] 5 | [advent-2021-clojure.day04-all-solo :as day04-solo])) 6 | 7 | (def test-input (slurp "resources/day04_sample_data.txt")) 8 | (def puzzle-input (slurp "resources/day04_data.txt")) 9 | 10 | (deftest part1-test 11 | (are [expected input] (= expected (day04/part1 input)) 12 | 4512 test-input 13 | 49686 puzzle-input)) 14 | 15 | (deftest part2-test 16 | (are [expected input] (= expected (day04/part2 input)) 17 | 1924 test-input 18 | 26878 puzzle-input)) 19 | 20 | (deftest part1-all-solo-test 21 | (are [expected input] (= expected (day04-solo/part1 input)) 22 | 4512 test-input 23 | 49686 puzzle-input)) 24 | 25 | (deftest part2-all-solo-test 26 | (are [expected input] (= expected (day04-solo/part2 input)) 27 | 1924 test-input 28 | 26878 puzzle-input)) -------------------------------------------------------------------------------- /test/advent_2021_clojure/day17_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day17-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day17 :refer :all])) 4 | 5 | (def test-input "target area: x=20..30, y=-10..-5") 6 | (def puzzle-input "target area: x=70..125, y=-159..-121") 7 | 8 | (deftest progress-test 9 | (let [target [10 20, -15 -5]] 10 | (are [expected point] (= expected (progress target point)) 11 | :hit [10 -5] 12 | :hit [19 -14] 13 | :hit [20 -15] 14 | :miss [21 -4] 15 | :miss [30 -5] 16 | :miss [5 -16] 17 | :miss [20 -20] 18 | :approaching [0 0] 19 | :approaching [10 0] 20 | :approaching [9 -15]))) 21 | 22 | (deftest part1-test 23 | (are [expected input] (= expected (part1 input)) 24 | 45 test-input 25 | 12561 puzzle-input)) 26 | 27 | (deftest part2-test 28 | (are [expected input] (= expected (part2 input)) 29 | 112 test-input 30 | 3785 puzzle-input)) 31 | -------------------------------------------------------------------------------- /src/advent_2021_clojure/day08_joepittsy.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day08-joepittsy 2 | (:require 3 | [advent-2021-clojure.day08 :refer [parse-input]] 4 | [advent-2021-clojure.utils :refer [parse-int]])) 5 | 6 | ; Assume we know the digits for 1 and 4 because they are obvious 7 | ; {[digit-length number-of-segments-in-one number-of-segments-in-four] digit} 8 | (def digit-finder {[6 2 3] 0 9 | [2 2 2] 1 10 | [5 1 2] 2 11 | [5 2 3] 3 12 | [4 2 4] 4 13 | [5 1 3] 5 14 | [6 1 3] 6 15 | [3 2 2] 7 16 | [7 2 4] 8 17 | [6 2 4] 9}) 18 | 19 | (defn digits [patterns outputs] 20 | (let [one (first (filter #(= 2 (count %)) patterns)) 21 | four (first (filter #(= 4 (count %)) patterns)) 22 | digit-keys (juxt count 23 | (comp count (partial set/intersection one)) 24 | (comp count (partial set/intersection four)))] 25 | (->> outputs 26 | (map (comp digit-finder digit-keys)) 27 | (apply str) 28 | parse-int))) 29 | 30 | (defn part2 [input] 31 | (->> (parse-input input) 32 | (map (partial apply digits)) 33 | (apply +))) -------------------------------------------------------------------------------- /test/advent_2021_clojure/day18_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day18-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day18 :refer :all])) 4 | 5 | (def puzzle-input (slurp "resources/day18_data.txt")) 6 | 7 | (deftest explode-tree-test 8 | (is (= (explode-tree [[[[[9,8],1],2],3],4]) 9 | [[[[0,9],2],3],4])) 10 | (is (= (explode-tree [7,[6,[5,[4,[3,2]]]]]) 11 | [7,[6,[5,[7,0]]]])) 12 | (is (= (explode-tree [[6,[5,[4,[3,2]]]],1]) 13 | [[6,[5,[7,0]]],3])) 14 | (is (= (explode-tree [[3,[2,[1,[7,3]]]],[6,[5,[4,[3,2]]]]]) 15 | [[3,[2,[8,0]]],[9,[5,[4,[3,2]]]]])) 16 | (is (= (explode-tree [[3,[2,[8,0]]],[9,[5,[4,[3,2]]]]]) 17 | [[3,[2,[8,0]]],[9,[5,[7,0]]]]))) 18 | 19 | (deftest split-tree-test 20 | (is (= (split-tree [[[[0,7],4],[15,[0,13]]],[1,1]]) 21 | [[[[0,7],4],[[7,8],[0,13]]],[1,1]])) 22 | (is (= (split-tree [[[[0,7],4],[[7,8],[0,13]]],[1,1]]) 23 | [[[[0,7],4],[[7,8],[0,[6,7]]]],[1,1]]))) 24 | 25 | (deftest part1-test 26 | (are [expected input] (= expected (part1 input)) 27 | ;;45 test-input 28 | 3574 puzzle-input)) 29 | 30 | (deftest part2-test 31 | (are [expected input] (= expected (part2 input)) 32 | 4763 puzzle-input)) 33 | -------------------------------------------------------------------------------- /resources/day16_data.txt: -------------------------------------------------------------------------------- 1 | 620D7B005DF2549DF6D51466E599E2BF1F60016A3F980293FFC16E802B325332544017CC951E3801A19A3C98A7FF5141004A48627F21A400C0C9344EBA4D9345D987A8393D43D000172329802F3F5DE753D56AB76009080350CE3B44810076274468946009E002AD2D9936CF8C4E2C472400732699E531EDDE0A4401F9CB9D7802F339DE253B00CCE77894EC084027D4EFFD006C00D50001098B708A340803118E0CC5C200A1E691E691E78EA719A642D400A913120098216D80292B08104DE598803A3B00465E7B8738812F3DC39C46965F82E60087802335CF4BFE219BA34CEC56C002EB9695BDAE573C1C87F2B49A3380273709D5327A1498C4F6968004EC3007E1844289240086C4D8D5174C01B8271CA2A880473F19F5024A5F1892EF4AA279007332980CA0090252919DEFA52978ED80804CA100622D463860200FC608FB0BC401888B09E2A1005B725FA25580213C392D69F9A1401891CD617EAF4A65F27BC5E6008580233405340D2BBD7B66A60094A2FE004D93F99B0CF5A52FF3994630086609A75B271DA457080307B005A68A6665F3F92E7A17010011966A350C92AA1CBD52A4E996293BEF5CBFC3480085994095007009A6A76DF136194E27CE34980212B0E6B3940B004C0010A8631E20D0803F0F21AC2020085B401694DA4491840D201237C0130004222BC3F8C2200DC7686B14A67432E0063801AC05C3080146005101362E0071010EC403E19801000340A801A002A118012C0200B006AC4015088018000B398019399C2FC432013E3003551006E304108AA000844718B51165F35FA89802F22D3801374CE3C3B2B8B5B7DDC11CC011C0090A6E92BF5226E92BF5327F3FD48750036898CC7DD9A14238DD373C6E70FBCA0096536673DC9C7770D98EE19A67308154B7BB799FC8CE61EE017CFDE2933FBE954C69864D1E5A1BCEC38C0179E5E98280 -------------------------------------------------------------------------------- /src/advent_2021_clojure/utils.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.utils 2 | (:require [clojure.string :as str])) 3 | 4 | (defn parse-int [v] (if (char? v) 5 | (Character/digit ^char v 10) 6 | (Integer/parseInt v))) 7 | 8 | (defn parse-binary [s] (Integer/parseInt s 2)) 9 | (defn binary-to-decimal [s] (Long/parseLong s 2)) 10 | 11 | (defn split-blank-line 12 | "Given an input string, returns a sequence of sub-strings, separated by a completely 13 | blank string. This function preserves any newlines between blank lines, and it filters 14 | out Windows' \"\r\" characters." 15 | [input] 16 | (-> (str/replace input "\r" "") 17 | (str/split #"\n\n"))) 18 | 19 | (defn abs [^long n] (Math/abs n)) 20 | 21 | (defn summation [n] 22 | (-> (* n (inc n)) 23 | (/ 2))) 24 | 25 | (defn char->int [c] (- (int c) 48)) 26 | 27 | (defn update-values 28 | "Thank you to Jay Fields' post for this awesome way to apply a function 29 | to every element of a map. 30 | http://blog.jayfields.com/2011/08/clojure-apply-function-to-each-value-of.html" 31 | [m f & args] 32 | (reduce (fn [r [k v]] (assoc r k (apply f v args))) {} m)) 33 | 34 | (defn lower-case? [s] (every? #(Character/isLowerCase ^char %) s)) 35 | (defn upper-case? [s] (every? #(Character/isUpperCase ^char %) s)) 36 | 37 | (defn update-add [m k v] (update m k #(+ (or % 0) v))) -------------------------------------------------------------------------------- /resources/day14_data.txt: -------------------------------------------------------------------------------- 1 | SVCHKVFKCSHVFNBKKPOC 2 | 3 | NC -> H 4 | PK -> V 5 | SO -> C 6 | PH -> F 7 | FP -> N 8 | PN -> B 9 | NP -> V 10 | NK -> S 11 | FV -> P 12 | SB -> S 13 | VN -> F 14 | SC -> H 15 | OB -> F 16 | ON -> O 17 | HN -> V 18 | HC -> F 19 | SN -> K 20 | CB -> H 21 | OP -> K 22 | HP -> H 23 | KS -> S 24 | BC -> S 25 | VB -> V 26 | FC -> B 27 | BH -> C 28 | HH -> O 29 | KH -> S 30 | VF -> F 31 | PF -> P 32 | VV -> F 33 | PP -> V 34 | BO -> H 35 | BF -> B 36 | PS -> K 37 | FO -> O 38 | KF -> O 39 | FN -> H 40 | CK -> B 41 | VP -> V 42 | HK -> F 43 | OV -> P 44 | CS -> V 45 | FF -> P 46 | OH -> N 47 | VS -> H 48 | VO -> O 49 | CP -> O 50 | KC -> V 51 | KV -> P 52 | BK -> B 53 | VK -> S 54 | NF -> V 55 | OO -> V 56 | FH -> H 57 | CN -> O 58 | SP -> B 59 | KN -> V 60 | OF -> H 61 | NV -> H 62 | FK -> B 63 | PV -> N 64 | NB -> B 65 | KK -> P 66 | VH -> P 67 | CC -> B 68 | HV -> V 69 | OC -> H 70 | PO -> V 71 | NO -> O 72 | BP -> C 73 | NH -> H 74 | BN -> O 75 | BV -> S 76 | CV -> B 77 | HS -> O 78 | NN -> S 79 | NS -> P 80 | KB -> F 81 | CO -> H 82 | HO -> P 83 | PB -> B 84 | BS -> P 85 | SH -> H 86 | FS -> V 87 | SF -> O 88 | OK -> F 89 | KP -> S 90 | BB -> C 91 | PC -> B 92 | OS -> C 93 | SV -> N 94 | SK -> K 95 | KO -> C 96 | SS -> V 97 | CF -> C 98 | HB -> K 99 | VC -> B 100 | CH -> P 101 | HF -> K 102 | FB -> V -------------------------------------------------------------------------------- /src/advent_2021_clojure/day14.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day14 2 | (:require [advent-2021-clojure.utils :as u])) 3 | 4 | (defn parse-input [input] 5 | (let [[template rules] (u/split-blank-line input)] 6 | [template 7 | (->> (re-seq #"\w+" rules) (partition 2) (map vec) (into {}))])) 8 | 9 | (defn update-add [m k n] (update m k #(+ (or % 0) n))) 10 | 11 | (defn apply-rules [rules freqs] 12 | (reduce (fn [acc [[a b :as word] n]] (let [c (rules word)] 13 | (-> acc 14 | (update-add (str a c) n) 15 | (update-add (str c b) n)))) 16 | {} freqs)) 17 | 18 | (defn score [initial-template freqs] 19 | (let [char-freqs (reduce (fn [acc [[a] n]] (update-add acc a n)) 20 | {(last initial-template) 1} 21 | freqs) 22 | sorted-instances (sort-by - (vals char-freqs))] 23 | (apply - ((juxt first last) sorted-instances)))) 24 | 25 | (defn solve [input step] 26 | (let [[template rules] (parse-input input) 27 | initial-freqs (->> template (partition 2 1) (map (partial apply str)) frequencies)] 28 | (->> (iterate (partial apply-rules rules) initial-freqs) 29 | (drop step) 30 | first 31 | (score template)))) 32 | 33 | (defn part1 [input] (solve input 10)) 34 | (defn part2 [input] (solve input 40)) 35 | -------------------------------------------------------------------------------- /src/advent_2021_clojure/day11.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day11 2 | (:require 3 | [advent-2021-clojure.point :as point] 4 | [advent-2021-clojure.utils :as utils])) 5 | 6 | (defn parse-grid [input] 7 | (->> (point/parse-to-char-coords input) 8 | (reduce (fn [acc [p c]] (assoc acc p (utils/char->int c))) {}))) 9 | 10 | (defn ready-to-flash? [v] (> v 9)) 11 | (def flashed? zero?) 12 | 13 | (defn- coordinates-where [f grid] (keep (fn [[k v]] (when (f v) k)) grid)) 14 | (defn coordinates-ready-to-flash [grid] (coordinates-where ready-to-flash? grid)) 15 | (defn coordinates-flashed [grid] (coordinates-where flashed? grid)) 16 | 17 | (defn increment-all [grid] (utils/update-values grid inc)) 18 | 19 | (defn cascade-flashes [grid] 20 | (if-some [p (->> grid coordinates-ready-to-flash first)] 21 | (recur (->> (point/surrounding p) 22 | (filter grid) 23 | (remove (comp flashed? grid)) 24 | (reduce #(update %1 %2 inc) (assoc grid p 0)))) 25 | grid)) 26 | 27 | (defn take-turn [grid] (-> grid increment-all cascade-flashes)) 28 | 29 | (defn octopus-flash-seq [input] 30 | (->> (parse-grid input) 31 | (iterate take-turn) 32 | (map (comp count coordinates-flashed)))) 33 | 34 | (defn part1 [input] 35 | (->> (octopus-flash-seq input) 36 | (take 101) 37 | (apply +))) 38 | 39 | (defn part2 [input] 40 | (->> (octopus-flash-seq input) 41 | (keep-indexed #(when (= 100 %2) %1)) 42 | first)) -------------------------------------------------------------------------------- /src/advent_2021_clojure/sorted_value_map.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.sorted-value-map 2 | (:refer-clojure :exclude [assoc merge-with] 3 | :rename {dissoc c-dissoc, first c-first})) 4 | 5 | (def empty-map {:key-to-value {}, :value-to-keys (sorted-map)}) 6 | (defn- value-of [svm k] (get-in svm [:key-to-value k])) 7 | (defn- keys-of [svm v] (get-in svm [:value-to-keys v])) 8 | 9 | (defn dissoc [svm k] 10 | (if-some [old-value (value-of svm k)] 11 | (if (= #{k} (keys-of svm old-value)) 12 | (-> svm 13 | (update :key-to-value c-dissoc k) 14 | (update :value-to-keys c-dissoc old-value)) 15 | (-> svm 16 | (update :key-to-value c-dissoc k) 17 | (update-in [:value-to-keys old-value] disj k))) 18 | svm)) 19 | 20 | (defn assoc [svm k v] 21 | (if (= v (value-of svm k)) 22 | svm 23 | (-> svm 24 | (dissoc k) 25 | (assoc-in [:key-to-value k] v) 26 | (update-in [:value-to-keys v] #(if % (conj % k) (sorted-set k)))))) 27 | 28 | (defn first [svm] 29 | (when-some [[v ks] (-> svm :value-to-keys c-first)] 30 | [(c-first ks) v])) 31 | 32 | (defn merge-with [f svm other-map] 33 | (reduce-kv (fn [acc k v] (if-some [curr-v (value-of svm k)] 34 | (if (= curr-v (f v curr-v)) 35 | acc 36 | (assoc acc k v)) 37 | (assoc acc k v))) 38 | svm 39 | other-map)) -------------------------------------------------------------------------------- /src/advent_2021_clojure/day02.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day02 2 | (:require [clojure.string :as str])) 3 | 4 | (def initial-submarine {:pos 0 :depth 0 :aim 0}) 5 | 6 | (defn parse-instruction [s] 7 | (let [[a b] (str/split s #" ")] 8 | [(keyword a) (Integer/parseInt b)])) 9 | 10 | (defn final-position [{:keys [pos depth]}] 11 | (* pos depth)) 12 | 13 | (defn create-mover [forward-fn down-fn up-fn] 14 | (fn [submarine [dir amount]] 15 | (let [op (dir {:forward forward-fn, :down down-fn, :up up-fn})] 16 | (op submarine amount)))) 17 | 18 | (def part1-mover (create-mover (fn [submarine amount] (update submarine :pos + amount)) 19 | (fn [submarine amount] (update submarine :depth + amount)) 20 | (fn [submarine amount] (update submarine :depth - amount)))) 21 | (def part2-mover (create-mover (fn [{aim :aim :as submarine} amount] (-> (update submarine :pos + amount) 22 | (update :depth + (* aim amount)))) 23 | (fn [submarine amount] (update submarine :aim + amount)) 24 | (fn [submarine amount] (update submarine :aim - amount)))) 25 | 26 | (defn solve [mover input] 27 | (->> (str/split-lines input) 28 | (map parse-instruction) 29 | (reduce mover initial-submarine) 30 | final-position)) 31 | 32 | (defn part1 [input] (solve part1-mover input)) 33 | (defn part2 [input] (solve part2-mover input)) -------------------------------------------------------------------------------- /src/advent_2021_clojure/day10.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day10 2 | (:require [clojure.string :as str])) 3 | 4 | (def close-delimiters {\( \), \[ \], \{ \}, \< \>}) 5 | (def delimiters (set (keys close-delimiters))) 6 | (def corrupt-char-points {\) 3, \] 57, \} 1197, \> 25137}) 7 | (def autocomplete-char-points {\) 1, \] 2, \} 3, \> 4}) 8 | 9 | (defn process-line [line] 10 | (loop [[c & xc] line, [s & xs :as stack] ()] 11 | (if c 12 | (cond (delimiters c) (recur xc (conj stack (close-delimiters c))) ;push 13 | (= c s) (recur xc xs) ; pop 14 | :else [:corrupt c]) 15 | [:incomplete stack]))) 16 | 17 | (defn- error-chars [error line] 18 | (let [[status c] (process-line line)] 19 | (when (= status error) c))) 20 | 21 | (defn first-corrupt-char [line] (error-chars :corrupt line)) 22 | (defn missing-chars [line] (error-chars :incomplete line)) 23 | 24 | (defn part1 [input] 25 | (->> (str/split-lines input) 26 | (keep first-corrupt-char) 27 | (map corrupt-char-points) 28 | (apply +))) 29 | 30 | (defn incomplete-char-bounty [chars] 31 | (reduce (fn [acc c] (+ (* acc 5) 32 | (autocomplete-char-points c))) 33 | 0 34 | chars)) 35 | 36 | (defn middle [coll] 37 | (nth coll (/ (count coll) 2))) 38 | 39 | (defn part2 [input] 40 | (->> (str/split-lines input) 41 | (keep missing-chars) 42 | (map incomplete-char-bounty) 43 | sort 44 | middle)) -------------------------------------------------------------------------------- /src/advent_2021_clojure/day25.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day25 2 | (:require [advent-2021-clojure.point :as point])) 3 | 4 | (defn parse-map [input] 5 | (let [points (point/parse-to-char-coords input) 6 | max-x (apply max (map ffirst points)) 7 | max-y (apply max (map (comp second first) points))] 8 | (reduce (fn [acc [coords c]] (case c 9 | \> (update acc :right conj coords) 10 | \v (update acc :down conj coords) 11 | \. acc)) 12 | {:right #{}, :down #{}, :max-x max-x, :max-y max-y} 13 | points))) 14 | 15 | (defmulti target-from (fn [_ _ dir] dir)) 16 | (defmethod target-from :right [m [x y] _] (if (>= x (:max-x m)) [0 y] [(inc x) y])) 17 | (defmethod target-from :down [m [x y] _] (if (>= y (:max-y m)) [x 0] [x (inc y)])) 18 | 19 | (defn move-all-in-direction [m dir] 20 | (reduce (fn [acc coords] 21 | (let [target (target-from m coords dir)] 22 | (if ((some-fn (:right m) (:down m)) target) 23 | acc 24 | (-> acc 25 | (update dir conj target) 26 | (update dir disj coords))))) 27 | m 28 | (dir m))) 29 | 30 | (defn take-turn [m] 31 | (reduce move-all-in-direction m [:right :down])) 32 | 33 | (defn sea-cucumber-seq [m] 34 | (let [next-map (take-turn m)] 35 | (if (= m next-map) 36 | (lazy-seq [m]) 37 | (lazy-seq (cons m (sea-cucumber-seq next-map)))))) 38 | 39 | (defn part1 [input] (->> input parse-map sea-cucumber-seq count)) 40 | -------------------------------------------------------------------------------- /src/advent_2021_clojure/day13.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day13 2 | (:require 3 | [advent-2021-clojure.utils :as utils :refer [parse-int]] 4 | [clojure.string :as str])) 5 | 6 | (defn parse-instruction [line] 7 | (let [[_ axis amount] (re-matches #"fold along ([xy])=(\d+)" line)] 8 | [(keyword axis) (parse-int amount)])) 9 | 10 | (defn parse-paper [input] 11 | (let [[dots folds] (utils/split-blank-line input)] 12 | [(->> (str/split-lines dots) 13 | (map #(mapv parse-int (str/split % #","))) 14 | set) 15 | (map parse-instruction (str/split-lines folds))])) 16 | 17 | (defn fold [dots [dir fold-line]] 18 | (let [coord-idx ({:x 0 :y 1} dir)] 19 | (->> dots 20 | (map (fn [dot] 21 | (let [v (dot coord-idx)] 22 | (if (<= v fold-line) 23 | dot 24 | (update dot coord-idx (partial - (* 2 fold-line))))))) 25 | set))) 26 | 27 | (defn part1 [input] 28 | (let [[dots [instruction]] (parse-paper input)] 29 | (count (fold dots instruction)))) 30 | 31 | (defn print-dots [dots] 32 | (let [min-x (apply min (map first dots)) 33 | min-y (apply min (map second dots)) 34 | max-x (apply max (map first dots)) 35 | max-y (apply max (map second dots))] 36 | (run! println (map (fn [y] 37 | (apply str (map #(if (dots [% y]) \# \space) 38 | (range min-x (inc max-x))))) 39 | (range min-y (inc max-y)))))) 40 | 41 | (defn part2 [input] 42 | (let [[dots instructions] (parse-paper input)] 43 | (print-dots (reduce fold dots instructions)))) -------------------------------------------------------------------------------- /test/advent_2021_clojure/day21_test.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day21-test 2 | (:require [clojure.test :refer :all] 3 | [advent-2021-clojure.day21 :refer :all])) 4 | 5 | (def test-input [4 8]) 6 | (def puzzle-input [10 8]) 7 | 8 | (deftest part1-test 9 | (are [expected players] (= expected (part1 (first players) (second players))) 10 | 739785 test-input 11 | 752247 puzzle-input)) 12 | 13 | (deftest board-pos-test 14 | (are [expected n] (= expected (board-pos n)) 15 | 1 1 16 | 2 2 17 | 9 9 18 | 10 10 19 | 1 11 20 | 9 19 21 | 10 20 22 | 1 21)) 23 | 24 | (deftest move-player-test 25 | (is (= (move-player {:players [{:pos 1, :score 3} {:pos 2, :score 4}], :next-player 0} 5) 26 | {:players [{:pos 6, :score 9} {:pos 2, :score 4}], :next-player 1})) 27 | (is (= (move-player {:players [{:pos 1, :score 3} {:pos 2, :score 4}], :next-player 1} 5) 28 | {:players [{:pos 1, :score 3} {:pos 7, :score 11}], :next-player 0}))) 29 | 30 | (deftest winner-test 31 | (is (nil? (winner {:players [{:pos 1, :score 3} {:pos 2, :score 4}], :next-player 0} 5))) 32 | (is (nil? (winner {:players [{:pos 1, :score 3} {:pos 2, :score 4}], :next-player 1} 5))) 33 | (is (= 0 (winner {:players [{:pos 1, :score 3} {:pos 2, :score 2}], :next-player 0} 3))) 34 | (is (= 1 (winner {:players [{:pos 1, :score 3} {:pos 2, :score 4}], :next-player 0} 4)))) 35 | 36 | (deftest part2-test 37 | (are [expected players] (= expected (part2 (first players) (second players))) 38 | 444356092776315 test-input 39 | 221109915584112 puzzle-input)) 40 | -------------------------------------------------------------------------------- /src/advent_2021_clojure/day09.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day09 2 | (:require 3 | [advent-2021-clojure.point :as point] 4 | [advent-2021-clojure.utils :as utils :refer [parse-int]] 5 | [clojure.string :as str])) 6 | 7 | (defn parse-cave [input] (->> (str/split-lines input) 8 | (mapv (partial mapv utils/char->int)))) 9 | 10 | (defn lowest-point? [cave point] 11 | (let [height (get-in cave point)] 12 | (->> (point/neighbors point) 13 | (keep (partial get-in cave)) 14 | (every? (partial < height))))) 15 | 16 | (defn all-coords [cave] (for [y (-> cave count range) 17 | x (-> cave first count range)] 18 | [y x])) 19 | 20 | (defn lowest-points [cave] 21 | (filter (partial lowest-point? cave) (all-coords cave))) 22 | 23 | (defn part1 [input] 24 | (let [cave (parse-cave input)] 25 | (->> (lowest-points cave) 26 | (map (partial get-in cave)) 27 | (map inc) 28 | (apply +)))) 29 | 30 | (def max-height 9) 31 | (defn high-point? [cave point] (= max-height (get-in cave point))) 32 | 33 | (defn basin-around [cave lowest-point] 34 | (loop [candidates #{lowest-point}, found #{}] 35 | (if-some [point (first candidates)] 36 | (recur (reduce conj (rest candidates) (->> (point/neighbors point) 37 | (filter (partial get-in cave)) 38 | (remove (some-fn found (partial high-point? cave))))) 39 | (conj found point)) 40 | found))) 41 | 42 | (defn part2 [input] 43 | (let [cave (parse-cave input)] 44 | (->> (lowest-points cave) 45 | (map (comp count (partial basin-around cave))) 46 | (sort-by -) 47 | (take 3) 48 | (apply *)))) -------------------------------------------------------------------------------- /src/advent_2021_clojure/day03.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day03 2 | (:require 3 | [advent-2021-clojure.utils :refer [parse-binary]] 4 | [clojure.string :as str])) 5 | 6 | (defn parse-nums [input] (->> input str/split-lines (map parse-binary))) 7 | (defn range-down-from [n] (range n -1 -1)) 8 | 9 | (defn most-significant-bit [nums] 10 | (let [n (apply max nums)] 11 | (int (/ (Math/log n) (Math/log 2))))) 12 | 13 | (defn bit-diff [nums bit] 14 | (reduce #(if (bit-test %2 bit) (inc %1) (dec %1)) 15 | 0 16 | nums)) 17 | 18 | (defn most-common-bit [nums bit] (if (neg-int? (bit-diff nums bit)) 0 1)) 19 | (defn least-common-bit [nums bit] (if (neg-int? (bit-diff nums bit)) 1 0)) 20 | 21 | (defn one-pass-bit-check [nums bit-fn] 22 | (->> (range-down-from (most-significant-bit nums)) 23 | (map (partial bit-fn nums)) 24 | (apply str) 25 | parse-binary)) 26 | 27 | (defn multi-pass-bit-check [nums bit-fn] 28 | (first (reduce (fn [remaining bit] (if (= 1 (count remaining)) 29 | (reduced remaining) 30 | (let [test (= 1 (bit-fn remaining bit))] 31 | (filter #(= test (bit-test % bit)) remaining)))) 32 | nums 33 | (range-down-from (most-significant-bit nums))))) 34 | 35 | (defn gamma-rate [nums] (one-pass-bit-check nums most-common-bit)) 36 | (defn epsilon-rate [nums] (one-pass-bit-check nums least-common-bit)) 37 | (defn oxygen-generator-rating [nums] (multi-pass-bit-check nums most-common-bit)) 38 | (defn co2-scrubber-rating [nums] (multi-pass-bit-check nums least-common-bit)) 39 | 40 | (defn multiply-rates [input & rate-fns] 41 | (->> (parse-nums input) 42 | ((apply juxt rate-fns)) 43 | (apply *))) 44 | 45 | (defn part1 [input] (multiply-rates input gamma-rate epsilon-rate)) 46 | (defn part2 [input] (multiply-rates input oxygen-generator-rating co2-scrubber-rating)) 47 | -------------------------------------------------------------------------------- /src/advent_2021_clojure/day15.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day15 2 | (:require [advent-2021-clojure.utils :refer [parse-int]] 3 | [advent-2021-clojure.point :as point] 4 | [advent-2021-clojure.sorted-value-map :as svm])) 5 | 6 | (defn parse-cave [input] 7 | (reduce (fn [acc [point c]] (assoc acc point (parse-int c))) 8 | {} 9 | (point/parse-to-char-coords input))) 10 | 11 | (def cave-risk 12 | (memoize (fn [n] (nth (cons 0 (cycle (range 1 10))) n)))) 13 | 14 | (defn multiply-cave [cave n] 15 | (let [length (inc (apply max (map ffirst cave)))] 16 | (apply merge (for [grid-x (range 0 n) :let [x-offset (* grid-x length)] 17 | grid-y (range 0 n) :let [y-offset (* grid-y length) 18 | p-offset [x-offset y-offset] 19 | n-offset (+ grid-x grid-y)]] 20 | (reduce (fn [acc [p n]] (assoc acc (mapv + p p-offset) 21 | (cave-risk (+ n n-offset)))) 22 | {} cave))))) 23 | 24 | (defn shortest-path [cave] 25 | (let [cave-length (apply max (map ffirst cave)) 26 | target [cave-length cave-length]] 27 | (loop [candidates (svm/assoc svm/empty-map point/origin 0), seen #{}] 28 | (let [[point cost] (svm/first candidates) 29 | next-options (->> (point/neighbors point) 30 | (filter cave) 31 | (remove seen) 32 | (map #(vector % (+ cost (cave %)))) 33 | (into {}))] 34 | (if (= point target) 35 | cost 36 | (recur (svm/merge-with min (svm/dissoc candidates point) next-options) 37 | (conj seen point))))))) 38 | 39 | (defn solve [num-multiples input] 40 | (-> input parse-cave (multiply-cave num-multiples) shortest-path)) 41 | 42 | (defn part1 [input] (solve 1 input)) 43 | (defn part2 [input] (solve 5 input)) 44 | -------------------------------------------------------------------------------- /src/advent_2021_clojure/point.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.point 2 | (:require [clojure.string :as str])) 3 | 4 | (def origin [0 0]) 5 | 6 | (defn parse-to-char-coords 7 | "Given an input string, returns a lazy sequence of [[x y] c] tuples of [x y] coords to each character c." 8 | [input] 9 | (->> (str/split-lines input) 10 | (map-indexed (fn [y line] 11 | (map-indexed (fn [x c] [[x y] c]) line))) 12 | (apply concat))) 13 | 14 | (defn inclusive-distance [[x1 y1] [x2 y2]] 15 | (letfn [(local-dist [^long v1 ^long v2] (Math/abs (- v1 v2)))] 16 | (inc (max (local-dist x1 x2) 17 | (local-dist y1 y2))))) 18 | 19 | (defn infinite-points-from [[x1 y1] [x2 y2]] 20 | (letfn [(ordinate-fn [v1 v2] (cond (< v1 v2) inc 21 | (> v1 v2) dec 22 | :else identity))] 23 | (let [x-fn (ordinate-fn x1 x2) 24 | y-fn (ordinate-fn y1 y2)] 25 | (map vector (iterate x-fn x1) (iterate y-fn y1))))) 26 | 27 | (defn inclusive-line-between 28 | ([[point1 point2]] 29 | (inclusive-line-between point1 point2)) 30 | 31 | ([point1 point2] 32 | (take (inclusive-distance point1 point2) (infinite-points-from point1 point2)))) 33 | 34 | (defn horizontal-line? 35 | ([[point1 point2]] (horizontal-line? point1 point2)) 36 | ([[_ y1] [_ y2]] (= y1 y2))) 37 | 38 | (defn vertical-line? 39 | ([[point1 point2]] (vertical-line? point1 point2)) 40 | ([[x1 _] [x2 _]] (= x1 x2))) 41 | 42 | (defn neighbors [point] 43 | (map (partial mapv + point) [[0 1] [0 -1] [-1 0] [1 0]])) 44 | 45 | (defn surrounding 46 | ([point] (surrounding false point)) 47 | ([include-self? point] (let [points (if include-self? [[-1 -1] [0 -1] [1 -1] [-1 0] [0 0] [1 0] [-1 1] [0 1] [1 1]] 48 | [[-1 -1] [-1 0] [-1 1] [0 -1] [0 1] [1 -1] [1 0] [1 1]])] 49 | (map (partial mapv + point) points)))) 50 | 51 | (defn perimeter-points [[x0 y0] [x1 y1]] 52 | (concat (for [x [x0 x1], y (range y0 (inc y1))] [x y]) 53 | (for [y [y0 y1], x (range (inc x0) x1)] [x y]))) -------------------------------------------------------------------------------- /src/advent_2021_clojure/day20.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day20 2 | (:require 3 | [advent-2021-clojure.point :as point] 4 | [advent-2021-clojure.utils :as utils])) 5 | 6 | (def dark-pixel 0) 7 | (def light-pixel 1) 8 | 9 | (defn parse-input [input] 10 | (let [pixel-map {\. dark-pixel, \# light-pixel} 11 | [alg image] (utils/split-blank-line input)] 12 | [(mapv pixel-map alg) 13 | (reduce (fn [m [coord c]] (assoc m coord (pixel-map c))) 14 | {} 15 | (point/parse-to-char-coords image))])) 16 | 17 | (defn border-seq [alg] 18 | (let [block-of {dark-pixel 0, light-pixel 511}] 19 | (iterate (comp alg block-of) dark-pixel))) 20 | 21 | (defn next-value-at [alg border image coords] 22 | (->> (point/surrounding true coords) 23 | (map #(or (image %) border)) 24 | (apply str) 25 | utils/parse-binary 26 | alg)) 27 | 28 | (defn migrate-image [alg border image] 29 | (reduce-kv (fn [m coord _] (assoc m coord (next-value-at alg border image coord))) 30 | {} image)) 31 | 32 | (defn expand-image [image border] 33 | (let [min-max (juxt (partial apply min) (partial apply max)) 34 | [min-x max-x] (min-max (map ffirst image)) 35 | [min-y max-y] (min-max (map (comp second first) image))] 36 | (reduce #(assoc %1 %2 border) 37 | image 38 | (point/perimeter-points [(dec min-x) (dec min-y)] 39 | [(inc max-x) (inc max-y)])))) 40 | 41 | (defn image-seq 42 | ([alg image] (image-seq alg image (border-seq alg))) 43 | ([alg image borders] (let [border (first borders) 44 | image' (->> (expand-image image border) 45 | (migrate-image alg border))] 46 | (lazy-seq (cons image' (image-seq alg image' (rest borders))))))) 47 | 48 | (defn solve [input enhance-count] 49 | (->> (parse-input input) 50 | (apply image-seq) 51 | (drop (dec enhance-count)) 52 | first 53 | (filter #(= light-pixel (second %))) 54 | count)) 55 | 56 | (defn part1 [input] (solve input 2)) 57 | (defn part2 [input] (solve input 50)) 58 | -------------------------------------------------------------------------------- /src/advent_2021_clojure/day24.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day24 2 | (:require [clojure.string :as str])) 3 | 4 | ; I am not proud of this code, since I was not even slightly interested in the puzzle. 5 | ; Please read the original source code that I simply ported into Clojure. 6 | ; https://todd.ginsberg.com/post/advent-of-code/2021/day24/ 7 | 8 | (defn parse-digit-instruction [lines] 9 | (letfn [(last-number [n] (->> (lines n) (re-seq #"-?\d+") last read-string))] 10 | (map last-number [4 5 15]))) 11 | 12 | (defn parse-input [input] 13 | (->> (str/split-lines input) 14 | (partition 18) 15 | (map (comp parse-digit-instruction vec)))) 16 | 17 | (defn magic-function [[a b c] z w] 18 | (if (not= (-> z (mod 26) (+ b)) w) 19 | (-> z (quot a) (* 26) (+ w c)) 20 | (quot z a))) 21 | 22 | (defn run-instruction-digit [z-values-this-round [a b c :as instruction] z min-max digit] 23 | (let [new-value-for-z (magic-function instruction z digit)] 24 | (if (or (= a 1) 25 | (and (= a 26) (< new-value-for-z z))) 26 | (assoc z-values-this-round new-value-for-z [(min (get-in z-values-this-round [new-value-for-z 0] Long/MAX_VALUE) 27 | (-> (first min-max) (* 10) (+ digit))) 28 | (max (get-in z-values-this-round [new-value-for-z 1] Long/MIN_VALUE) 29 | (-> (second min-max) (* 10) (+ digit)))]) 30 | z-values-this-round))) 31 | 32 | (defn run-instruction [z-values instruction] 33 | (reduce (fn [z-values-this-round [z min-max]] 34 | (reduce (fn [zvtr digit] (run-instruction-digit zvtr instruction z min-max digit)) 35 | z-values-this-round 36 | (range 1 10))) 37 | {} 38 | z-values)) 39 | 40 | (defn solve [input] 41 | (let [result-map (reduce (fn [z-values instruction] (run-instruction z-values instruction)) 42 | {0 [0 0]} 43 | (parse-input input))] 44 | (get result-map 0))) 45 | 46 | (defn part1 [input] (-> input solve second)) 47 | (defn part2 [input] (-> input solve first)) 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Advent of Code 2021 2 | 3 | | Day # | Source | Blog Post | 4 | | ----- | ------ | --------- | 5 | | 1 | [source](src/advent_2021_clojure/day01.clj)
[windowed source](src/advent_2021_clojure/day01-windowed.clj) | [blog](docs/day01.md) | 6 | | 2 | [source](src/advent_2021_clojure/day02.clj) | [blog](docs/day02.md) | 7 | | 3 | [source](src/advent_2021_clojure/day03.clj) | [blog](docs/day03.md) | 8 | | 4 | [source](src/advent_2021_clojure/day04.clj) | [blog](docs/day04.md) | 9 | | 5 | [source](src/advent_2021_clojure/day05.clj) | [blog](docs/day05.md) | 10 | | 6 | [source](src/advent_2021_clojure/day06.clj) | [blog](docs/day06.md) | 11 | | 7 | [source](src/advent_2021_clojure/day07.clj) | [blog](docs/day07.md) | 12 | | 8 | [source](src/advent_2021_clojure/day08.clj) | [blog](docs/day08.md) | 13 | | 9 | [source](src/advent_2021_clojure/day09.clj) | [blog](docs/day09.md) | 14 | | 10 | [source](src/advent_2021_clojure/day10.clj) | [blog](docs/day10.md) | 15 | | 11 | [source](src/advent_2021_clojure/day11.clj) | [blog](docs/day11.md) | 16 | | 12 | [source](src/advent_2021_clojure/day12.clj) | [blog](docs/day12.md) | 17 | | 13 | [source](src/advent_2021_clojure/day13.clj) | [blog](docs/day13.md) | 18 | | 14 | [source](src/advent_2021_clojure/day14.clj) | [blog](docs/day14.md) | 19 | | 15 | [source](src/advent_2021_clojure/day15.clj) | [blog](docs/day15.md) | 20 | | 16 | [source](src/advent_2021_clojure/day16.clj) | [blog](docs/day16.md) | 21 | | 17 | [source](src/advent_2021_clojure/day17.clj) | [blog](docs/day17.md) | 22 | | 18 | [source](src/advent_2021_clojure/day18.clj) | [blog](docs/day18.md) | 23 | | 19 | [source](src/advent_2021_clojure/day19.clj) | [blog](docs/day19.md) | 24 | | 20 | [source](src/advent_2021_clojure/day20.clj) | [blog](docs/day20.md) | 25 | | 21 | [source](src/advent_2021_clojure/day21.clj) | [blog](docs/day21.md) | 26 | | 22 | [source](src/advent_2021_clojure/day22.clj) | [blog](docs/day22.md) | 27 | | 23 | [source](src/advent_2021_clojure/day23.clj) | [blog](docs/day23.md) | 28 | | 24 | [source](src/advent_2021_clojure/day24.clj) | [blog](docs/day24.md) | 29 | | 25 | [source](src/advent_2021_clojure/day25.clj) | [blog](docs/day25.md) | 30 | -------------------------------------------------------------------------------- /src/advent_2021_clojure/day08.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day08 2 | (:require [advent-2021-clojure.utils :as utils :refer [parse-int]] 3 | [clojure.set :as set] 4 | [clojure.string :as str])) 5 | 6 | (defn parse-component [component] 7 | (map set (str/split component #" "))) 8 | 9 | (defn parse-line [line] 10 | (->> (str/split line #"\|") 11 | (map (comp parse-component str/trim)))) 12 | 13 | (defn parse-input [input] 14 | (map parse-line (str/split-lines input))) 15 | 16 | (def unique-lengths #{2 3 4 7}) 17 | (defn part1 [input] 18 | (->> (parse-input input) 19 | (mapcat second) 20 | (filter #(-> % count unique-lengths)) 21 | count)) 22 | 23 | (defn identify-signals [signal-patterns] 24 | (letfn [(only-subset [s coll] (first (filter (partial set/subset? s) coll))) 25 | (only-superset [s coll] (first (filter (partial set/superset? s) coll))) 26 | (only-not-subset [s coll] (first (remove (partial set/subset? s) coll))) 27 | (leftover [vals coll] (first (remove vals coll)))] 28 | (let [patterns (group-by count signal-patterns) 29 | ; Unique lengths 30 | one (first (patterns 2)) 31 | four (first (patterns 4)) 32 | seven (first (patterns 3)) 33 | eight (first (patterns 7)) 34 | 35 | ; 0, 6, and 9 all have length of 6 36 | zero-six-nine (patterns 6) 37 | six (only-not-subset one zero-six-nine) 38 | nine (only-subset four zero-six-nine) 39 | zero (leftover #{six nine} zero-six-nine) 40 | 41 | ; 2, 3, and 5 all have length of 5 42 | two-three-five (patterns 5) 43 | five (only-superset six two-three-five) 44 | three (only-subset one two-three-five) 45 | two (leftover #{three five} two-three-five)] 46 | {zero 0, one 1, two 2, three 3, four 4, five 5, six 6, seven 7, eight 8, nine 9}))) 47 | 48 | (defn find-digits [signals outputs] 49 | (->> (map signals outputs) 50 | (apply str) 51 | (parse-int))) 52 | 53 | (defn part2 [input] 54 | (->> (parse-input input) 55 | (map (fn [[patterns outputs]] (-> (identify-signals patterns) 56 | (find-digits outputs)))) 57 | (apply +))) -------------------------------------------------------------------------------- /src/advent_2021_clojure/day12.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day12 2 | (:require 3 | [advent-2021-clojure.utils :as utils] 4 | [clojure.string :as str])) 5 | 6 | (def start-cave "start") 7 | (def end-cave "end") 8 | 9 | (defn parse-connections [input] 10 | (let [every-path (->> (str/split-lines input) 11 | (map #(str/split % #"-")) 12 | (reduce (fn [acc [from to]] (-> acc 13 | (update from conj to) 14 | (update to conj from))) {}))] 15 | (utils/update-values every-path #(remove (partial = start-cave) %)))) 16 | 17 | (defn start-cave? [cave] (= start-cave cave)) 18 | (defn end-cave? [cave] (= end-cave cave)) 19 | (defn intermediate-cave? [cave] ((complement (some-fn start-cave? end-cave?)) cave)) 20 | (defn small-cave? [cave] ((every-pred intermediate-cave? utils/lower-case?) cave)) 21 | (defn big-cave? [cave] ((every-pred intermediate-cave? utils/upper-case?) cave)) 22 | 23 | (defn has-repeats? [seen] (some (partial <= 2) (vals seen))) 24 | 25 | (defn approachable? [allow-repeat-visit? seen cave] 26 | (or ((some-fn end-cave? big-cave? (complement seen)) cave) 27 | (and allow-repeat-visit? (not (has-repeats? seen))))) 28 | 29 | (defn find-paths 30 | ([allow-repeat-visit? connections] 31 | (find-paths allow-repeat-visit? connections [start-cave] {})) 32 | 33 | ([allow-repeat-visit? connections path small-caves-seen] 34 | (let [cave (last path)] 35 | (if (end-cave? cave) 36 | [path] 37 | (->> (connections cave) 38 | (filter (partial approachable? allow-repeat-visit? small-caves-seen)) 39 | (mapcat (fn [cave] 40 | (find-paths allow-repeat-visit? 41 | connections 42 | (conj path cave) 43 | (if (small-cave? cave) 44 | (update small-caves-seen cave #(inc (or % 0))) 45 | small-caves-seen))))))))) 46 | 47 | (defn part1 [input] (->> input parse-connections (find-paths false) count)) 48 | (defn part2 [input] (->> input parse-connections (find-paths true) count)) 49 | -------------------------------------------------------------------------------- /resources/day19_sample_data.txt: -------------------------------------------------------------------------------- 1 | --- scanner 0 --- 2 | 404,-588,-901 3 | 528,-643,409 4 | -838,591,734 5 | 390,-675,-793 6 | -537,-823,-458 7 | -485,-357,347 8 | -345,-311,381 9 | -661,-816,-575 10 | -876,649,763 11 | -618,-824,-621 12 | 553,345,-567 13 | 474,580,667 14 | -447,-329,318 15 | -584,868,-557 16 | 544,-627,-890 17 | 564,392,-477 18 | 455,729,728 19 | -892,524,684 20 | -689,845,-530 21 | 423,-701,434 22 | 7,-33,-71 23 | 630,319,-379 24 | 443,580,662 25 | -789,900,-551 26 | 459,-707,401 27 | 28 | --- scanner 1 --- 29 | 686,422,578 30 | 605,423,415 31 | 515,917,-361 32 | -336,658,858 33 | 95,138,22 34 | -476,619,847 35 | -340,-569,-846 36 | 567,-361,727 37 | -460,603,-452 38 | 669,-402,600 39 | 729,430,532 40 | -500,-761,534 41 | -322,571,750 42 | -466,-666,-811 43 | -429,-592,574 44 | -355,545,-477 45 | 703,-491,-529 46 | -328,-685,520 47 | 413,935,-424 48 | -391,539,-444 49 | 586,-435,557 50 | -364,-763,-893 51 | 807,-499,-711 52 | 755,-354,-619 53 | 553,889,-390 54 | 55 | --- scanner 2 --- 56 | 649,640,665 57 | 682,-795,504 58 | -784,533,-524 59 | -644,584,-595 60 | -588,-843,648 61 | -30,6,44 62 | -674,560,763 63 | 500,723,-460 64 | 609,671,-379 65 | -555,-800,653 66 | -675,-892,-343 67 | 697,-426,-610 68 | 578,704,681 69 | 493,664,-388 70 | -671,-858,530 71 | -667,343,800 72 | 571,-461,-707 73 | -138,-166,112 74 | -889,563,-600 75 | 646,-828,498 76 | 640,759,510 77 | -630,509,768 78 | -681,-892,-333 79 | 673,-379,-804 80 | -742,-814,-386 81 | 577,-820,562 82 | 83 | --- scanner 3 --- 84 | -589,542,597 85 | 605,-692,669 86 | -500,565,-823 87 | -660,373,557 88 | -458,-679,-417 89 | -488,449,543 90 | -626,468,-788 91 | 338,-750,-386 92 | 528,-832,-391 93 | 562,-778,733 94 | -938,-730,414 95 | 543,643,-506 96 | -524,371,-870 97 | 407,773,750 98 | -104,29,83 99 | 378,-903,-323 100 | -778,-728,485 101 | 426,699,580 102 | -438,-605,-362 103 | -469,-447,-387 104 | 509,732,623 105 | 647,635,-688 106 | -868,-804,481 107 | 614,-800,639 108 | 595,780,-596 109 | 110 | --- scanner 4 --- 111 | 727,592,562 112 | -293,-554,779 113 | 441,611,-461 114 | -714,465,-776 115 | -743,427,-804 116 | -660,-479,-426 117 | 832,-632,460 118 | 927,-485,-438 119 | 408,393,-506 120 | 466,436,-512 121 | 110,16,151 122 | -258,-428,682 123 | -393,719,612 124 | -211,-452,876 125 | 808,-476,-593 126 | -575,615,604 127 | -485,667,467 128 | -680,325,-822 129 | -627,-443,-432 130 | 872,-547,-609 131 | 833,512,582 132 | 807,604,487 133 | 839,-516,451 134 | 891,-625,532 135 | -652,-548,-490 136 | 30,-46,-14 -------------------------------------------------------------------------------- /src/advent_2021_clojure/day17.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day17 2 | (:require [advent-2021-clojure.utils :refer [parse-int]])) 3 | 4 | (defn parse-input [input] 5 | (->> input 6 | (re-seq #"target area: x=(.*)\.\.(.*), y=(.*)\.\.(.*)") 7 | first rest (map parse-int) vec)) 8 | 9 | (defn progress [target point] 10 | {:pre [(every? neg-int? (drop 2 target)) 11 | (< (target 2) (target 3))]} 12 | (let [[tx1 tx2 ty1 ty2] target 13 | [px py] point] 14 | (cond 15 | (and (<= tx1 px tx2) (<= ty1 py ty2)) :hit 16 | (or (< tx2 px) (< py ty1)) :miss 17 | :else :approaching))) 18 | 19 | (defn trajectory-from-origin [[dx0 dy0]] 20 | (->> [0 0 dx0 dy0] 21 | (iterate (fn [[x y dx dy]] [(+ x dx) 22 | (+ y dy) 23 | (max (dec dx) 0) 24 | (dec dy)])) 25 | (map (fn [[x y]] [x y])))) 26 | 27 | (defn hits-target? [target trajectory] 28 | (->> (map #(progress target %) trajectory) 29 | (drop-while #(= % :approaching)) 30 | (first) 31 | (= :hit))) 32 | 33 | (defn x-values [dx] (->> (trajectory-from-origin [dx 0]) 34 | (map first) 35 | (partition 2 1) 36 | (take-while #(apply < %)) 37 | (map second))) 38 | 39 | (defn y-values [dy] (->> (trajectory-from-origin [0 dy]) 40 | (map second) 41 | rest)) 42 | 43 | (defn possible-dx0 [target] 44 | (let [[min-x max-x] target 45 | all-target-x (set (range min-x (inc max-x)))] 46 | (filter #(some all-target-x (x-values %)) 47 | (range 1 (inc max-x))))) 48 | 49 | (defn possible-dy0 [target] 50 | (let [[_ _ min-y max-y] target 51 | all-target-y (set (range min-y (inc max-y)))] 52 | (->> (range min-y (- min-y)) 53 | (filter (fn [dy] 54 | (let [ys (take-while #(<= min-y %) (y-values dy))] 55 | (some all-target-y ys))))))) 56 | 57 | (defn hit-trajectories [target] 58 | (let [all-x (possible-dx0 target) 59 | all-y (possible-dy0 target) 60 | all-velocities (for [x all-x, y all-y] [x y])] 61 | (->> (map trajectory-from-origin all-velocities) 62 | (filter #(hits-target? target %))))) 63 | 64 | (defn apex [trajectory] 65 | (reduce (fn [acc [_ y]] (if (> y acc) y (reduced acc))) 66 | Long/MIN_VALUE 67 | trajectory)) 68 | 69 | (defn part1 [input] (->> input parse-input hit-trajectories (map apex) (apply max))) 70 | (defn part2 [input] (->> input parse-input hit-trajectories count)) 71 | -------------------------------------------------------------------------------- /src/advent_2021_clojure/day04.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day04 2 | (:require 3 | [advent-2021-clojure.utils :refer [parse-int] :as utils] 4 | [clojure.set :as set] 5 | [clojure.string :as str])) 6 | 7 | (defn parse-numbers-to-draw [line] 8 | (->> (str/split line #",") 9 | (map parse-int))) 10 | 11 | (defn parse-board [lines] 12 | {:marked #{} 13 | :unmarked (->> (str/split-lines lines) 14 | (map #(map parse-int (re-seq #"\d+" %))) 15 | (map-indexed (fn [y nums] 16 | (map-indexed (fn [x n] [n [x y]]) nums))) 17 | (apply concat) 18 | (into {}))}) 19 | 20 | (defn parse-game [input] 21 | (let [[drawn & boards] (utils/split-blank-line input)] 22 | {:numbers-to-draw (parse-numbers-to-draw drawn) 23 | :numbers-drawn () 24 | :boards (map parse-board boards)})) 25 | 26 | (defn coords-of [board n] (get-in board [:unmarked n])) 27 | (defn unmarked-values [board] (-> board :unmarked keys)) 28 | 29 | (def winning-combos (let [horizontal (for [y (range 5)] (map #(vector % y) (range 5))) 30 | vertical (for [x (range 5)] (map #(vector x %) (range 5)))] 31 | (concat horizontal vertical))) 32 | 33 | (defn check-for-bingo [{:keys [marked] :as board}] 34 | (if (some (partial set/superset? marked) winning-combos) 35 | (assoc board :bingo? true) 36 | board)) 37 | 38 | (defn place-number [board n] 39 | (if-let [c (coords-of board n)] 40 | (-> board 41 | (update :marked conj c) 42 | (update :unmarked dissoc n) 43 | (check-for-bingo)) 44 | board)) 45 | 46 | (defn take-turn [{:keys [numbers-to-draw boards] :as game}] 47 | (let [drawn (first numbers-to-draw)] 48 | (-> game 49 | (update :numbers-to-draw rest) 50 | (update :numbers-drawn conj drawn) 51 | (assoc :boards (map #(place-number % drawn) boards))))) 52 | 53 | (defn final-score [{:keys [boards numbers-drawn] :as _game}] 54 | (when-some [winner (first (filter :bingo? boards))] 55 | (* (apply + (unmarked-values winner)) 56 | (first numbers-drawn)))) 57 | 58 | (defn remove-winners [game] 59 | (update game :boards (partial remove :bingo?))) 60 | 61 | (defn play-until-winner [game] 62 | (->> (iterate take-turn game) 63 | (keep final-score) 64 | first)) 65 | 66 | (defn play-until-solitaire [game] 67 | (->> (iterate (comp remove-winners take-turn) game) 68 | (drop-while #(> (count (:boards %)) 1)) 69 | first)) 70 | 71 | (defn part1 [input] (-> input parse-game play-until-winner)) 72 | (defn part2 [input] (-> input parse-game play-until-solitaire play-until-winner)) 73 | -------------------------------------------------------------------------------- /src/advent_2021_clojure/day18.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day18 2 | (:require [clojure.string :as str])) 3 | 4 | (defn parse-trees [input] (map read-string (str/split-lines input))) 5 | 6 | (defn explode-pos 7 | ([tree] (explode-pos tree [])) 8 | ([tree pos] (if (>= (count pos) 4) 9 | pos 10 | (->> (get-in tree pos) 11 | (keep-indexed (fn [idx leaf] (when (vector? leaf) 12 | (explode-pos tree (conj pos idx))))) 13 | first)))) 14 | 15 | (defn regular-numbers 16 | ([tree] (regular-numbers tree [])) 17 | ([tree pos] (->> (get-in tree pos) 18 | (map-indexed (fn [idx leaf] (let [leaf-pos (conj pos idx)] 19 | (if (number? leaf) 20 | [[leaf-pos leaf]] 21 | (regular-numbers tree leaf-pos))))) 22 | (apply concat)))) 23 | 24 | (defn regular-number-positions [tree] (->> tree regular-numbers (map first))) 25 | 26 | (defn pos-before-and-after [coll v] 27 | (->> (concat [nil] coll [nil]) 28 | (partition 3 1) 29 | (keep (fn [[a b c]] (when (= b v) [a c]))) 30 | first)) 31 | 32 | (defn add-if-present [tree pos v] 33 | (if pos (update-in tree pos + v) tree)) 34 | 35 | (defn explode-tree [tree] 36 | (when-some [pos (explode-pos tree)] 37 | (let [[a b] (get-in tree pos) 38 | zeroed-out (assoc-in tree pos 0) 39 | [left-pos right-pos] (-> (regular-number-positions zeroed-out) 40 | (pos-before-and-after pos))] 41 | (-> zeroed-out 42 | (add-if-present left-pos a) 43 | (add-if-present right-pos b))))) 44 | 45 | (defn split-pos [tree] (->> (regular-numbers tree) 46 | (keep (fn [[p v]] (when (>= v 10) p))) 47 | first)) 48 | 49 | (defn split-val [n] (let [a (quot n 2) 50 | b (- n a)] 51 | [a b])) 52 | 53 | (defn split-tree [tree] 54 | (when-some [pos (split-pos tree)] 55 | (update-in tree pos split-val))) 56 | 57 | (defn reduce-tree [tree] 58 | (->> tree 59 | (iterate (partial (some-fn explode-tree split-tree))) 60 | (take-while some?) 61 | last)) 62 | 63 | (defn add-trees [trees] 64 | (reduce #(reduce-tree (vector %1 %2)) trees)) 65 | 66 | (defn magnitude [tree] 67 | (->> (mapv #(if (number? %) % (magnitude %)) tree) 68 | (mapv * [3 2]) 69 | (apply +))) 70 | 71 | (defn part1 [input] 72 | (->> input parse-trees add-trees magnitude)) 73 | 74 | (defn part2 [input] 75 | (let [trees (parse-trees input)] 76 | (->> (for [t0 trees, t1 trees, :when (not= t0 t1)] [t0 t1]) 77 | (map (comp magnitude add-trees)) 78 | (apply max)))) -------------------------------------------------------------------------------- /src/advent_2021_clojure/day22.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day22 2 | (:require [clojure.string :as str])) 3 | 4 | (def dimensions [:x :y :z]) 5 | 6 | (defn parse-instruction [line] 7 | (let [[instruction dim-str] (str/split line #" ") 8 | [x0 x1 y0 y1 z0 z1] (map read-string (re-seq #"\-?\d+" dim-str))] 9 | [(keyword instruction) {:x [x0 x1], :y [y0 y1], :z [z0 z1]}])) 10 | 11 | (defn within-initialization-area? [cuboid] 12 | (letfn [(dim-ok? [dim] (let [[v0 v1] (dim cuboid)] 13 | (and (>= v0 -50) (<= v1 50))))] 14 | (every? dim-ok? dimensions))) 15 | 16 | (defn overlap? 17 | ([cuboid1 cuboid2] (every? (partial overlap? cuboid1 cuboid2) dimensions)) 18 | ([cuboid1 cuboid2 dim] (let [[v0a v1a] (dim cuboid1) 19 | [v0b v1b] (dim cuboid2)] 20 | (and (<= v0a v1b) (<= v0b v1a))))) 21 | 22 | (defn split-on-dimension [cuboid1 cuboid2 dim] 23 | (if-not (overlap? cuboid1 cuboid2 dim) 24 | {:safe [cuboid1]} 25 | (let [[v0a v1a] (dim cuboid1) 26 | [v0b v1b] (dim cuboid2) 27 | overlap0 (max v0a v0b) 28 | overlap1 (min v1a v1b) 29 | safe-regions (filter (partial apply <=) [[v0a (dec overlap0)] [(inc overlap1) v1a]]) 30 | overlap-region [overlap0 overlap1]] 31 | {:safe (map #(assoc cuboid1 dim %) safe-regions) 32 | :unsafe [(assoc cuboid1 dim overlap-region)]}))) 33 | 34 | (defn remove-overlaps [cuboid1 cuboid2] 35 | (if-not (overlap? cuboid1 cuboid2) 36 | [cuboid1] 37 | (first (reduce (fn [[acc-safe acc-unsafe] dim] 38 | (let [{:keys [safe unsafe]} 39 | (->> acc-unsafe 40 | (map #(split-on-dimension % cuboid2 dim)) 41 | (apply merge-with conj))] 42 | [(apply conj acc-safe safe) unsafe])) 43 | [() [cuboid1]] 44 | dimensions)))) 45 | 46 | (defn remove-all-overlaps [cuboids new-cuboid] 47 | (reduce (fn [acc c] (apply conj acc (remove-overlaps c new-cuboid))) 48 | () 49 | cuboids)) 50 | 51 | (defn apply-instruction [cuboids [op new-cuboid]] 52 | (let [remaining (remove-all-overlaps cuboids new-cuboid)] 53 | (if (= op :on) 54 | (conj remaining new-cuboid) 55 | remaining))) 56 | 57 | (defn apply-instructions [instructions] 58 | (reduce apply-instruction () instructions)) 59 | 60 | (defn cuboid-size [cuboid] 61 | (->> (map (fn [dim] (let [[v0 v1] (cuboid dim)] 62 | (inc (- v1 v0)))) dimensions) 63 | (apply *))) 64 | 65 | (defn solve [instruction-filter input] 66 | (->> (str/split-lines input) 67 | (map parse-instruction) 68 | (filter #(instruction-filter (second %))) 69 | apply-instructions 70 | (map cuboid-size) 71 | (apply +))) 72 | 73 | (defn part1 [input] (solve within-initialization-area? input)) 74 | (defn part2 [input] (solve identity input)) -------------------------------------------------------------------------------- /resources/day22_sample_data3.txt: -------------------------------------------------------------------------------- 1 | on x=-5..47,y=-31..22,z=-19..33 2 | on x=-44..5,y=-27..21,z=-14..35 3 | on x=-49..-1,y=-11..42,z=-10..38 4 | on x=-20..34,y=-40..6,z=-44..1 5 | off x=26..39,y=40..50,z=-2..11 6 | on x=-41..5,y=-41..6,z=-36..8 7 | off x=-43..-33,y=-45..-28,z=7..25 8 | on x=-33..15,y=-32..19,z=-34..11 9 | off x=35..47,y=-46..-34,z=-11..5 10 | on x=-14..36,y=-6..44,z=-16..29 11 | on x=-57795..-6158,y=29564..72030,z=20435..90618 12 | on x=36731..105352,y=-21140..28532,z=16094..90401 13 | on x=30999..107136,y=-53464..15513,z=8553..71215 14 | on x=13528..83982,y=-99403..-27377,z=-24141..23996 15 | on x=-72682..-12347,y=18159..111354,z=7391..80950 16 | on x=-1060..80757,y=-65301..-20884,z=-103788..-16709 17 | on x=-83015..-9461,y=-72160..-8347,z=-81239..-26856 18 | on x=-52752..22273,y=-49450..9096,z=54442..119054 19 | on x=-29982..40483,y=-108474..-28371,z=-24328..38471 20 | on x=-4958..62750,y=40422..118853,z=-7672..65583 21 | on x=55694..108686,y=-43367..46958,z=-26781..48729 22 | on x=-98497..-18186,y=-63569..3412,z=1232..88485 23 | on x=-726..56291,y=-62629..13224,z=18033..85226 24 | on x=-110886..-34664,y=-81338..-8658,z=8914..63723 25 | on x=-55829..24974,y=-16897..54165,z=-121762..-28058 26 | on x=-65152..-11147,y=22489..91432,z=-58782..1780 27 | on x=-120100..-32970,y=-46592..27473,z=-11695..61039 28 | on x=-18631..37533,y=-124565..-50804,z=-35667..28308 29 | on x=-57817..18248,y=49321..117703,z=5745..55881 30 | on x=14781..98692,y=-1341..70827,z=15753..70151 31 | on x=-34419..55919,y=-19626..40991,z=39015..114138 32 | on x=-60785..11593,y=-56135..2999,z=-95368..-26915 33 | on x=-32178..58085,y=17647..101866,z=-91405..-8878 34 | on x=-53655..12091,y=50097..105568,z=-75335..-4862 35 | on x=-111166..-40997,y=-71714..2688,z=5609..50954 36 | on x=-16602..70118,y=-98693..-44401,z=5197..76897 37 | on x=16383..101554,y=4615..83635,z=-44907..18747 38 | off x=-95822..-15171,y=-19987..48940,z=10804..104439 39 | on x=-89813..-14614,y=16069..88491,z=-3297..45228 40 | on x=41075..99376,y=-20427..49978,z=-52012..13762 41 | on x=-21330..50085,y=-17944..62733,z=-112280..-30197 42 | on x=-16478..35915,y=36008..118594,z=-7885..47086 43 | off x=-98156..-27851,y=-49952..43171,z=-99005..-8456 44 | off x=2032..69770,y=-71013..4824,z=7471..94418 45 | on x=43670..120875,y=-42068..12382,z=-24787..38892 46 | off x=37514..111226,y=-45862..25743,z=-16714..54663 47 | off x=25699..97951,y=-30668..59918,z=-15349..69697 48 | off x=-44271..17935,y=-9516..60759,z=49131..112598 49 | on x=-61695..-5813,y=40978..94975,z=8655..80240 50 | off x=-101086..-9439,y=-7088..67543,z=33935..83858 51 | off x=18020..114017,y=-48931..32606,z=21474..89843 52 | off x=-77139..10506,y=-89994..-18797,z=-80..59318 53 | off x=8476..79288,y=-75520..11602,z=-96624..-24783 54 | on x=-47488..-1262,y=24338..100707,z=16292..72967 55 | off x=-84341..13987,y=2429..92914,z=-90671..-1318 56 | off x=-37810..49457,y=-71013..-7894,z=-105357..-13188 57 | off x=-27365..46395,y=31009..98017,z=15428..76570 58 | off x=-70369..-16548,y=22648..78696,z=-1892..86821 59 | on x=-53470..21291,y=-120233..-33476,z=-44150..38147 60 | off x=-93533..-4276,y=-16170..68771,z=-104985..-24507 -------------------------------------------------------------------------------- /resources/day24_data.txt: -------------------------------------------------------------------------------- 1 | inp w 2 | mul x 0 3 | add x z 4 | mod x 26 5 | div z 1 6 | add x 11 7 | eql x w 8 | eql x 0 9 | mul y 0 10 | add y 25 11 | mul y x 12 | add y 1 13 | mul z y 14 | mul y 0 15 | add y w 16 | add y 6 17 | mul y x 18 | add z y 19 | inp w 20 | mul x 0 21 | add x z 22 | mod x 26 23 | div z 1 24 | add x 11 25 | eql x w 26 | eql x 0 27 | mul y 0 28 | add y 25 29 | mul y x 30 | add y 1 31 | mul z y 32 | mul y 0 33 | add y w 34 | add y 12 35 | mul y x 36 | add z y 37 | inp w 38 | mul x 0 39 | add x z 40 | mod x 26 41 | div z 1 42 | add x 15 43 | eql x w 44 | eql x 0 45 | mul y 0 46 | add y 25 47 | mul y x 48 | add y 1 49 | mul z y 50 | mul y 0 51 | add y w 52 | add y 8 53 | mul y x 54 | add z y 55 | inp w 56 | mul x 0 57 | add x z 58 | mod x 26 59 | div z 26 60 | add x -11 61 | eql x w 62 | eql x 0 63 | mul y 0 64 | add y 25 65 | mul y x 66 | add y 1 67 | mul z y 68 | mul y 0 69 | add y w 70 | add y 7 71 | mul y x 72 | add z y 73 | inp w 74 | mul x 0 75 | add x z 76 | mod x 26 77 | div z 1 78 | add x 15 79 | eql x w 80 | eql x 0 81 | mul y 0 82 | add y 25 83 | mul y x 84 | add y 1 85 | mul z y 86 | mul y 0 87 | add y w 88 | add y 7 89 | mul y x 90 | add z y 91 | inp w 92 | mul x 0 93 | add x z 94 | mod x 26 95 | div z 1 96 | add x 15 97 | eql x w 98 | eql x 0 99 | mul y 0 100 | add y 25 101 | mul y x 102 | add y 1 103 | mul z y 104 | mul y 0 105 | add y w 106 | add y 12 107 | mul y x 108 | add z y 109 | inp w 110 | mul x 0 111 | add x z 112 | mod x 26 113 | div z 1 114 | add x 14 115 | eql x w 116 | eql x 0 117 | mul y 0 118 | add y 25 119 | mul y x 120 | add y 1 121 | mul z y 122 | mul y 0 123 | add y w 124 | add y 2 125 | mul y x 126 | add z y 127 | inp w 128 | mul x 0 129 | add x z 130 | mod x 26 131 | div z 26 132 | add x -7 133 | eql x w 134 | eql x 0 135 | mul y 0 136 | add y 25 137 | mul y x 138 | add y 1 139 | mul z y 140 | mul y 0 141 | add y w 142 | add y 15 143 | mul y x 144 | add z y 145 | inp w 146 | mul x 0 147 | add x z 148 | mod x 26 149 | div z 1 150 | add x 12 151 | eql x w 152 | eql x 0 153 | mul y 0 154 | add y 25 155 | mul y x 156 | add y 1 157 | mul z y 158 | mul y 0 159 | add y w 160 | add y 4 161 | mul y x 162 | add z y 163 | inp w 164 | mul x 0 165 | add x z 166 | mod x 26 167 | div z 26 168 | add x -6 169 | eql x w 170 | eql x 0 171 | mul y 0 172 | add y 25 173 | mul y x 174 | add y 1 175 | mul z y 176 | mul y 0 177 | add y w 178 | add y 5 179 | mul y x 180 | add z y 181 | inp w 182 | mul x 0 183 | add x z 184 | mod x 26 185 | div z 26 186 | add x -10 187 | eql x w 188 | eql x 0 189 | mul y 0 190 | add y 25 191 | mul y x 192 | add y 1 193 | mul z y 194 | mul y 0 195 | add y w 196 | add y 12 197 | mul y x 198 | add z y 199 | inp w 200 | mul x 0 201 | add x z 202 | mod x 26 203 | div z 26 204 | add x -15 205 | eql x w 206 | eql x 0 207 | mul y 0 208 | add y 25 209 | mul y x 210 | add y 1 211 | mul z y 212 | mul y 0 213 | add y w 214 | add y 11 215 | mul y x 216 | add z y 217 | inp w 218 | mul x 0 219 | add x z 220 | mod x 26 221 | div z 26 222 | add x -9 223 | eql x w 224 | eql x 0 225 | mul y 0 226 | add y 25 227 | mul y x 228 | add y 1 229 | mul z y 230 | mul y 0 231 | add y w 232 | add y 13 233 | mul y x 234 | add z y 235 | inp w 236 | mul x 0 237 | add x z 238 | mod x 26 239 | div z 26 240 | add x 0 241 | eql x w 242 | eql x 0 243 | mul y 0 244 | add y 25 245 | mul y x 246 | add y 1 247 | mul z y 248 | mul y 0 249 | add y w 250 | add y 7 251 | mul y x 252 | add z y -------------------------------------------------------------------------------- /src/advent_2021_clojure/day21.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day21 2 | (:require [advent-2021-clojure.utils :as u])) 3 | 4 | (defn new-player [start] {:pos start, :score 0}) 5 | (defn new-game [players] {:players (vec players), :next-player 0}) 6 | 7 | (defn board-pos [n] (if (> n 10) (board-pos (- n 10)) n)) 8 | (defn move-pos [roll pos] (board-pos (+ roll pos))) 9 | (defn swap-players [n] (- 1 n)) 10 | 11 | (def deterministic-die {:values (cycle (range 1 101)), :num-rolls 0}) 12 | (defn roll-die [die n] 13 | (let [{:keys [values]} die 14 | sum (apply + (take n values))] 15 | [(-> die 16 | (update :values #(drop n %)) 17 | (update :num-rolls + n)) 18 | sum])) 19 | 20 | (defn move-player [game roll] 21 | (let [{:keys [players next-player]} game 22 | player (players next-player) 23 | moved (update player :pos (partial move-pos roll)) 24 | scored (update moved :score + (:pos moved))] 25 | (-> game 26 | (assoc-in [:players next-player] scored) 27 | (update :next-player swap-players)))) 28 | 29 | (defn take-turn [game] 30 | (let [[rolled-die sum] (-> game :die (roll-die 3))] 31 | (-> game 32 | (move-player sum) 33 | (assoc :die rolled-die)))) 34 | 35 | (defn winner [game target-score] 36 | (->> (:players game) 37 | (keep-indexed (fn [idx {:keys [score]}] (when (>= score target-score) idx))) 38 | first)) 39 | 40 | (defn play-until [game target-score] 41 | (->> (iterate take-turn game) 42 | (filter #(winner % target-score)) 43 | first)) 44 | 45 | (defn final-score [game] 46 | (let [{:keys [die players next-player]} game] 47 | (* (:num-rolls die) 48 | (:score (get players next-player))))) 49 | 50 | (defn part1 [player1 player2] 51 | (-> (new-game (map new-player [player1 player2])) 52 | (assoc :die deterministic-die) 53 | (play-until 1000) 54 | final-score)) 55 | 56 | (def dirac-rolls {3 1, 4 3, 5 6, 6 7, 7 6, 8 3, 9 1}) 57 | 58 | (def game-option-sorter 59 | (letfn [(game-compare [g] (let [{:keys [players next-player]} g 60 | [{pos1 :pos, score1 :score} {pos2 :pos, score2 :score}] players] 61 | [(+ score1 score2) score1 score2 next-player pos1 pos2]))] 62 | (fn [g1 g2] (compare (game-compare g1) (game-compare g2))))) 63 | 64 | (defn roll-dirac-dice [game] 65 | (reduce (fn [acc [roll n]] 66 | (let [next-game (move-player game roll)] 67 | (u/update-add acc next-game n))) 68 | {} 69 | dirac-rolls)) 70 | 71 | (defn part2 [player1 player2] 72 | (let [target 21 73 | initial-game (new-game (map new-player [player1 player2]))] 74 | (loop [game-options (sorted-set-by game-option-sorter initial-game), universes {initial-game 1}] 75 | (if-let [game (first game-options)] 76 | (let [paths-to-game (universes game) 77 | rolled-universes (roll-dirac-dice game) 78 | next-game-options (->> (keys rolled-universes) 79 | (remove #(winner % target)) 80 | (apply conj (disj game-options game))) 81 | next-universes (reduce-kv (fn [m k v] (u/update-add m k (* v paths-to-game))) 82 | (dissoc universes game) 83 | rolled-universes)] 84 | (recur next-game-options next-universes)) 85 | (->> universes 86 | (map (fn [[game n]] [(winner game target) n])) 87 | (group-by first) 88 | (map (comp #(apply + (map second %)) second)) 89 | (apply max)))))) -------------------------------------------------------------------------------- /resources/day18_data.txt: -------------------------------------------------------------------------------- 1 | [3,[5,[7,[3,9]]]] 2 | [[[[7,0],0],[2,[2,8]]],[[[7,8],1],3]] 3 | [[[[2,7],0],7],4] 4 | [[2,1],[9,0]] 5 | [[[[7,1],[3,2]],[[9,8],5]],[2,7]] 6 | [[[8,9],[[8,7],0]],[[[8,7],[6,3]],[[1,7],[8,9]]]] 7 | [[8,6],[[9,[1,7]],[6,[3,9]]]] 8 | [[2,[[5,6],6]],[[4,[5,9]],[3,[4,5]]]] 9 | [[[[2,0],[1,1]],[6,6]],[[1,9],[[2,7],[6,8]]]] 10 | [[[4,6],[[6,3],[3,9]]],[[[2,6],[6,1]],[[9,9],[1,5]]]] 11 | [[[4,[3,1]],3],6] 12 | [[0,[[5,2],8]],[1,[9,[4,3]]]] 13 | [[[[8,6],[2,1]],[2,[8,6]]],[[[7,1],[3,9]],0]] 14 | [[[[4,7],[2,7]],[[8,9],2]],[[[2,4],[7,2]],[3,7]]] 15 | [[5,[2,2]],[[1,6],[[9,1],[5,0]]]] 16 | [[5,[[1,2],[6,4]]],[6,8]] 17 | [[[5,[1,7]],7],[7,[8,1]]] 18 | [[1,9],[[0,3],[[6,7],[2,4]]]] 19 | [1,[7,[[0,6],0]]] 20 | [[[[5,7],9],[[3,2],7]],[[5,1],[9,9]]] 21 | [[[[0,4],[9,6]],[[8,3],[7,4]]],[7,[6,2]]] 22 | [[[[1,6],0],[[8,0],[3,4]]],[[3,[0,3]],4]] 23 | [4,[[7,8],[4,[9,7]]]] 24 | [[[2,[3,7]],5],[0,[9,9]]] 25 | [[[2,0],[[5,8],[7,6]]],[[9,[6,2]],[3,2]]] 26 | [[[3,1],3],[[[3,7],6],[9,8]]] 27 | [[7,[[2,5],5]],[5,[3,[4,5]]]] 28 | [[[6,7],6],[2,[[9,3],9]]] 29 | [[[[5,6],7],[[3,2],5]],[[9,[4,3]],[3,8]]] 30 | [0,7] 31 | [[[4,6],[2,9]],[[[7,6],[5,1]],7]] 32 | [[0,5],[[1,[4,1]],[[7,3],9]]] 33 | [[[2,[3,8]],5],[[[5,9],8],[7,0]]] 34 | [[[6,[8,6]],[[3,6],7]],[[2,1],[6,[7,5]]]] 35 | [[2,[[6,3],[8,9]]],[[[5,6],4],[[7,0],1]]] 36 | [[[[7,1],[5,6]],8],[[[8,9],4],[8,3]]] 37 | [[[9,2],[1,0]],0] 38 | [[5,[5,[8,5]]],4] 39 | [[3,[5,[4,9]]],3] 40 | [[8,[[7,7],6]],5] 41 | [[4,[[5,1],1]],[1,[1,[9,8]]]] 42 | [[[7,[3,6]],[[2,8],[4,7]]],[[[8,8],[4,0]],[2,4]]] 43 | [[[[3,6],3],[0,9]],2] 44 | [[2,8],[[8,[8,6]],[[1,1],[4,5]]]] 45 | [[2,[1,[1,0]]],[[[6,2],[7,4]],[[7,1],6]]] 46 | [3,[8,[7,[8,6]]]] 47 | [[1,0],[[[0,4],[0,5]],[1,5]]] 48 | [[[[5,0],4],[[7,8],[8,8]]],[[1,7],0]] 49 | [1,[[[4,1],7],[6,[9,0]]]] 50 | [[[1,8],2],[[5,5],[8,5]]] 51 | [[4,[9,[0,6]]],[[[8,9],[4,5]],4]] 52 | [[[[5,4],[1,7]],[[3,1],[7,9]]],[[[0,8],[4,7]],[[5,9],6]]] 53 | [[[[8,0],9],4],[[7,[1,3]],5]] 54 | [[[[5,0],6],[[6,1],8]],[[9,1],7]] 55 | [[9,[6,[8,8]]],[7,[[7,1],6]]] 56 | [[[5,[1,5]],[3,[4,2]]],[[[5,2],7],[[6,9],[2,8]]]] 57 | [[[5,[5,5]],[5,7]],[4,[[2,9],7]]] 58 | [[[[0,4],0],[[0,6],[3,0]]],[0,[[8,1],2]]] 59 | [[[7,[4,6]],[[7,2],[4,6]]],[[[9,3],[4,9]],6]] 60 | [[6,7],7] 61 | [[[4,1],[8,[1,5]]],[[4,6],0]] 62 | [[[4,[5,5]],5],[[0,[2,7]],[1,1]]] 63 | [[[[0,1],3],[6,7]],[4,7]] 64 | [[4,[6,4]],[[[9,8],1],[9,3]]] 65 | [[[4,9],0],[[[7,0],[0,9]],[1,[1,0]]]] 66 | [[[7,9],[[9,5],[6,9]]],[[0,[3,0]],[0,[5,9]]]] 67 | [9,[[0,0],[[1,9],9]]] 68 | [[[5,[0,5]],[[9,8],[9,5]]],[[0,[2,5]],7]] 69 | [[[[5,8],6],9],[[[2,7],7],[[7,8],5]]] 70 | [[8,[[4,7],6]],2] 71 | [[[[7,1],[9,0]],[9,[1,7]]],[[8,[6,7]],[2,5]]] 72 | [[4,[2,9]],8] 73 | [[[[7,6],[5,3]],[5,[9,7]]],[[6,[8,1]],[[6,4],9]]] 74 | [[7,[[7,8],4]],[[1,3],[4,[9,7]]]] 75 | [[[6,[6,7]],[[2,8],3]],[7,[6,[0,3]]]] 76 | [[9,8],[[0,[4,8]],[[9,1],1]]] 77 | [[[[4,0],[5,9]],7],[6,[[5,9],[9,6]]]] 78 | [[8,1],[1,[9,[8,3]]]] 79 | [[[1,[5,1]],[6,7]],[[5,9],[2,[6,7]]]] 80 | [[[3,7],[[7,8],1]],[[0,[6,3]],[8,0]]] 81 | [[5,[[9,3],[1,2]]],7] 82 | [[[1,[9,9]],3],[[6,4],[4,1]]] 83 | [[6,[1,[3,6]]],[2,9]] 84 | [[2,[0,2]],[5,[[9,4],[5,0]]]] 85 | [[4,[[3,1],[7,0]]],[[9,1],[[5,5],[6,7]]]] 86 | [[3,[[7,1],[3,4]]],[7,[9,[9,4]]]] 87 | [[9,9],[[5,4],[[9,7],4]]] 88 | [[[5,1],8],[[6,7],9]] 89 | [[[0,[9,5]],[4,3]],[3,2]] 90 | [[[6,[4,1]],[[8,7],[5,3]]],[[[1,2],5],[[9,2],5]]] 91 | [[[[7,4],[9,0]],[[1,8],[2,9]]],[[5,[1,9]],[4,0]]] 92 | [[[4,[3,8]],[[3,3],[2,8]]],[[[1,3],9],[[8,5],6]]] 93 | [[[[6,4],[7,9]],[[7,6],8]],[7,[9,8]]] 94 | [[7,[3,5]],7] 95 | [[[[5,0],[2,3]],[3,7]],[[4,[6,3]],[7,[4,4]]]] 96 | [[6,[3,[7,6]]],[[[5,8],[8,1]],[3,[1,5]]]] 97 | [[8,[9,[5,2]]],2] 98 | [[1,[5,4]],[[7,[8,0]],8]] 99 | [[[[2,7],4],3],[[1,4],[8,4]]] 100 | [3,[9,2]] -------------------------------------------------------------------------------- /src/advent_2021_clojure/day16.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day16 2 | (:require [advent-2021-clojure.utils :refer [parse-binary binary-to-decimal]] 3 | [clojure.string :as str])) 4 | 5 | (defn literal-value? [n] (= 4 n)) 6 | (def total-length-type? zero?) 7 | 8 | (defn left-pad-to-multiple [s mult c] 9 | (let [rem (mod (count s) mult)] 10 | (if (zero? rem) s (reduce #(str %2 %1) s (repeat (- mult rem) c))))) 11 | 12 | (defn hex-packet-to-binary-string [hex-packet] 13 | (apply str (map #(-> (Character/digit ^char % 16) 14 | (Integer/toBinaryString) 15 | (left-pad-to-multiple 4 \0)) 16 | hex-packet))) 17 | 18 | (defn literal-value-portion [s] 19 | (let [[ones [first-zero]] (split-with #(-> % first (= \1)) (partition 5 s))] 20 | (->> (concat ones first-zero) flatten (apply str)))) 21 | 22 | (defn padded-binary-to-decimal [s] 23 | (->> s (partition 5) (map rest) flatten (apply str) binary-to-decimal)) 24 | 25 | (defn parse-literal-packet [packet input] 26 | (let [portion (literal-value-portion input)] 27 | [(assoc packet :value (padded-binary-to-decimal portion)) (subs input (count portion))])) 28 | 29 | (declare parse-sub-packet) 30 | 31 | (defn parse-sub-packets [finished? packet input] 32 | (loop [p packet, remainder input] 33 | (if (finished? p remainder) 34 | [p remainder] 35 | (let [[sub-packet leftovers] (parse-sub-packet remainder)] 36 | (recur (update p :sub-packets conj sub-packet) leftovers))))) 37 | 38 | (defn parse-total-length-operator [packet input] 39 | (let [sub-packet-length (parse-binary (subs input 0 15)) 40 | sub-packet-payload (subs input 15 (+ 15 sub-packet-length)) 41 | non-sub-packet-payload (subs input (+ 15 sub-packet-length))] 42 | [(first (parse-sub-packets (fn [_ remainder] (str/blank? remainder)) packet sub-packet-payload)) 43 | non-sub-packet-payload])) 44 | 45 | (defn parse-num-sub-packets-operator [packet input] 46 | (let [num-sub-packets (parse-binary (subs input 0 11))] 47 | (parse-sub-packets (fn [p _] (= num-sub-packets (-> p :sub-packets count))) packet (subs input 11)))) 48 | 49 | (defn parse-operator-packet [packet input] 50 | (let [length-type-id (binary-to-decimal (subs input 0 1)) 51 | remainder (subs input 1) 52 | op-packet (assoc packet :sub-packets [])] 53 | (if (total-length-type? length-type-id) 54 | (parse-total-length-operator op-packet remainder) 55 | (parse-num-sub-packets-operator op-packet remainder)))) 56 | 57 | (defn parse-sub-packet [input] 58 | (let [version (binary-to-decimal (subs input 0 3)) 59 | packet-type (binary-to-decimal (subs input 3 6)) 60 | packet {:version version, :packet-type packet-type} 61 | payload (subs input 6)] 62 | (if (literal-value? packet-type) 63 | (parse-literal-packet packet payload) 64 | (parse-operator-packet packet payload)))) 65 | 66 | (defn parse-packet [input] (first (parse-sub-packet input))) 67 | 68 | (defn add-versions [packet] 69 | (reduce #(+ %1 (add-versions %2)) 70 | (:version packet) 71 | (:sub-packets packet))) 72 | 73 | (defn part1 [input] 74 | (-> input hex-packet-to-binary-string parse-packet add-versions)) 75 | 76 | (defn value-of [packet] 77 | (or (:value packet) 78 | (let [[sub-a sub-b :as sub-values] (map value-of (:sub-packets packet))] 79 | (case (:packet-type packet) 80 | 0 (apply + sub-values) 81 | 1 (apply * sub-values) 82 | 2 (apply min sub-values) 83 | 3 (apply max sub-values) 84 | 5 (if (> sub-a sub-b) 1 0) 85 | 6 (if (< sub-a sub-b) 1 0) 86 | 7 (if (= sub-a sub-b) 1 0))))) 87 | 88 | (defn part2 [input] 89 | (-> input hex-packet-to-binary-string parse-packet value-of)) -------------------------------------------------------------------------------- /resources/day07_data.txt: -------------------------------------------------------------------------------- 1 | 1101,1,29,67,1102,0,1,65,1008,65,35,66,1005,66,28,1,67,65,20,4,0,1001,65,1,65,1106,0,8,99,35,67,101,99,105,32,110,39,101,115,116,32,112,97,115,32,117,110,101,32,105,110,116,99,111,100,101,32,112,114,111,103,114,97,109,10,322,659,689,304,1706,69,576,110,238,904,299,206,78,954,776,590,404,125,235,438,472,187,205,620,14,378,1056,496,1323,59,44,636,432,658,30,195,107,425,105,214,908,145,641,1467,441,346,455,1454,773,146,97,42,509,8,1217,503,901,1147,1654,45,1438,503,62,851,590,105,217,44,646,197,491,333,1224,90,262,1132,1499,864,128,165,36,646,422,1265,501,414,328,170,1566,115,1049,154,224,490,1018,1019,1484,315,614,816,207,240,423,132,196,484,532,857,341,723,69,294,787,1020,691,185,525,697,1435,62,156,21,314,489,640,93,415,446,902,15,510,91,104,317,971,108,187,616,794,416,1332,499,1086,443,514,258,383,162,1034,1,331,269,283,1835,150,1698,1020,318,1540,687,17,889,585,1682,67,547,1,1353,149,1650,145,13,151,1144,409,294,740,676,267,827,1624,804,44,795,297,265,426,508,11,1359,963,277,203,1093,450,1229,287,160,1913,914,512,1098,103,975,64,26,787,87,104,340,362,153,173,93,455,89,577,40,1459,320,398,1245,12,452,515,594,0,1497,1238,88,14,538,431,0,699,1033,483,574,593,612,770,1006,332,23,753,1334,536,109,164,250,86,333,1577,896,1199,521,73,467,1037,0,539,375,1243,238,301,262,191,415,88,515,1410,54,1019,934,81,1273,78,306,57,145,472,57,682,203,63,512,427,104,457,214,197,1766,350,355,536,839,7,586,1209,71,88,858,562,64,335,84,1161,1305,1203,102,52,193,47,852,718,885,146,111,1014,667,8,52,637,254,1453,674,1542,47,107,55,321,591,829,1113,40,215,398,254,327,181,200,20,129,265,109,705,1265,12,148,367,349,333,341,272,90,166,699,681,1927,1267,86,282,299,36,48,1594,110,645,569,724,199,334,239,117,448,108,67,1257,142,902,208,728,700,107,1,621,1036,1397,837,313,380,208,156,39,220,238,648,197,26,2,1010,98,458,271,1237,99,751,31,236,26,622,802,4,121,244,240,67,462,1181,100,1381,1494,446,23,35,95,357,212,90,820,56,96,171,11,1101,1020,149,125,1504,896,25,8,1704,193,421,134,135,1397,1052,1059,741,967,1537,373,585,279,46,398,654,305,435,89,11,702,27,102,573,497,139,530,805,3,122,1329,175,134,137,57,516,790,587,163,296,153,1124,1336,946,63,39,278,13,253,237,653,200,250,1067,1891,697,182,628,0,60,303,389,1821,189,295,41,619,71,795,1228,110,1198,306,941,59,72,666,610,850,984,564,330,636,111,1541,542,80,212,927,127,427,33,365,313,697,200,286,708,478,264,448,1159,256,28,273,7,238,176,956,735,264,361,1882,139,1345,1,271,508,0,190,110,119,76,715,1338,80,1026,132,286,966,337,1715,514,328,265,63,1376,1413,1421,457,66,1594,737,59,548,184,801,165,96,129,1200,50,604,1013,309,627,625,597,1012,77,670,177,264,115,174,109,148,270,24,346,33,1270,359,954,113,207,484,1756,1155,1067,991,1358,61,530,612,135,351,706,244,489,609,484,76,168,258,161,694,1019,1502,558,117,112,1041,1040,448,879,37,616,930,32,357,1650,231,458,1068,585,9,439,412,292,116,494,246,28,260,463,200,84,1106,750,667,1284,129,878,1077,453,960,409,1327,412,243,89,616,443,256,645,1083,526,95,818,9,59,76,541,312,1168,430,64,2,187,561,1322,369,1245,64,854,126,359,240,42,157,35,232,863,74,331,250,695,914,182,208,94,656,87,530,1444,163,429,46,299,1038,38,471,91,112,819,1644,244,1718,76,806,103,752,124,796,1183,15,829,1038,6,529,913,140,326,435,44,617,659,123,753,444,467,408,182,1387,202,684,60,55,26,155,902,1075,86,375,924,862,150,1230,700,143,417,156,933,872,639,1032,137,146,1649,1562,4,11,257,556,29,1440,177,935,741,492,300,1530,92,453,56,244,37,997,762,624,456,1182,845,150,367,393,334,338,100,278,1374,267,1261,25,106,332,25,2,14,123,288,600,880,838,323,183,1075,202,445,218,1538,73,300,555,322,587,7,606,753,676,28,57,557,1283,23,73,31,370,29,491,5,31,97,199,188,1088,276,1061,1043,42,1463,601,56,255,426,150,1451,562,0,408,7,701,111,1145,838,976,310,561,645,33,213,1020,73,81,849,2,586,825,183,2,704,59,1515,906,647,91,585,14,778,333,258,353,128,839,146,81,231,128,716,699,64,345,812,906,1180,286,243,295,1031,197,1392 -------------------------------------------------------------------------------- /src/advent_2021_clojure/day19.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day19 2 | (:require 3 | [advent-2021-clojure.utils :as utils :refer [parse-int]] 4 | [clojure.set :as set] 5 | [clojure.string :as str])) 6 | 7 | (defn parse-scanner [input] 8 | (let [[header & beacons] (str/split-lines input)] 9 | {:id (->> header (re-seq #"\d+") first parse-int) 10 | :beacons (->> beacons 11 | (map (fn [line] (mapv parse-int (str/split line #",")))) 12 | set) 13 | :scanners #{[0 0 0]}})) 14 | 15 | (defn parse-input [input] 16 | (reduce #(assoc %1 (:id %2) %2) 17 | {} 18 | (map parse-scanner (utils/split-blank-line input)))) 19 | 20 | (def orientation-fns [; Face straight 21 | identity 22 | (fn [[x y z]] [(- z) y x]) 23 | (fn [[x y z]] [(- x) y (- z)]) 24 | (fn [[x y z]] [z y (- x)]) 25 | 26 | ; Face right 27 | (fn [[x y z]] [(- y) x z]) 28 | (fn [[x y z]] [(- z) x (- y)]) 29 | (fn [[x y z]] [y x (- z)]) 30 | (fn [[x y z]] [z x y]) 31 | 32 | ; Face behind 33 | (fn [[x y z]] [(- x) (- y) z]) 34 | (fn [[x y z]] [(- z) (- y) (- x)]) 35 | (fn [[x y z]] [x (- y) (- z)]) 36 | (fn [[x y z]] [z (- y) x]) 37 | 38 | ; Face left 39 | (fn [[x y z]] [y (- x) z]) 40 | (fn [[x y z]] [(- z) (- x) y]) 41 | (fn [[x y z]] [(- y) (- x) (- z)]) 42 | (fn [[x y z]] [z (- x) (- y)]) 43 | 44 | ; Face up 45 | (fn [[x y z]] [x z (- y)]) 46 | (fn [[x y z]] [y z x]) 47 | (fn [[x y z]] [(- x) z y]) 48 | (fn [[x y z]] [(- y) z (- x)]) 49 | 50 | ; Face down 51 | (fn [[x y z]] [x (- z) y]) 52 | (fn [[x y z]] [(- y) (- z) x]) 53 | (fn [[x y z]] [(- x) (- z) (- y)]) 54 | (fn [[x y z]] [y (- z) (- x)])]) 55 | 56 | (defn path-to [from to] (mapv - to from)) 57 | (defn follow-path [point path] (mapv + point path)) 58 | 59 | (defn combine-beacons [beacons0 beacons1] 60 | (->> (for [b0 (drop 11 beacons0) #_beacons0, b1 beacons1] (path-to b1 b0)) 61 | (filter (fn [path] (>= (->> (map #(follow-path % path) beacons1) 62 | set 63 | (set/intersection beacons0) 64 | count) 65 | 12))) 66 | first)) 67 | 68 | (defn combine-scanners [scanner0 scanner1] 69 | (let [[beacons0 beacons1] (map :beacons [scanner0 scanner1])] 70 | (first (keep (fn [f] 71 | (let [beacons1' (map f beacons1)] 72 | (when-some [path (combine-beacons beacons0 beacons1')] 73 | (-> scanner0 74 | (update :beacons set/union (set (map #(follow-path % path) beacons1'))) 75 | (update :scanners (fn [s] (apply conj s (map #(follow-path (f %) path) (:scanners scanner1))))))))) 76 | orientation-fns)))) 77 | 78 | (defn combine-all-scanners [scanners] 79 | (println "Examining" (count scanners) "scanners") 80 | (if (= (count scanners) 1) 81 | (scanners 0) 82 | (recur (reduce (fn [acc [id0 id1]] (if-not (and (acc id0) (acc id1)) 83 | acc 84 | (if-some [scanner' (combine-scanners (acc id0) (acc id1))] 85 | (do (println "Combining" id0 "with" id1 "from keys" (keys acc)) 86 | (-> acc 87 | (dissoc id1) 88 | (assoc id0 scanner'))) 89 | acc))) 90 | scanners 91 | (for [id0 (keys scanners), id1 (keys scanners), :when (> id1 id0)] [id0 id1]))))) 92 | 93 | (defn manhattan-distance [p1 p2] 94 | (->> (path-to p1 p2) 95 | (map utils/abs) 96 | (apply +))) 97 | 98 | (defn greatest-distances [s] 99 | (->> (for [v1 s, v2 s, :when (not= v1 v2)] [v1 v2]) 100 | (map (partial apply manhattan-distance)) 101 | (apply max))) 102 | 103 | (defn part1 [input] (->> input parse-input combine-all-scanners :beacons count)) 104 | (defn part2 [input] (->> input parse-input combine-all-scanners :scanners greatest-distances)) 105 | -------------------------------------------------------------------------------- /src/advent_2021_clojure/day23.clj: -------------------------------------------------------------------------------- 1 | (ns advent-2021-clojure.day23 2 | (:require [advent-2021-clojure.utils :refer [abs]])) 3 | 4 | (def hallway-length 11) 5 | (def room-pos {:a 2, :b 4, :c 6, :d 8}) 6 | (def all-rooms (keys room-pos)) 7 | (def room-entrances (-> room-pos vals set)) 8 | (defn move-cost [amphipod amount] (* amount ({:a 1 :b 10 :c 100 :d 1000} amphipod))) 9 | 10 | (defn create-burrow [a0 a1, b0 b1, c0 c1, d0 d1] 11 | {:hallway (vec (repeat hallway-length nil)) 12 | :rooms {:a [a0 a1] 13 | :b [b0 b1] 14 | :c [c0 c1] 15 | :d [d0 d1]} 16 | :cost 0}) 17 | 18 | (defn hallway-at [burrow n] (get-in burrow [:hallway n])) 19 | (defn room-occupants [burrow room] (get-in burrow [:rooms room])) 20 | 21 | (defn room-exiting? [burrow room] (let [values (room-occupants burrow room)] 22 | (and (not-every? nil? values) 23 | (not-every? #(= room (or % room)) values)))) 24 | (defn room-entering? [burrow room] (let [values (room-occupants burrow room)] 25 | (and (not-every? #(= room %) values) 26 | (every? #(= room (or % room)) values)))) 27 | (defn room-complete? [burrow room] (every? #(= room %) (room-occupants burrow room))) 28 | 29 | (defn room-mover [burrow room] (->> (room-occupants burrow room) 30 | (keep-indexed (fn [idx v] (when v [idx v]))) 31 | first)) 32 | 33 | (defn exiting-rooms [burrow] (filter #(room-exiting? burrow %) all-rooms)) 34 | 35 | (defn entering-rooms [burrow] (filter #(room-entering? burrow %) all-rooms)) 36 | 37 | (defn success? [burrow] (every? #(room-complete? burrow %) all-rooms)) 38 | 39 | (defn left-right-hallways [room] 40 | (let [pos (room-pos room)] 41 | [(remove room-entrances (range (dec pos) -1 -1)) 42 | (remove room-entrances (range (inc pos) hallway-length))])) 43 | 44 | (defn hallway-destinations-from [burrow room] 45 | (->> (left-right-hallways room) 46 | (map (fn [rng] (take-while #(nil? (hallway-at burrow %)) rng))) 47 | (apply concat))) 48 | 49 | (defn leave-room [burrow room hallway-pos] 50 | {:pre [(room-exiting? burrow room) 51 | (nil? (hallway-at burrow hallway-pos))]} 52 | (let [[depth amphipod] (room-mover burrow room) 53 | cost (move-cost amphipod (-> room room-pos (- hallway-pos) abs (+ depth) inc))] 54 | (-> burrow 55 | (assoc-in [:hallway hallway-pos] amphipod) 56 | (assoc-in [:rooms room depth] nil) 57 | (update :cost + cost)))) 58 | 59 | (defn leave-room-options [burrow] 60 | (mapcat (fn [room] (map #(leave-room burrow room %) 61 | (hallway-destinations-from burrow room))) 62 | (exiting-rooms burrow))) 63 | 64 | (defn nearest-neighbors [burrow room] 65 | (keep (fn [spaces] (->> spaces 66 | (keep #(when-some [v (hallway-at burrow %)] 67 | [% v])) 68 | first)) 69 | (left-right-hallways room))) 70 | 71 | (defn enter-room-depth [burrow room] 72 | (->> (room-occupants burrow room) 73 | (keep-indexed (fn [idx v] (when-not v idx))) 74 | last)) 75 | 76 | (defn enter-room [burrow room hallway-pos] 77 | {:pre [(= room (hallway-at burrow hallway-pos)) 78 | (room-entering? burrow room)]} 79 | (let [depth (enter-room-depth burrow room) 80 | cost (move-cost room (-> room room-pos (- hallway-pos) abs (+ depth) inc))] 81 | (-> burrow 82 | (assoc-in [:hallway hallway-pos] nil) 83 | (assoc-in [:rooms room depth] room) 84 | (update :cost + cost)))) 85 | 86 | (defn enter-room-options [burrow] 87 | (mapcat (fn [room] (keep (fn [[pos v]] (when (= v room) 88 | (enter-room burrow room pos))) 89 | (nearest-neighbors burrow room))) 90 | (entering-rooms burrow))) 91 | 92 | (def burrow-sorter 93 | (letfn [(burrow-comparator [b] [(:cost b) (.toString b)])] 94 | (fn [b1 b2] (compare (burrow-comparator b1) (burrow-comparator b2))))) 95 | 96 | (defn solve [burrow] 97 | (loop [options (sorted-set-by burrow-sorter burrow)] 98 | (let [b (first options)] 99 | (if (success? b) 100 | (:cost b) 101 | (let [enter (enter-room-options b)] 102 | (recur (apply conj (disj options b) (if (seq enter) 103 | enter 104 | (leave-room-options b))))))))) 105 | 106 | (defn add-header-row [burrow] 107 | (letfn [(insert-middle [[v0 v1] v0' v1'] [v0 v0' v1' v1])] 108 | (-> burrow 109 | (update-in [:rooms :a] insert-middle :d :d) 110 | (update-in [:rooms :b] insert-middle :c :b) 111 | (update-in [:rooms :c] insert-middle :b :a) 112 | (update-in [:rooms :d] insert-middle :a :c)))) 113 | 114 | (defn part1 [input] (->> input (apply create-burrow) solve)) 115 | (defn part2 [input] (->> input (apply create-burrow) add-header-row solve)) 116 | -------------------------------------------------------------------------------- /docs/day06.md: -------------------------------------------------------------------------------- 1 | # Day Six: Lanternfish 2 | 3 | * [Problem statement](https://adventofcode.com/2021/day/6) 4 | * [Solution code](https://github.com/abyala/advent-2021-clojure/blob/master/src/advent_2021_clojure/day06.clj) 5 | 6 | --- 7 | 8 | ## Preamble 9 | 10 | This might be one of the simplest puzzles I've seen in a while. My write-up breaks apart more succinct code into 11 | more pieces so it's easier to read, even though this doubles the number of lines actually needed. Anyway, off we go! 12 | 13 | --- 14 | 15 | ## Part 1 16 | 17 | We are observing lanternfish having babies. How awkward. Every fish has a timer, and on its zeroth day, it resets its 18 | timer to 6 and creates a baby lanternfish with a timer of 8. It's the miracle of digital life. 19 | 20 | Now the puzzle does each and every fish individually, what its timer is, and the emergence of new fish with timers of 21 | 8. I decided not to represent every fish individually, but instead just record how many fish have a certain timer on 22 | 9. each day. It turns out I wasn't punished for this when I got to part 2, so score! 23 | 24 | My target data representation is a simple map of `{timer fish-count}`, given a single-line input string with 25 | comma-separated starting timers. The easiest approach is to use `re-seq` to do a reg-ex extraction of every number 26 | between the commas, map it to an integer using `parse-int`, and then call the `frequencies` function. This core 27 | function takes in a sequence and returns a map of every value to its count within the sequence, which conveniently is 28 | exactly what we need here! To make later code cleaner, I'll merge the input fish into a default map of `no-fish`, 29 | where all numbers 0 through 8 are mapped to zero, such that every possible timer value is in the map. It's not strictly 30 | necessary, but it helps. 31 | 32 | ```clojure 33 | (def no-fish {0 0, 1 0, 2 0, 3 0, 4 0, 5 0, 6 0, 7 0, 8 0}) 34 | 35 | (defn parse-fish [input] 36 | (->> (re-seq #"\d" input) 37 | (map parse-int) 38 | frequencies 39 | (merge no-fish))) 40 | ``` 41 | 42 | Now the whole problem statement really comes down to one function - `next-generation`, but we've got a little setup to 43 | make it simple to read. First, I define convenience constants of `delivery-timer`, `post-delivery-timer` and 44 | `new-fish-timer`, set to 0, 6 and 8, respectively; the zero may seem like overkill, but I want my code to tell a 45 | story. Then I make a helper function called `next-timer` that defines the next generation of a timer. Values of 0 (aka 46 | `delivery-timer`) become 6 (aka `post-delivery-timer`), and everything else decrements. 47 | 48 | ```clojure 49 | (def delivery-timer 0) 50 | (def post-delivery-timer 6) 51 | (def new-fish-timer 8) 52 | 53 | (defn next-timer [timer] 54 | (if (zero? timer) post-delivery-timer (dec timer))) 55 | ``` 56 | 57 | Now we can build `next-generation` cleanly in two steps. First, starting with the `no-fish` map again, we map every 58 | timer to its `next-timer` value, and add it to the map. Then we add in the new fish. Now the question is, why do we 59 | _add_ the fish values to the map instead of just setting them? It's because the next generation's number of fish with 60 | a timer of 6 is the sum of the previous generation's fish with timers of 0 and 7. 61 | 62 | One terrific function I want to show off here is `reduce-kv`, which is another form of `reduce`. Both take in 63 | 3 arguments - a reducing function, an initial value (this is actually optional for `reduce`), and the collection to 64 | reduce over. However, while `reduce` has a 2-arity function of `[accumulator value]`, `reduce-kv` requires the 65 | collection to be associative, and thus its reducing function is `3-arity` or form `[accumulator key value]`. This is 66 | effectively the same as using `(reduce (fn [accumulator [key value]] ...))`, but it's clearer to see what's going on 67 | by using `reduce-kv`. 68 | 69 | ```clojure 70 | (defn next-generation [fish] 71 | (let [deliveries (fish delivery-timer)] 72 | (-> (reduce-kv (fn [m k v] (update m (next-timer k) + v)) no-fish fish) 73 | (assoc new-fish-timer deliveries)))) 74 | ``` 75 | 76 | Almost done. Before creating the `solve` function, let's define `nth-generation`, a function which takes in the day 77 | number we want to look at, as well as the initial map of fish, and then return the map on that day. We'll just use 78 | `iterate` invoking `next-generation`, and then use `nth` to get the correct resulting value. 79 | 80 | ```clojure 81 | (defn nth-generation [n fish] 82 | (-> (iterate next-generation fish) 83 | (nth n))) 84 | ``` 85 | 86 | Finally, we create `solve`, which takes in both the input and the number of days. The function parses the fish, invokes 87 | `nth-generation` to get the state on the last day, calls `vals` to get to the number of fish, and then `(apply +)` to 88 | add them up. And the `day1` function just calls `solve` with 80 for the number of days. 89 | 90 | ```clojure 91 | (defn solve [input num-days] 92 | (->> (parse-fish input) 93 | (nth-generation num-days) 94 | vals 95 | (apply +))) 96 | 97 | (defn day1 [input] (solve input 80)) 98 | ``` 99 | 100 | --- 101 | 102 | ## Part 2 103 | 104 | Well now we just need to get the 256th day instead of the 80th day. I'm assuming this just asserts that our algorithms 105 | don't contain a giant list of each and every fish, because my data set brought me into the trillians of fish. Our 106 | algorithm can handle the extra data, no problem. One nice thing about Clojure is that it will automatically convert 107 | Ints into Longs, or Floats into Doubles, to avoid overflow or underflow. 108 | 109 | Anyway, here's the one-line `day2` function. 110 | 111 | ```clojure 112 | (defn day2 [input] (solve input 256)) 113 | ``` 114 | 115 | Be free, lanternfish, and procreate until we run out of water molecules in the sea! 116 | -------------------------------------------------------------------------------- /docs/day07.md: -------------------------------------------------------------------------------- 1 | # Day Seven: The Treachery of Whales 2 | 3 | * [Problem statement](https://adventofcode.com/2021/day/7) 4 | * [Solution code](https://github.com/abyala/advent-2021-clojure/blob/master/src/advent_2021_clojure/day07.clj) 5 | 6 | --- 7 | 8 | ## Part 1 9 | 10 | In a fight between giant whales and an army of crabs in submarines, always go with the crabs. That is the lessons for 11 | the day. 12 | 13 | A bunch of crabs are all spread out in a line, and we need to find the cheapest way to get them to come together on 14 | a single spot. Each time a crab moves one space, it costs one piece of fuel, so we need to sum up the total number of 15 | steps needs to get everyone aligned, no matter what space they end up on. 16 | 17 | Alright, let's do the parsing. It's almost not worth mentioning anymore, but here's how we parse a comma-separated 18 | String into a sequence of integers. We've done this multiple times already. 19 | 20 | ```clojure 21 | (defn parse-crabs [input] 22 | (map parse-int (str/split input #","))) 23 | ``` 24 | 25 | Now the plan is to pick a point on the line, and calculate the total fuel spent to get every crab on that point. So 26 | we'll make a `distances-to` function that maps the absolute value of the difference between two points, and sums them 27 | up. Now call me petty, but I really prefer to call Clojure functions over Java functions because it just looks so much 28 | nicer, and because it makes the typecasting nicer to look at. 29 | 30 | ```clojure 31 | ; advent-2021-clojure.utils namespace 32 | (defn abs [^long n] (Math/abs n)) 33 | 34 | ; advent-2021-clojure.day07 namespace 35 | (defn distances-to [crabs pos] 36 | (->> crabs 37 | (map #(utils/abs (- % pos))) 38 | (apply +))) 39 | ``` 40 | 41 | Ok, it's time to write the `part1` function already! After we parse the input, we're going to pick the min and max 42 | crab values, since that will define the range of values we'll want to calculate with `distances-to`. Then we do the 43 | calculations for each value in the range, and then extract the minimum to get to the answer. 44 | 45 | ```clojure 46 | (defn part1 [input] 47 | (let [crabs (parse-crabs input) 48 | [first-crab last-crab] (apply (juxt min max) crabs)] 49 | (->> (range first-crab (inc last-crab)) 50 | (map (partial distances-to crabs)) 51 | (apply min)))) 52 | ``` 53 | 54 | Onward! 55 | 56 | --- 57 | 58 | ## Part 2 59 | 60 | This part is pretty much the same as the first part, except that the cost of moving crabs increases according to the 61 | summation function. (Thank you to coworker Matt for reminding me that this was a summation and not a factorial!) And 62 | thank you to high school math for teaching me that `(summation n) = (n * (n + 1)) / 2`, so I'll throw that 63 | into a `summation` function in the `utils` namespace again. 64 | 65 | ```clojure 66 | ; advent-2021-clojure.utils namespace 67 | (defn summation [n] 68 | (-> (* n (inc n)) 69 | (/ 2))) 70 | ``` 71 | 72 | So now we need to have a new calculation function, `summation-distances-to`, which sums up the summation distance from 73 | each crab to a given position. Hmm... that looks awfully similar to the `distances-to` function. Is there a 74 | refactoring in our future? 75 | 76 | ```clojure 77 | (defn summation-distances-to [crabs pos] 78 | (->> crabs 79 | (map #(summation (utils/abs (- % pos)))) 80 | (apply +))) 81 | ``` 82 | 83 | Well we can write the `part2` function. And oh look - it's a copy-paste job from the `part1` function, except that 84 | we are calling `summation-distances-to` instead of `distances-to`. 85 | 86 | ```clojure 87 | (defn part2 [input] 88 | (let [crabs (parse-crabs input) 89 | [first-crab last-crab] (apply (juxt min max) crabs)] 90 | (->> (range first-crab (inc last-crab)) 91 | (map (partial summation-distances-to crabs)) 92 | (apply min)))) 93 | ``` 94 | 95 | --- 96 | 97 | ## Refactor! 98 | 99 | Alright, I can begrudgingly accept one copy-paste job, but not two. So let's make this cleaner. 100 | 101 | The first approach we can take is having a `solve` function where we pass in either `distances-to` or 102 | `summation-distances-to`. 103 | 104 | ```clojure 105 | (defn distances-to [crabs pos] ...) 106 | (defn summation-distances-to [crabs pos] ...) 107 | 108 | (defn solve [f input] 109 | (let [crabs (parse-crabs input) 110 | [first-crab last-crab] (apply (juxt min max) crabs)] 111 | (->> (range first-crab (inc last-crab)) 112 | (map (partial f crabs)) 113 | (apply min)))) 114 | 115 | (defn part1 [input] (solve distances-to input)) 116 | (defn part2 [input] (solve summation-distances-to input)) 117 | ``` 118 | 119 | That's a good start, since it combines shared logic between `part1` and `part2`, but we still have code duplication 120 | between `distances-to` and `summation-distances-to`, so we aren't done yet. What we really want to do is make a 121 | common function called `total-crab-distances-to`, which will sum up the distances from every crab to a position, 122 | applying the input function `f` to each distance. For part 1, we don't need to mutate the distances in any way, so 123 | we'll pass in `identity`. For part 2, we need the summation value of the distance, so we'll pass in 124 | `utils/summation`. Then the `solve` function just needs to pass that function `f` through to `total-crab-distance-to` 125 | to get to the answer. 126 | 127 | ```clojure 128 | (defn total-crab-distance-to [f crabs pos] 129 | (->> crabs 130 | (map #(-> % (- pos) utils/abs f)) 131 | (apply +))) 132 | 133 | (defn solve [f input] 134 | (let [crabs (parse-crabs input) 135 | [first-crab last-crab] (apply (juxt min max) crabs)] 136 | (->> (range first-crab (inc last-crab)) 137 | (map (partial total-crab-distance-to f crabs)) 138 | (apply min)))) 139 | 140 | (defn part1 [input] (solve identity input)) 141 | (defn part2 [input] (solve utils/summation input)) 142 | ``` 143 | -------------------------------------------------------------------------------- /docs/day14.md: -------------------------------------------------------------------------------- 1 | # Day Fourteen: Extended Polymerization 2 | 3 | * [Problem statement](https://adventofcode.com/2021/day/14) 4 | * [Solution code](https://github.com/abyala/advent-2021-clojure/blob/master/src/advent_2021_clojure/day14.clj) 5 | 6 | --- 7 | 8 | ## Preamble 9 | 10 | Today we're going to play with string manipulation... sort of. We're given an initial polymer, and a mapping of 11 | two-character strings to the character we must insert between them. This results in the polymer growing in size very 12 | quickly, and we need to subtract the number of occurrences of the least common character from the most common character 13 | after a number of generations. 14 | 15 | I enjoyed this problem because part 1 was very simple, but part 2 took a moment to consider because it suggested I 16 | needed a more efficient algorithm. The key point is that we don't actually care about the polymer string itself; all 17 | that matters is how many instances of each character pair appears within the list. 18 | 19 | For instance, given the rules `{"AB" "C", "BA" "D", "AA" "X"}`, the input polymer `"AABA"` becomes `"AXACBDA"`, while 20 | the input polymer `"ABAA"` becomes `"ACBDAXA"`. Both of those output strings have the same 2-character pairs of 21 | internal strings, namely `("AC" "AX" "BD" "CB" "DA" "XA")`. Therefore, the strings themselves don't actually matter; 22 | it's just the number of each character pair in the string. 23 | 24 | With that understanding, let's get to the code. 25 | 26 | --- 27 | 28 | ## Parts 1 and 2 29 | 30 | First off, we'll parse the input. I took a slightly different approach from my usual strategy of parsing everything 31 | into individual lines, based on some tricks I've seen in previous Advent solutions from the Clojure community. So 32 | today, I still use my `split-blank-line` function to separate the first line (the template) from the multi-line rules. 33 | But this time, I use `re-seq` on all of the rule lines together, taking apart just the words. Given that long sequence, 34 | I call `(partition 2 words)` to collect them into pairs, then `map` each list pair into a vector, so I can collect 35 | them all into a map. The result of parsing the input is a two-element vector of the initial template and the map of 36 | 2-character strings to the 1-character string to inject between them. 37 | 38 | ```clojure 39 | (defn parse-input [input] 40 | (let [[template rules] (u/split-blank-line input)] 41 | [template 42 | (->> (re-seq #"\w+" rules) (partition 2) (map vec) (into {}))])) 43 | ``` 44 | 45 | Now really quickly, I'm going to make a helper function called `update-add`, which sort of acts like a map with a 46 | default value, since we saw in Day 12 that if you try to update a map by calling `inc` or `+`, when the current key is 47 | not in the map, you'll get a NullPointerException. So `update-add` lets us still update the map by essentially allowing 48 | the default value to be a zero. We'll use this several times in the solution. 49 | 50 | ```clojure 51 | (defn update-add [m k n] (update m k #(+ (or % 0) n))) 52 | ``` 53 | 54 | Next we'll create the function `apply-rules`, which takes in the rule set and the frequencies of string pairs. It's 55 | just a simple use of `reduce`, but with a heck of a deconstructor on its input. Remember that our `freqs` function 56 | argument is a map of structure `{pair1 c1, pair2 c2}` or `{[a1 b1] c1, [a2 b2] c2}`. Since maps are themselves 57 | sequences, we can think of the map as a big sequence of key-value pairs, like `([pair1 c1], [pair2 c2])`, which again 58 | looks like `([[a1 b1] c1], [[a2 b2] c2])`. Thus because the reducing function takes an accumulated value and the next 59 | pair in the `freqs` map, we deconstruct it to `(fn [acc [[a b :as word] n]])`. 60 | 61 | With that ready to go, the reduction is simple. We find the character `c` in the rules for our `a-b` word, and in the 62 | next generation each of those pairs will translate into the same number of `ac` and `cb` pairs. So we'll use our handy 63 | `update-add` function to add `n` instances of both strings to the accumulating map of frequencies. 64 | 65 | ```clojure 66 | (defn apply-rules [rules freqs] 67 | (reduce (fn [acc [[a b :as word] n]] (let [c (rules word)] 68 | (-> acc 69 | (update-add (str a c) n) 70 | (update-add (str c b) n)))) 71 | {} freqs)) 72 | ``` 73 | 74 | Alright, let's work out the score for a given "string," which again we are representing as a frequency map. To start, 75 | we need to convert the frequency map of string pairs to the frequency map of individual characters. To do that, we must 76 | recognize that if we had the string `aabaab`, the map would be `{"aa" 2, "ab" 2, "ba" 1}`. Looking at just the first 77 | three characters, `aab`, we note that the second character of the first pair is the first character of the second pair, 78 | so we don't want to count both letters within each pair. We just need to count the first of each pair, so long as we 79 | add in the very last letter of the _original_ input, since that will never change from generation to generation. Once 80 | we have that value, named below `char-freqs`, we need to find the largest and smallest frequency; note again that we 81 | don't care about the characters themselves, so we can call `(sort-by - (vals char-freqs))` to pull out the frequency 82 | counts using `vals` before sorting from largest to smallest. Finally, we use `juxt` to grab the first and last values 83 | (largest and smallest, respectively) and subtract the two. 84 | 85 | ```clojure 86 | (defn score [initial-template freqs] 87 | (let [char-freqs (reduce (fn [acc [[a] n]] (update-add acc a n)) 88 | {(last initial-template) 1} 89 | freqs) 90 | sorted-instances (sort-by - (vals char-freqs))] 91 | (apply - ((juxt first last) sorted-instances)))) 92 | ``` 93 | 94 | We're in the home stretch, because it's time for our solve function! First we'll parse the input, and then we need to 95 | transform the polymer template into the initial frequency map by pairing together all of the characters before calling 96 | `frequencies`. Once we have that map, we'll use `iterate` to make an infinite sequence of new frequency maps for each 97 | generation, skip to the correct generation, and score it. Remember that we need to pass in the original template to the 98 | `score` function so we get that last character. 99 | 100 | ```clojure 101 | (defn solve [input step] 102 | (let [[template rules] (parse-input input) 103 | initial-freqs (->> template (partition 2 1) (map (partial apply str)) frequencies)] 104 | (->> (iterate (partial apply-rules rules) initial-freqs) 105 | (drop step) 106 | first 107 | (score template)))) 108 | ``` 109 | 110 | Now we just have to invoke the `solve` method with the generation number, which is `10` for part 1 and `40` for part 2. 111 | 112 | ```clojure 113 | (defn part1 [input] (solve input 10)) 114 | (defn part2 [input] (solve input 40)) 115 | ``` -------------------------------------------------------------------------------- /docs/day25.md: -------------------------------------------------------------------------------- 1 | # Day Twenty-Five: Sea Cucumber 2 | 3 | * [Problem statement](https://adventofcode.com/2021/day/25) 4 | * [Solution code](https://github.com/abyala/advent-2021-clojure/blob/master/src/advent_2021_clojure/day25.clj) 5 | 6 | --- 7 | 8 | ## Preamble 9 | 10 | As tends to happen, Day 25 is a fairly easy problem. I decided to have a little fun with it, just to make the solution 11 | a little extra fun, but it wasn't strictly necessary. 12 | 13 | For my solution I leveraged just a tiny bit of `defmulti` and `defmethod`, since I hadn't used them all year. The 14 | `defmulti` macro defines a function that dispatches to one of several implementations, based on how the multi-method 15 | dispatch is configured. Java sort of has two ways of doing this - there's polymorphism by inheritance (a method on an 16 | interface or class gets implemented or overridden at runtime), and there are overloaded methods (several methods on 17 | a class or interface with the same name but different argument types). In Clojure, we have full control over how to 18 | dispatch a multi-method, based on the method arguments. 19 | 20 | For instance, I'll make a multimethod called "describe arguments," which takes in a variable number of arguments, and 21 | returns a String description of them. I'll implement four multi-method implementations for 0, 1, 2, or any other number 22 | of arguments. Try doing _that_ in Java! 23 | 24 | ```clojure 25 | (defmulti describe-arguments (fn [& args] (count args))) 26 | (defmethod describe-arguments 0 [] "Nothing") 27 | (defmethod describe-arguments 1 [v] (str "One item of type " (type v))) 28 | (defmethod describe-arguments 2 [v0 v1] (str "Two items, whose equality is " (= v0 v1))) 29 | (defmethod describe-arguments :default [& vs] (str (count vs) " items, beginning with " (first vs))) 30 | 31 | (describe-arguments) 32 | => "Nothing" 33 | (describe-arguments "Something") 34 | => "One item of type class java.lang.String" 35 | (describe-arguments "Foo" "Bar") 36 | => "Two items, whose equality is false" 37 | (describe-arguments "Foo" "Bar" "Baz") 38 | => "3 items, beginning with Foo" 39 | ``` 40 | 41 | So again, this is absolutely overkill, but I'll use a multimethod for fun today. 42 | 43 | --- 44 | 45 | ## Part 1 46 | 47 | We are given a map of down and right arrows, representing the movement patterns of cucumbers. We need to detect how 48 | many turns it will take until they don't move. 49 | 50 | I originally made a simple data structure of `{[x y] value}` for every point in the map, but I made it faster by 51 | storing separate sets of the right-facing coordinates and down-facing coordinates, plus the max `x` and `y` values in 52 | the map. For convenience that we'll see later, I named my set keys `:right` and :down`. 53 | 54 | ```clojure 55 | (defn parse-map [input] 56 | (let [points (point/parse-to-char-coords input) 57 | max-x (apply max (map ffirst points)) 58 | max-y (apply max (map (comp second first) points))] 59 | (reduce (fn [acc [coords c]] (case c 60 | \> (update acc :right conj coords) 61 | \v (update acc :down conj coords) 62 | \. acc)) 63 | {:right #{}, :down #{}, :max-x max-x, :max-y max-y} 64 | points))) 65 | ``` 66 | 67 | Next, we want to know what where each sea cucumber wants to move to, based on the direction it's facing, and that the 68 | map wraps around the edges. So we'll make our over-designed multi-method take in the map, the coordinates, and the 69 | direction we're facing, where we dispatch on the direction. The we create two implementation methods to provide the 70 | target coordinates. 71 | 72 | ```clojure 73 | (defmulti target-from (fn [_ _ dir] dir)) 74 | (defmethod target-from :right [m [x y] _] (if (>= x (:max-x m)) [0 y] [(inc x) y])) 75 | (defmethod target-from :down [m [x y] _] (if (>= y (:max-y m)) [x 0] [x (inc y)])) 76 | ``` 77 | 78 | Overkill and bad design for the problem statement? Absolutely. Neat? I think so! 79 | 80 | Now let's move all available sea cucumbers in a single direction, either `:right` or `:down`, which as usual we can 81 | accomplish with a simple `reduce`. We'll reduce over `(dir m)` to get all coordinates with the cucumbers moving in the 82 | desired direction, and we'll start with the current state of the map. For each cucumber, we'll determine the target 83 | it's trying to reach, and make sure it's not occupied by checking if either `(:right m)` or `(:down m)`, both 84 | returning sets, contain the target. If one of them does, don't change the accumulating map. If the spot is vacant, then 85 | the set of coordinates in this direction loses the original coordinates and adds the target coordinates. Note that when 86 | checking for an occupant, it's important to compare against the original map `m` instead of the accumulated map `acc`, 87 | since all cucumbers in one direction move at the same time, politely. 88 | 89 | ```clojure 90 | (defn move-all-in-direction [m dir] 91 | (reduce (fn [acc coords] 92 | (let [target (target-from m coords dir)] 93 | (if ((some-fn (:right m) (:down m)) target) 94 | acc 95 | (-> acc 96 | (update dir conj target) 97 | (update dir disj coords))))) 98 | m 99 | (dir m))) 100 | ``` 101 | 102 | Now we can put it all together. First, we'll create `take-turn`, which moves all cucumbers in the map first right and 103 | then down. We could use the thread-first macro as always, but I think that another `reduce` that goes `[:right :down]` 104 | is a neat way of looking at things. This also shows that if a reducing function takes in two arguments, the 105 | accumulated value and the next value, we can use the function by name instead of needing a lambda. And since the 106 | arguments for `move-all-in-direction` are `[m dir]`, look how neat `take-turn` can look! 107 | 108 | ```clojure 109 | ; I think this structure is neat, in case the cucumbers start moving in stranger patterns in the future. 110 | (defn take-turn [m] 111 | (reduce move-all-in-direction m [:right :down])) 112 | 113 | ; Currently it's equivalent to the more explicit code: 114 | (defn take-turn [m] 115 | (-> m 116 | (move-all-in-direction :right) 117 | (move-all-in-direction :down))) 118 | ``` 119 | 120 | Then `sea-cucumber-seq` returns the sequence of all distinct maps of sea cucumbers. I opted to use a `lazy-seq` instead 121 | of my more typical solution of `iterate`, `(partition 2 1)`, and `take-while`, because this makes it easier to include 122 | both the initial map value and the final one. The function returns the current map and lazily evaluates the next map 123 | if it has changed. Then all that `part1` has to do is parse the map, create the sequence, and count the distinct number 124 | of maps. 125 | 126 | ```clojure 127 | (defn sea-cucumber-seq [m] 128 | (let [next-map (take-turn m)] 129 | (if (= m next-map) 130 | (lazy-seq [m]) 131 | (lazy-seq (cons m (sea-cucumber-seq next-map)))))) 132 | 133 | (defn part1 [input] (->> input parse-map sea-cucumber-seq count)) 134 | ``` 135 | 136 | That's it! Now, once I finish up day 19's problem, this will be another successful year of Advent Of Code! -------------------------------------------------------------------------------- /docs/day01.md: -------------------------------------------------------------------------------- 1 | # Day One: Sonar Sweep 2 | 3 | * [Problem statement](https://adventofcode.com/2021/day/1) 4 | * [Solution code](https://github.com/abyala/advent-2021-clojure/blob/master/src/advent_2021_clojure/day01.clj) 5 | * [Additional solution code](https://github.com/abyala/advent-2021-clojure/blob/master/src/advent_2021_clojure/day01-windowed.clj) 6 | 7 | --- 8 | 9 | ## Part 1 10 | 11 | The problem starts with us receiving an input string of one integer per line, which we need to turn into a sequence 12 | of integers. We solve this with a simple parsing function, `parse-measurements`, which splits the input by each line, 13 | and then maps each individual String using the Java function `Integer.parseInt` within a lambda call. 14 | 15 | ```clojure 16 | (defn parse-measurements [input] 17 | (->> (str/split-lines input) 18 | (map #(Integer/parseInt %)))) 19 | ``` 20 | 21 | The problem needs us to count up the number of times a measurement is greater than the previous measurement. To do this, 22 | we use the `partition` function to create sequences of length 2 with a step of 1, such that we get every adjacent pair. 23 | This means that `(partition 2 1 (range 4))` would convert `(0 1 2 3)` to `((0 1) (1 2) (2 3))`. After that, we apply a 24 | filter for the appropriate pairs, and count them up. 25 | 26 | I've shown two ways to implement the filter. In the first instance, I destructured the pair into `[a b]` and then 27 | simply checked the predicate `(< a b)`. However, even though that syntax works, I thought a partial function would look 28 | nicer, since all we need to check is that "each" value in the pair is less than the next one. Thus the function 29 | `(partial apply <)` does the same thing without an explicit lambda. The `partial` function says "run this against all 30 | subsequent values you provide as arguments," which again in this case is a single sequence with two values. The 31 | `apply` function says to call the `<` function on each value _within_ the sequence. 32 | 33 | ```clojure 34 | ; Original implementation 35 | (defn part1 [input] 36 | (->> (parse-measurements input) 37 | (partition 2 1) 38 | (filter (fn [[a b]] (< a b))) 39 | count)) 40 | 41 | ; Cleaner implementation 42 | (defn part1 [input] 43 | (->> (parse-measurements input) 44 | (partition 2 1) 45 | (filter (partial apply <)) 46 | count)) 47 | ``` 48 | 49 | Easy enough. Let's see what part 2 brings us! 50 | 51 | --- 52 | 53 | ## Part 2 54 | 55 | This part of the puzzle is very similar to the first, except that instead of checking for increasing values in the 56 | measurements themselves, we need to look for increasing values in the rolling sum of three values. There's really 57 | nothing to this, if we copy-paste part 1 and make a tiny change. 58 | 59 | After we parse the measurements, we need to make a new rolling partition of size 3 and step 1, and then add those 60 | values up. Once we're done and we have a new sequence of integers, we again partition our pairs and do our filter. 61 | I use another partial function, this time mapping each three-tuple to the sum of its values, using 62 | `(map (partial apply +) sequence-of-three-tuples)`. Put it together, and we have a working solution. 63 | 64 | ```clojure 65 | (defn part2 [input] 66 | (->> (parse-measurements input) 67 | (partition 3 1) 68 | (map (partial apply +)) 69 | (partition 2 1) 70 | (filter (partial apply <)) 71 | count)) 72 | ``` 73 | 74 | --- 75 | 76 | ## Cleanup 77 | 78 | Ok, that worked just fine, but let's do a little cleanup to make things nicer. First of all, while we wait for the 79 | next GA version of Clojure that introduces [CLJ-2667](https://clojure.atlassian.net/browse/CLJ-2667), I really hate 80 | having to call `(map #(Integer/parseInt %) sequence)` to get to the Java static method 81 | `Integer.parseInt`. It's just ugly. So I created a utils namespace to define a nice `parse-int` function to use. 82 | 83 | ```clojure 84 | (defn parse-int [s] (Integer/parseInt s)) 85 | ``` 86 | 87 | And then, even though it's sort of bad form, I `refer` to it in my `day01` namespace. It should be in the 88 | `clojure.core` namespace and we all know it, so this is me sticking it to the man. Take that, nobody caring at all! 89 | 90 | ```clojure 91 | (defn parse-measurements [input] 92 | (->> (str/split-lines input) 93 | (map parse-int))) 94 | ``` 95 | 96 | Next, let's address the "issue" where parts 1 and 2 are so very similar. In theory, here's what the algorithm should 97 | look like: 98 | 1. Parse the input string into a sequence of integers for each measurement. 99 | 2. Transform that numeric sequence into a different numeric sequence, based on what each part requires. 100 | 1. For part 1, no transformation is necessary. 101 | 2. For part 2, transform that sequence into the sum of the three-measurement sliding window. 102 | 3. Count the number of adjancent pairs with increasing values. 103 | 104 | Piece of cake. Starting with part 1, we'll make our unified `solve` function, and have `part1` send it the input 105 | string and the `identity` function, so the transformation does nothing. For part 2, we'll make a simple function 106 | `three-measurement-sliding-window` to create the sliding window, and then send it along. 107 | 108 | ```clojure 109 | (defn solve [input mapping] 110 | (->> (parse-measurements input) 111 | (mapping) 112 | (partition 2 1) 113 | (filter (partial apply <)) 114 | count)) 115 | 116 | (defn three-measurement-sliding-window [measurements] 117 | (->> (partition 3 1 measurements) 118 | (map (partial apply +)))) 119 | 120 | (defn part1 [input] (solve input identity)) 121 | (defn part2 [input] (solve input three-measurement-sliding-window)) 122 | ``` 123 | 124 | There we go, all clean. On to day 2! 125 | 126 | --- 127 | 128 | ## Refactoring Once More 129 | 130 | A coworker of mine correctly observed that for part 2, we don't need to sum the values at all. If the first four 131 | values of the sequence are `(a b c d)`, then `(< (+ a b c) (+ b c d))` is equivalent to `(< a d)`. As a result, 132 | all we need to do is determine how many numbers apart we need to compare values. For part 1, we compare each 133 | adjacent pair, and for part 2 we compare numbers that are 3 spaces apart. 134 | 135 | So let's make this happen with a few small revisions to the `solve` function. Instead of taking in a mapping function, 136 | we'll instead take in the `window-size` we need; part 1 has a window size of two for the adjacent values, and part 2 137 | has a window size of four (one more than the triple). The `solve` function parses the measurements, creates 138 | partitions of the correct size, and then calls everyone's favorite function `juxt`. The `juxt` function applies a 139 | number of functions to an input, returning a vector of the responses. In this case, we'll map `(juxt first last)` to 140 | each windowed sequence, such that each sequence returns a two-element vector. From there, we apply the same old 141 | filter, and count up the results. 142 | 143 | ```clojure 144 | (defn solve [input window-size] 145 | (->> (parse-measurements input) 146 | (partition window-size 1) 147 | (map (juxt first last)) 148 | (filter (partial apply <)) 149 | count)) 150 | 151 | (defn part1 [input] (solve input 2)) 152 | (defn part2 [input] (solve input 4)) 153 | ``` -------------------------------------------------------------------------------- /docs/day13.md: -------------------------------------------------------------------------------- 1 | # Day Thirteen: Transparent Origami 2 | 3 | * [Problem statement](https://adventofcode.com/2021/day/13) 4 | * [Solution code](https://github.com/abyala/advent-2021-clojure/blob/master/src/advent_2021_clojure/day13.clj) 5 | 6 | --- 7 | 8 | ## Preamble 9 | 10 | Today's puzzle was really interesting, until I got stuck on the tiniest silly algorithmic bug that stumped me for 11 | several hours. I ended up coding it up once, then again twice in a worse way, then bashed my head against the wall 12 | until I found the bug. What you see below is the original solution, minus that silly bug. Still, it was a terrific 13 | puzzle once I fumigated my brain. 14 | 15 | --- 16 | 17 | ## Part 1 18 | 19 | The puzzle gives us a piece of paper with `x,y` pairs that correspond to dots on a piece of paper, and instructions 20 | on how to fold the paper. The idea is that after folding a paper, a dot may appear at a point from either the 21 | "unfolding" side, or from the folding side once we project its new coordinates across the fold line. 22 | 23 | Since we're just playing with dots, the easiest data structure I can see is a set of all `[x, y]` coordinates that 24 | have dots, plus a sequence of folding instructions. We can create a two-element sequence from the input file by 25 | using `utils/split-blank-line` as we've seen before. Parsing the set of points isn't too difficult. We look at each 26 | row of input, split each row into two strings that are separated by a comma, and map each side to an integer. This 27 | gives us a sequence of coordinates, which we throw into a set. 28 | 29 | Parsing the fold instructions is _just_ difficult enough to extract out a `parse-instruction` function to apply to 30 | each line of instruction input. We'll use a regex to pull out the String `"x"` or `"y"` plus the row or column number 31 | to extract. We'll parse the latter into an integer, and convert the former into a keyword because that's what we do 32 | in Clojure. 33 | 34 | Thus the `parse-paper` function returns a two-element sequence of `(set-of-dots, sequence-of-instructions)`. 35 | 36 | ```clojure 37 | (defn parse-instruction [line] 38 | (let [[_ axis amount] (re-matches #"fold along ([xy])=(\d+)" line)] 39 | [(keyword axis) (parse-int amount)])) 40 | 41 | (defn parse-paper [input] 42 | (let [[dots folds] (utils/split-blank-line input)] 43 | [(->> (str/split-lines dots) 44 | (map #(mapv parse-int (str/split % #","))) 45 | set) 46 | (map parse-instruction (str/split-lines folds))])) 47 | ``` 48 | 49 | All of the magic goes into the `fold` function. Shout out to my Kotlin (and Scala?) friends for whom "fold" is 50 | equivalent to Clojure's `reduce` function - I'm using `fold` here because I can! Anyway, it's actually rather simple. 51 | First, we'll deconstruct the instruction back into its direction and fold-line components. Then we'll create 52 | `coord-idx`, which maps `:x` to the first value in a coordinate pair, and `:y` to the second; this lets us have a 53 | single `fold` function instead of `fold-up` and `fold-left` functions. 54 | 55 | For each dot currently on the paper, we'll pull out the relevant component of the coordinate using `coord-idx`; 56 | assuming a point `[10 15]` and a horizontal fold along `x=8`, the value we look at is `10`. If the dot is not on the 57 | folding side (the value is less than the fold line), the point will just map to itself. If the dot _is_ on the folding 58 | side, we need to update is ordinate by projecting it across the line. In our example `x=10` and `fold-line=8`, using 59 | "normal" arithmatic instead prefix notation, the distance from `x` to the line is `(x - line)`, so the target is 60 | `line - (x - line)` which becomes `(2 * line - x)`. Once we map each point to its new location, we push the sequence 61 | into a set so we can avoid duplicates. This isn't strictly necessary, but the datatypes are more intuitive this way. 62 | 63 | ```clojure 64 | (defn fold [dots [dir fold-line]] 65 | (let [coord-idx ({:x 0 :y 1} dir)] 66 | (->> dots 67 | (map (fn [dot] 68 | (let [v (dot coord-idx)] 69 | (if (<= v fold-line) 70 | dot 71 | (update dot coord-idx (partial - (* 2 fold-line))))))) 72 | set))) 73 | ``` 74 | 75 | Now it's easy to solve part 1. We just need to fold the paper once, and count the number of dots. Since I'm such a big 76 | fan of Clojure's destructuring, I'll draw attention to how we pull out only the first instruction from the sequence 77 | by making the binding `(let [[dots] [instruction]] (parse-paper input))`, which would be equivalent to 78 | `(let [[dots instructions] (parse-paper input), instruction (first instructions)])`. 79 | 80 | ```clojure 81 | (defn part1 [input] 82 | (let [[dots [instruction]] (parse-paper input)] 83 | (count (fold dots instruction)))) 84 | ``` 85 | 86 | ### Quick aside 87 | 88 | So what tripped me up for so long on this problem? It was the update function, as I did not use `(2 * line - x`). 89 | Instead, I originally decided to find the maximum `x` or `y` value on the board, and just called `(maximum - x)`. 90 | That worked for part 1, and _almost_ worked for part 2, except that if there were an even number of rows or columns, 91 | there would be data loss. 92 | 93 | --- 94 | 95 | ## Part 2 96 | 97 | For part 2, we just need to run the dots through all of the instructions, and print out the results such that we can 98 | read the 8 capital letters. 99 | 100 | Since the data structure for the dots is a map instead of nested vectors (at least this implementation is!), we need 101 | to create lines of strings from the map of points. We'll find the min and max values of `x` and `y` across the points, 102 | and then use nested `map` functions and `range`s to convert each row ito a String of hash marks and spaces. 103 | 104 | The only unusual part of the `print-dots` function is the use of the `run!` function. We know that `run!` is unusual 105 | because it ends in an exclamation mark, which by convention is a way of telling the developer that the function has 106 | side effects. `run!` applies a function to every element in a collection; in this case, we want to just call `println` 107 | on each row of strings. I wonder it's called `println` instead of `println!` since it has side effects... 108 | 109 | ```clojure 110 | (defn print-dots [dots] 111 | (let [min-x (apply min (map first dots)) 112 | min-y (apply min (map second dots)) 113 | max-x (apply max (map first dots)) 114 | max-y (apply max (map second dots))] 115 | (run! println (map (fn [y] 116 | (apply str (map #(if (dots [% y]) \# \space) 117 | (range min-x (inc max-x))))) 118 | (range min-y (inc max-y)))))) 119 | ``` 120 | 121 | And now, we just reduce the `fold` function over the dots and the instructions, and print out the results. By calling 122 | `(reduce fold dots instructions)` and combining rivals `reduce` and `fold`, we're bringing about world peace, one 123 | puzzle at a time! 124 | 125 | ```clojure 126 | (defn part2 [input] 127 | (let [[dots instructions] (parse-paper input)] 128 | (print-dots (reduce fold dots instructions)))) 129 | ``` 130 | 131 | ### That bug again 132 | 133 | How did my output look when I used the wrong arithmetic on my dot mapping function? Check it out below. With enough 134 | trial and error, I could have figured out what each of those letters were, but what's the point of that? 135 | 136 | ```clojure 137 | ;; Incorrect mapping function 138 | 139 | ;; ## ## # #### #### # #### # ## # # 140 | ;; # # # # #### # # # ### #### 141 | ;; # #### ## #### # # ## ### #### 142 | ;;# # ### ## # # # # ## # # # # 143 | ;;## # # ## #### ### #### #### # # # # 144 | 145 | ;; Correct mapping function 146 | 147 | ;; ## ### #### ### # ## # # # # 148 | ;; # # # # # # # # # # # # # 149 | ;; # # # # ### # # ## #### 150 | ;; # ### # # # # # ## # # # # 151 | ;;# # # # # # # # # # # # # # 152 | ;; ## # # #### ### #### ### # # # # 153 | ``` 154 | -------------------------------------------------------------------------------- /docs/day09.md: -------------------------------------------------------------------------------- 1 | # Day Nine: Smoke Basin 2 | 3 | * [Problem statement](https://adventofcode.com/2021/day/9) 4 | * [Solution code](https://github.com/abyala/advent-2021-clojure/blob/master/src/advent_2021_clojure/day09.clj) 5 | 6 | --- 7 | 8 | ## Preamble 9 | 10 | This year, I seem to be on a quest to replace anonymous functions of the form `#(foo %)` with partial functions and 11 | the use of `juxt` and `comp`. I'm not sure why that is, but it seems to be a pattern. I have discussed `partial`, 12 | `juxt`, and `comp` in previous problems this season, so I won't be explaining them anymore, so feel free to look back 13 | if things are starting to look funny. 14 | 15 | --- 16 | 17 | ## Part 1 18 | 19 | When exploring underwater caves, one does run the risk of running into 20 | [liquid hot magma](https://www.youtube.com/watch?v=UNJU-5vCrJc), which apparently we have done today. We need to 21 | navigate the cave carefully. In part 1, we need to identify all points within the cave that are local minima (smaller 22 | than all cardinal neighbors), increment their heights and add the results together. 23 | 24 | In most such problems, I tend to convert the entire input into one giant map of coordinates to values, in the shape of 25 | `{[x y] v}`, but for a change of pace I decided to go old-school and use nested vectors. So my core data structure 26 | is simply `[[values]]`. Thus to parse the cave from the initial input, I split the input string into a sequence of 27 | strings for each line, and then we have a fun line `(mapv (partial mapv (comp parse-int str)) line)`. So let's piece 28 | this together. The outer `mapv` says we're going to create a vector from mapping each line to the internal function; 29 | we need `mapv` to ensure we get an outer vector, since we'll be accessing it by index. The inner function can be 30 | partial because we're calling `(mapv (comp parse-int str) line)` where `line` is fed in from the outer `mapv`. Each 31 | inner line is a numeric string, so we need to convert each character to its integer value. I implemented a `char->int` 32 | function in the `utils` package to perform this. 33 | 34 | ```clojure 35 | ; advent-2021-clojure.utils namespace 36 | (defn char->int [c] (- (int c) 48)) 37 | 38 | ; advent-2021-clojure.day09 namespace 39 | (defn parse-cave [input] (->> (str/split-lines input) 40 | (mapv (partial mapv utils/char->int)))) 41 | ``` 42 | 43 | Next, we need to find all of the lowest points in the cave, so we'll need four functions to get there. First, the 44 | `neighbors` function in the `advent-2021-clojure.point` namespace will return the coordinates of the four points 45 | adjacent to a single point. This is one of those instances of using `(mapv f coll1 coll2)` instead of the normal 46 | `(mapv f coll)`, where the mapping function gets applied to the nth instance of multiple collections. This lets us 47 | add the `x` and `y` coordinates of two points together. 48 | 49 | The `lowest-point?` function checks if a single point in a cave is a local low point. This is the first of several 50 | instances where we'll be calling `(get-in cave point)`. `get-in` takes in a collection and a vector of nested values 51 | to apply to get to a sub-value in a collection, or `nil` if that value does not exist in the collection. So 52 | `(get-in {:name {:first "Franklin" :middle "Delano" :last "Roosevelt"}} [:name :middle])` returns `"Delano"`, and for 53 | vectors, `(get-in [:a :b [:c :d :e]] [2 2])` returns `:e`. Thus we get the value of a point (which is a vector) within 54 | the cave by calling `(get-in cave point)`. Then we find all neighbors to the given point, keep the ones that exist 55 | within the cave by calling `keep`, and then ensure that the point's height is lower than all of its neighbors. 56 | 57 | With that function, it's easy to find all of the lowest points. `all-coords` will return all `[y x]` coordinates in 58 | the cave; note that we use `[y x]` instead of `[x y]` because the outer vector of our `[[values]]` data structure is 59 | the row, which is indexed by `y`, and the inner vector is the column, which is indexed by `x`. Finally, in 60 | `lowest-points`, we take all of the coordinates in the cave, and filter them to the ones that pass `lowest-point?`. 61 | 62 | ```clojure 63 | ; advent-2021-clojure.point namespace 64 | (defn neighbors [point] 65 | (map (partial mapv + point) [[0 1] [0 -1] [-1 0] [1 0]])) 66 | 67 | ; advent-2021-clojure.day09 namespace 68 | (defn lowest-point? [cave point] 69 | (let [height (get-in cave point)] 70 | (->> (neighbors point) 71 | (keep (partial get-in cave)) 72 | (every? (partial < height))))) 73 | 74 | (defn all-coords [cave] (for [y (-> cave count range) 75 | x (-> cave first count range)] 76 | [y x])) 77 | 78 | (defn lowest-points [cave] 79 | (filter (partial lowest-point? cave) (all-coords cave))) 80 | ``` 81 | 82 | Finally, we're ready to write `part1`. Starting with the coordinates of all of the lowest points in the cave, we map 83 | them to their actual values, increment each one, and add them together. 84 | 85 | ```clojure 86 | (defn part1 [input] 87 | (let [cave (parse-cave input)] 88 | (->> (lowest-points cave) 89 | (map (partial get-in cave)) 90 | (map inc) 91 | (apply +)))) 92 | ``` 93 | 94 | Have we reached the low point of our problem today? Let's find out. 95 | 96 | --- 97 | 98 | ## Part 2 99 | 100 | Now we need to find all of the "basins" in the cave, a section whose phrasing I found incredibly misleading. The 101 | instructions say, in part, "A basin is all locations that eventually flow downward to a single low point" 102 | (emphasis added). That last part is untrue. What the problem seems to mean is "a basin is all locations that are 103 | fully enclosed by either the outer wall or a 9." It appears that a basin can happily contain two or more low points. 104 | 105 | So the way I want to find the basins is to go to each lowest point, and find the basin around it by moving outward 106 | until we hit either a wall or a 9. For this, we'll first start with a `high-point?` function, which just returns 107 | whether a coordinate in the cave equals the max value of 9. 108 | 109 | Then, the `basin-around` function takes in a cave and a low point, and returns the set of all coordinates within that 110 | basin. We'll use the recursive `loop-recur` construct, in which I often use the bindings of `candidates` (what I'm 111 | looping over) and `found` (the accumulated values). If there are candidate points to inspect, which we can tell by 112 | calling `(if-some [point (first candidates)])`, then we continue to the loop, and if not then we return the set of 113 | found points. When looping, we remove the current point from the `candidates` with `(rest candidates)`, and then add 114 | the all neighbors of that point that are in the cave, but which are not either already found or are themselves high 115 | points. 116 | 117 | ```clojure 118 | (def max-height 9) 119 | (defn high-point? [cave point] (= max-height (get-in cave point))) 120 | 121 | (defn basin-around [cave lowest-point] 122 | (loop [candidates #{lowest-point}, found #{}] 123 | (if-some [point (first candidates)] 124 | (recur (reduce conj (rest candidates) (->> (point/neighbors point) 125 | (filter (partial get-in cave)) 126 | (remove (some-fn found (partial high-point? cave))))) 127 | (conj found point)) 128 | found))) 129 | ``` 130 | 131 | And then we're ready to build `part2`. Starting from each of the lowest points, we map each one first to its basin 132 | (expressed as its set of points), which we then convert to the number of points within the basin. Then 133 | `(sort-by -)` puts them in reverse size order, from which we take the top three and multiply those values together. 134 | 135 | ```clojure 136 | (defn part2 [input] 137 | (let [cave (parse-cave input)] 138 | (->> (lowest-points cave) 139 | (map (comp count (partial basin-around cave))) 140 | (sort-by -) 141 | (take 3) 142 | (apply *)))) 143 | ``` -------------------------------------------------------------------------------- /docs/day20.md: -------------------------------------------------------------------------------- 1 | # Day Twenty: Trench Map 2 | 3 | * [Problem statement](https://adventofcode.com/2021/day/20) 4 | * [Solution code](https://github.com/abyala/advent-2021-clojure/blob/master/src/advent_2021_clojure/day20.clj) 5 | 6 | --- 7 | 8 | ## Part 1 9 | 10 | Today we're simulating a Game Of Life as represented by an image whose pixels are either light or dark with each 11 | generation. Our goal is to run the game a certain number of times against an image atop an infinite background, and 12 | then count up the number of lit pixels in the end. 13 | 14 | To start off, let's parse the input, which comes in as a single line of the 512-character "algorithm," followed by 15 | the initial image. We'll just return that as a two-element vector, leveraging `utils/split-blank-line` to split the 16 | input into its two segments. For the algorithm, we'll make a `pixel-map` that converts `.` to zero and `#` to 1; calling 17 | `mapv` will ensure the result is a an indexed vector instead of a linked list. For the image, we'll want that to be a 18 | map of `{[x y] 0-or-1}`. We'll leverage our existing `point/parse-to-char-coords` to return a sequence of each 19 | coordinate pair to its character, and then associate the coords onto the resulting map by again mapping the character 20 | using the `pixel-map`. Naturally we'll use constants of `dark-pixel` and `light-pixel` to avoid repeating `0` and `1` 21 | throughout the solution. 22 | 23 | ```clojure 24 | (def dark-pixel 0) 25 | (def light-pixel 1) 26 | 27 | (defn parse-input [input] 28 | (let [pixel-map {\. dark-pixel, \# light-pixel} 29 | [alg image] (utils/split-blank-line input)] 30 | [(mapv pixel-map alg) 31 | (reduce (fn [m [coord c]] (assoc m coord (pixel-map c))) 32 | {} 33 | (point/parse-to-char-coords image))])) 34 | ``` 35 | 36 | Our goal will include going to every point in the current image, looking at its surrounding 3x3 grid, and construct a 37 | new image from the converted characters. But what do we do along the border? Well initially, every surrounding point 38 | will be dark (aka `0`), but the algorithm tells us whether that will be true for every generation. To determine this, 39 | we'll make an infinite `border-seq`, which takes in the algorithm and returns what every infinite border value will be 40 | in each generation. Imagining an initial point far away from our image; all nine points in its grid will be dark, 41 | meaning that its binary value will be `000000000` or just plain `0`. Some algorithms will map a `0` to `0` again, 42 | keeping the border dark, but some might map it to `1` to make it light. Then similarly, a distant point of all light 43 | values will have a binary value of `111111111` or `511`, which could again map each character to dark or light. So 44 | `block-of` will map `0` to `0`, and `1` to `511`, and we'll `iterate` on mapping each previous value to its block 45 | value, and then use `alg` to convert it to its new value at that index. 46 | 47 | For what it's worth, in the sample problem, `(get alg 0)` is zero, so the border is always dark. In my input, 48 | `(map alg [0 511])` was `[1 0]`, so the background flickered every generation from dark to light and back again. 49 | 50 | ```clojure 51 | (defn border-seq [alg] 52 | (let [block-of {dark-pixel 0, light-pixel 511}] 53 | (iterate (comp alg block-of) dark-pixel))) 54 | ``` 55 | 56 | Now we can create a `migrate-image` function, which takes in the algorithm, the value of all border coordinates, and 57 | the current image, and returns the next image by migrating every point. To do this, we'll use a simple `reduce-kv` to 58 | migrate each coordinate in the image, calling `next-value-at` for each coordinate. The `next-value-at` looks up all 59 | points surrounding the current one, maps it to either its value on the image or else the default value of the 60 | infinite border character. Then it concatenates all 9 characters into a binary string, which it converts into a number 61 | and finds within the algorithm vector. 62 | 63 | ```clojure 64 | (defn next-value-at [alg border image coords] 65 | (->> (point/surrounding true coords) 66 | (map #(or (image %) border)) 67 | (apply str) 68 | utils/parse-binary 69 | alg)) 70 | 71 | (defn migrate-image [alg border image] 72 | (reduce-kv (fn [m coord _] (assoc m coord (next-value-at alg border image coord))) 73 | {} image)) 74 | ``` 75 | 76 | We want to get to a function that returns every generation of the image, but there's only one issue to address first. 77 | While `migrate-image` and `next-value-at` takes into account the infinite border, with each generation we almost 78 | certainly will have at least one point along the perimeter that interacted with the infinite border, meaning that the 79 | points just outside the previous border of the image will need to be calculated in the next generation. To account for 80 | this, with each generation we'll need to expand the image by one row at the top and bottom, and by one column on the 81 | left and right. (In theory, we could just expand the entire image once to the maximum intended size, but that pollutes 82 | what each function does.) 83 | 84 | The `expand-image` function looks at the current min and max values for both `x` and `y`, and associates the border 85 | character to all coordinates of the surrounding perimeter. We implement `perimeter-points` in the `point` namespace 86 | by taking all `[x y]` coordinates from a top-left point to the bottom-right point. Then `expand-image` uses `reduce` 87 | to `assoc` the `border` onto each perimeter point, starting from the `image` map itself. 88 | 89 | ```clojure 90 | ; advent-2021-clojure.point namespace 91 | (defn perimeter-points [[x0 y0] [x1 y1]] 92 | (concat (for [x [x0 x1], y (range y0 (inc y1))] [x y]) 93 | (for [y [y0 y1], x (range (inc x0) x1)] [x y]))) 94 | 95 | ; advent-2021-clojure.day20 namespace 96 | (defn expand-image [image border] 97 | (let [min-max (juxt (partial apply min) (partial apply max)) 98 | [min-x max-x] (min-max (map ffirst image)) 99 | [min-y max-y] (min-max (map (comp second first) image))] 100 | (reduce #(assoc %1 %2 border) 101 | image 102 | (point/perimeter-points [(dec min-x) (dec min-y)] 103 | [(inc max-x) (inc max-y)])))) 104 | ``` 105 | 106 | So where are we now? We know the sequence of values of the infinite border, and we can use that sequence to both 107 | expand the current image one step into its perimeter, and then to migrate every point of the image based on its 108 | neighbors. The only major step remaining is to create `image-seq`, to generate an infinite sequence of every generation 109 | the image goes through as it migrates. This function takes in the algorithm and initial image, and then creates the 110 | `border-seq` in the background. For each generation, is expands and migrates the image on the current head of the 111 | border sequence, and then uses `lazy-seq` (similar to `iterate`) to create the next element of the sequence. We don't 112 | use `iterate` here because we can't use the same value for the border with each generation, but rather need to call 113 | `(rest borders)` each time. 114 | 115 | ```clojure 116 | (defn image-seq 117 | ([alg image] (image-seq alg image (border-seq alg))) 118 | ([alg image borders] (let [border (first borders) 119 | image' (->> (expand-image image border) 120 | (migrate-image alg border))] 121 | (lazy-seq (cons image' (image-seq alg image' (rest borders))))))) 122 | ``` 123 | 124 | Alright, let's create our `solve` function. We'll take in the input string and the number of enhancements we want to 125 | apply. The function parses the input, converts it into the sequence of images, and finds the `nth` value by dropping 126 | `(dec enhance-count)` values from the sequence and pulling the next value. From that image, it counts the number of 127 | values in the `[[x y] pixel]` pair by counting the number of pixels that are `light-pixel`, or `1`. 128 | 129 | ```clojure 130 | (defn solve [input enhance-count] 131 | (->> (parse-input input) 132 | (apply image-seq) 133 | (drop (dec enhance-count)) 134 | first 135 | (filter #(= light-pixel (second %))) 136 | count)) 137 | ``` 138 | 139 | Finally, `part1` just calls `solve`, looking for the number of pixels after the second enhancement. 140 | ```clojure 141 | (defn part1 [input] (solve input 2)) 142 | ``` 143 | 144 | --- 145 | 146 | ## Part 2 147 | 148 | Part 2 runs the same algorithm for fifty enhancements. The code we already have is fine; we'll get the answer in about 149 | 10-15 seconds, which is fast enough for me. 150 | 151 | ```clojure 152 | (defn part2 [input] (solve input 50)) 153 | ``` -------------------------------------------------------------------------------- /docs/day11.md: -------------------------------------------------------------------------------- 1 | # Day Eleven: Dumbo Octopus 2 | 3 | * [Problem statement](https://adventofcode.com/2021/day/11) 4 | * [Solution code](https://github.com/abyala/advent-2021-clojure/blob/master/src/advent_2021_clojure/day11.clj) 5 | 6 | --- 7 | 8 | ## Preamble 9 | 10 | Today's puzzle deals with watching bioluminescent dumbo octopuses flash in the water. When most of us are doing the 11 | problems each day, we just want to get to it. However, if you're reading this and didn't watch the 12 | [video clip of dumbo octopuses](https://www.youtube.com/watch?v=eih-VSaS2g0), now is a good time to do it! 13 | 14 | One other thing - according to both [this Grammarly article](https://www.grammarly.com/blog/octopi-octopuses/) and 15 | [this Merriam-Webster article](https://www.merriam-webster.com/words-at-play/the-many-plurals-of-octopus-octopi-octopuses-octopodes), while the 16 | plural form for "octopus" can be both "octopuses" or "octopi," neither is exclusively correct and the former is more 17 | English-sounding. So Twitter commenters can all relax now; they're both fine. Swim along, please. 18 | 19 | --- 20 | 21 | ## Second Preamble 22 | 23 | I completed this puzzle and finished my write-up, and then I went to sleep. In the morning, I realized I had made it 24 | far too complicated, so I've simplified it here. Rather than keep multiple copies of both the code and the write-up, 25 | I'm just replacing the write-up. If you really want to read my original implementation (why would you?), check out the 26 | Git history. 27 | 28 | --- 29 | 30 | ## Part 1 31 | 32 | Today, as our submarine travels through the underground cave, we find 100 conveniently aligned dumbo octopuses whose 33 | bodies flash as they gain energy. We're here to watch the show and keep records of when they flash. I will explain 34 | the steps of the flashing rules throughout the problem, but if you haven't read the puzzle, it would make more sense 35 | if you did so. 36 | 37 | Let's start off with parsing. On day 9, I said that I usually convert grids to maps but wanted instead to work with 38 | vectors of vectors. Well... this time I stuck with maps. They can often be easier to deal with, especially when a 39 | change with one point cascades to another point. To help with today's problem, I pulled in some helper code I've used 40 | in previous years, in a function called `point/parse-to-char-coords`, which reads multiple lines of input and returns 41 | a sequence (not a map) of `[[x y] c]` mapping the coordinates to the character. To parse out the grid, I push that 42 | sequence into a map, converting each character into an int, using the `char->int` function we saw on day 9. 43 | 44 | ```clojure 45 | ; advent-2021-clojure.point 46 | (defn parse-to-char-coords 47 | "Given an input string, returns a lazy sequence of [[x y] c] tuples of [x y] coords to each character c." 48 | [input] 49 | (->> (str/split-lines input) 50 | (map-indexed (fn [y line] 51 | (map-indexed (fn [x c] [[x y] c]) line))) 52 | (apply concat))) 53 | 54 | ; advent-2021-clojure.day11 namespace 55 | (defn parse-grid [input] 56 | (->> (point/parse-to-char-coords input) 57 | (reduce (fn [acc [p c]] (assoc acc p (utils/char->int c))) {}))) 58 | ``` 59 | 60 | Next, I'm going to create a few helper functions so we don't have to keep thinking about the actual energy levels 61 | throughout the code. `ready-to-flash?` states whether the octopus has an energy level above 9, meaning it is about to 62 | flash. `flashed?` returns whether the octopus just flashed, which means its current energy level is 0. Then 63 | `coordinates-ready-to-flash` and `coordinates-flashed` returns which octopuses are either ready to flash or just did 64 | flash. 65 | 66 | ```clojure 67 | (defn ready-to-flash? [v] (> v 9)) 68 | (def flashed? zero?) 69 | 70 | (defn- coordinates-where [f grid] (keep (fn [[k v]] (when (f v) k)) grid)) 71 | (defn coordinates-ready-to-flash [grid] (coordinates-where ready-to-flash? grid)) 72 | (defn coordinates-flashed [grid] (coordinates-where flashed? grid)) 73 | ``` 74 | 75 | On every step/turn of the program, we'll need to increment every octopus's energy level, and then check for flashes. 76 | If an octopus flashes, it sets its energy level to 0 and causes all of its surrounding neighbors to increment their 77 | energy levels if they hadn't flashed yet. Finally, any of those neighbors check to see if they're ready to flash too. 78 | 79 | First, let's take care of the easy part - incrementing every octopus's energy level. `increment-all` is a very simple 80 | function, but we're going to leverage a utility function I found online a while ago, called `update-values`, which 81 | applies a function `f` to the value of every key in a map. In this case, we just call `inc`. I'll include the text of 82 | `update-values` here since it's the first time we're using it this year. 83 | 84 | ```clojure 85 | (defn update-values 86 | "Thank you to Jay Fields' post for this awesome way to apply a function 87 | to every element of a map. 88 | http://blog.jayfields.com/2011/08/clojure-apply-function-to-each-value-of.html" 89 | [m f & args] 90 | (reduce (fn [r [k v]] (assoc r k (apply f v args))) {} m)) 91 | 92 | (defn increment-all [grid] (utils/update-values grid inc)) 93 | ``` 94 | 95 | The `cascade-flashes` function is a little more involved since it is recursive. To start, we'll check to see if there 96 | are any points ready to flash. For that, we can get the (lazy) sequence of coordinates ready to flash, and pick the 97 | first one off; if there are none, this will return `nil`. If we find such a point, we'll map it to its surrounding 98 | points, and filter for only those coordinates that are on the grid; this takes care of boundary conditions. Next, 99 | we'll remove any points that correspond to octopuses that have already flashed, since after incrementing every octopus, 100 | only those that just flashed will have an energy level of 0. Note that `(remove (comp flashed? grid) points)` uses the 101 | `comp` function because I think it looks cleaner than `(remove #(flashed? (grid %)) points)`. Finally, we need to 102 | increment those neighbors and reset the current octopus to zero, and we do that with a `reduce` call. The 103 | initialization value in this case isn't the `grid`, but rather `(assoc grid p 0)` so the chosen octopus goes down to 104 | an energy level of 0. 105 | 106 | ```clojure 107 | (defn cascade-flashes [grid] 108 | (if-some [p (->> grid coordinates-ready-to-flash first)] 109 | (recur (->> (point/surrounding p) 110 | (filter grid) 111 | (remove (comp flashed? grid)) 112 | (reduce #(update %1 %2 inc) (assoc grid p 0)))) 113 | grid)) 114 | ``` 115 | 116 | One thing that might look a little odd in that function is that we are using `recur` without first using `loop`. The 117 | `recur` function "rebinds the bindings of the recursion point to the values of the expressions," which is how Clojure 118 | can emulate tail call optimization even though Java doesn't actually support it. Clojure supports two recursion points - 119 | functions (`fn` or `defn`) and `loop`s. Here we're making the whole `cascade-flashes` function recursive. 120 | 121 | So now we can implement the very simple `take-turn` function. Given a grid, it will increment all of the energy levels 122 | and cascade all of the flashes, returning the new grid. 123 | 124 | ```clojure 125 | (defn take-turn [grid] (-> grid increment-all cascade-flashes)) 126 | ``` 127 | 128 | Ok, so now that `take-turn` is fully functional, I am going to create a function `octopus-flash-seq` that takes in an 129 | input string, and returns a sequence of states of the grid, starting with the original generation. We'll use 130 | `(iterate take-turn grid)` to generate the sequence of grids, which we will then map to the number of coordinates 131 | that had just flashed. Again, `(map (comp count coordinates-flashed))` looks simpler than 132 | `(map #(count (coordinates-flashed %)))`. 133 | 134 | ```clojure 135 | (defn octopus-flash-seq [input] 136 | (->> (parse-grid input) 137 | (iterate take-turn) 138 | (map (comp count coordinates-flashed)))) 139 | ``` 140 | 141 | Finally, we can write part 1! We want to add together the number of flashes for the first 100 generations, which means 142 | to get the first 101 values, or drop the initial value and add the next 100. Either way, it's a small function. 143 | 144 | ```clojure 145 | (defn part1 [input] 146 | (->> (octopus-flash-seq input) 147 | (take 101) 148 | (apply +))) 149 | ``` 150 | 151 | --- 152 | 153 | ## Part 2 154 | 155 | Well now this is a lovely treat - we have almost nothing to do! We need to find the first step during which every 156 | octopus flashes. Luckily, the problem statement tells us there are 100 octopuses, so from the `octopus-flash-seq`, we 157 | just need to find the first generation where flash count is 100. `keep-indexed` comes to the rescue here, since it 158 | returns the non-nil results of calling a function `(fn [index value])` on each element in its input sequence. We use 159 | `when` to return either the index (`%1`) or `nil` if the value isn't 100, and then we pull out the first such index. 160 | 161 | ```clojure 162 | (defn part2 [input] 163 | (->> (octopus-flash-seq input) 164 | (keep-indexed #(when (= 100 %2) %1)) 165 | first)) 166 | ``` 167 | 168 | We're almost halfway through the Advent, and the problems are still pretty straightforward. That means the rest of the 169 | puzzles will be equally reasonable, right? 170 | 171 | Right? -------------------------------------------------------------------------------- /resources/day10_data.txt: -------------------------------------------------------------------------------- 1 | (({(({{<{{(<{<{}()>(<>{})}{[(){}]{{}[]}}>)<[{{{}[]}{{}{}}}{{[]()}<[][]>}]>}}(<[{([()<>][[]<>])}((( 2 | ([<[([<{(<[[[<()[]>[()()]]<[<>()]<[][]>>]](({({}{})}{<[]<>><[]()>})[<<[]<>>([]())>{<<>{}><<><>>}])><{<{( 3 | [([(([<<<{(<([()()]{{}{}}){({}[])[()[]]}>({(<>()){{}{}}}<<[]{}>[<>()]>)>}[([<({}[]){{}{}}>])[<<< 4 | ([[{{([<<<<({(<>{}){[]{}}]<({}{})[[]()]>)((([]())(()[])){[()[]]{()<>}})>{[<(<>[])>{[()<>]{()[]}}]({(<><>)[[ 5 | {<({[(({((({((<>())([]()))}[{{[]()}{()()}}(<<><>>(<>{}))])))}<<((([[<>{}][<><>]](<{}()>[{}}))< 6 | ({{<({<{<{{[<{{}<>}[<>()]>[<()<>><[]()>]]}(([{{}<>}{(){}}][(()<>){<><>}])(({{}()})[{[]{}}]))}>} 7 | (<[[[<[<{{([{(()()){[]{})}{[[][]][<>[]]}][{({}[])}[<<>[]>]])}}({(<(<(){}>{()[]}){[()[]]{{} 8 | [<[[({([(<[{<({}())[[]()]>{<()<>>[(){}]}}]>)]<<{(<<<<>><{}>>([{}()][{}<>])>[[{<>}<{}()>][{()[]}]])}>> 9 | [{(<{{([(<[<{(<>[]){{}()}}>(<({}<>)[{}{}]>{{<><>}[<>{}]})]{[{{{}()}<()()>}]{([()[]])[(<>())<()[] 10 | <[[<<<{({[<{(<[][]><{}<>>)[([]<>)[[][]]]}[{[<>[]]}((<>()){()<>})]>{[(<()()>{{}()})<[{}[]]{ 11 | {[{(([<([[{[(({}<>){<>[]})([[][]](<>[]))]{{[[]{}]}}}]<{(<[()<>]<{}()]>{<[]()>[()[]]})<(<()<>>[[][]] 12 | {<<<{<{({[[({<<>{}>[<>]})<[<<>[]><[]<>>][[<>{}]({}{})]>]][{[([[][]][[]()])]([[()()]{[]<>}])}<[({[]<>} 13 | ([<(<<{<<((<<({}())[{}<>]>{<[][]>{{}()}}>(<<<>()>(()[])>))<<{[<>()](<>())}<([][])[()()]>>> 14 | <<(<[{[<{(<<{<[][]>[<>[]]}<(<>{})([][])>>>)<<[{[()()][[]{}]}<[()[]]<[][]>>]>{<{<[]<>>{<>()}}[<()()>[<>() 15 | <(({({{{(([({<<><>>{[]<>}}({()[]}([]{}))}[{<()[]><[][]>}<{[]{}}{[]()}>]]<<<[(){}]{(){}}>{{( 16 | [{[[{<<{<{<<([()()]{[]()})[[[][]]<[][]>]>>}>(<{(([[]()][[]()])[({}<>){<>}]){([{}[]]){[{}[]>[[]{} 17 | ({[[{<[({[{({{{}[]}[<>[]]}{({}()){{}}})[({<>[]}{[]()}){{(){}}[[]{}]}]}](([[[()<>](<>[])]<< 18 | [(<{{[<{<{(<{({}[])<()>}><<<<><>><()()>>>)[{[{{}[]}({}[])]<(()[])({})>][{<<><>>}[({}[])<<> 19 | (<({[<<[<(<<<<()()><{}{}>>([{}<>]}>[({{}<>}[()<>])(({}[])[[]()])]>{{<{{}[]}(<>())>[[(){}]< 20 | <<{(<({{[{<[(([]<>)<[]{}>)(<<>{}><[]()>)]{{[{}[]]<(){}>}(<{}<>>(()()))}>(<<({})(<>[])>><{{[]<>}(< 21 | <(<({[<[[<({[[{}{}][[]<>]}[<<>{}>(()[])]}<{{()[]}}[[<>[]]<[]()>]>)<[(([][])<[]()>)(<()[]>)] 22 | ({[<(({[{((((<<>[]>(<>()))<(()[])>)<<({}{})<<><>>>>)[[[<(){}>(<><>)]([(){}][{}()])]])[((<(( 23 | ({[{[([{[[<[((<>())[<>()])]({{(){}}{()()}}([(){}]<<>[]>))>]][[<[<[{}<>]<(){}>>({[]()}<{}[]>)]((<()()>( 24 | <{[{(<{<({[<<(<>{})((){})>(({}<>)[{}{}])><[[<>[]]<(){}>]<(<>[])<{}{}>>>]{{([{}[]])<<()[]>>} 25 | [{[[(([(<[[{<{()[]}([]<>)><<[]()>[<>()]>}]<([<()<>>{<>()}][(()<>){{}{}}])>]<{{[{[][]}(())]}( 26 | <<(<<[(([({<{(())}([{}[]](<>[]))]<<<()<>><[]{}>>[<[][]>{{}<>}]>}[[{[[]()]}({()()}{[]})]<(< 27 | ({(<<<<[({{<<{<>{}}({}())>({()<>}[()<>])>{{{()<>}(()[])}(<[]<>>(()<>))}}(<<[{}]((){})><({}[ 28 | ({[(<<[<[(<[<(()<>)>{(()())({}[])}>[{{[]<>}[{}{}]}({[]()}[<>[]])]>)]({[<[{[]<>}[{}()]][[[]<>]<<>>] 29 | {(<{<{[({({[<[[]<>][()[]]>{({}()){<><>}}]{([[]()]({}{}))[<<><>>{{}{}}]}}){{(<([]{})>({<>[]}<[][]>))} 30 | <{([{{([{[[{{(()[])<{}[]>}}{{{()()}<<><>>}}][{{[[][]]{[][]}}}]]}])}{([[<(<<<<>[]>([]{})>>({<{}()>[[]()]>([[][ 31 | [{(<{[[{((<{<[[][]][[]<>]>[(()())[(){}]]}{{{{}[]}[(){}]}(<()[]>{[]()})}>({((()()}){({}[])([]{})}}))) 32 | {{((<({[({{{({<>{}}<[]{}>)[{{}()}{<>[]}]}<[({}[])<<>>]>}}(<<({[]<>}([][]))(<[]()><{}[]>)>{<({}[]){{}{ 33 | [<(<[{<({[(([[[]][[]<>]][[<><>]{[]()}])[{({}{}){<>{}}}<{()<>}{{}{}}>])](<[({<>{}}([]()))][[<{}<>>([]())](( 34 | <([[<(({{([{[{<>}([]())][{()()}(<>()))}[([<><>]{<>[]})(([]{}){{}()})]](<(([]<>)([]{}))[(<>())]>)) 35 | {{{[{[(<((<([{{}{}}[[][]]](([])))])){[<<[<[]()>[<><>]]>>{{<[[][]][{}<>]>[<[]<>>{[]()}]}[[([]() 36 | <<([({({{[{[<(()[])><([]<>){<>{}}>]<[(<>[])<{}()>]>}]}<<<[{([][])((){})}(<{}<>>{()[]})][({[][]}{[]{}})]><[( 37 | [[((([([[{<([(()()){{}<>}]<([]<>){[]{}}>)[(<{}{}>[{}<>])(<{}{}>[()[]])]><[({{}[]})[[<>()][{}[]]]]{<<()[] 38 | <<<{(([{[<({{({}{})(<><>)}({{}()})}{<<()><(){}>>[{<>{}][()<>]]}){(<<{}[]>[<><>]>({[]()}<{}{}>)){{{<>}{[]< 39 | ([[{{<{<{[<{([()()]<<>[]>)[[()<>](()<>)]}>[<({()<>}(()))<{[]{}}{{}{}}>>(([(){}]<()()>)<<()()>(<><>)>)]](({ 40 | ((<<[<([{<([<<()()>{[]<>}>[<{}[]>([])]][([<>()]{()[]})])><((<<<><>>{<><>}}([{}()][()<>])){[{[]}] 41 | [<{(({{[{<[[{[<>()]}[[()()]({}[])]]<[({}<>)(<>{})]<(<>{})[{}<>]>>]<<(([]())<{}()>)(<[][]>(()[]))>>>(( 42 | <[<[[([<(<({{[()]<<>>}([<>[]][{}()])}([<[]{}><{}()>][<<>{}){<>()}]))>[<[[<()[]><()[]>][[<> 43 | {[[[{<[{[{[[[(<>()){[]}]{[{}()]}]{[<()<>><<><>>]({<>()}{[]()})}]}]<[{(<<()()><[]()>>(<{}<>>{(){}}))([{{}}][[ 44 | <([([([{<[[{{<{}()>{[]()}}}<((<><>){[]()}>>]{[[[{}[]]({}<>)]<((){})>]{(<()<>>(()<>))([<>]<{}< 45 | [({<[[{[({<{((<>{})<{}[]>)[<[]()>([]<>)]}>}<{<<<[][]>[{}{}]>>([<(){}><{}[]>])}<<<<<><>>{{}<>>>{[<> 46 | {<{{<([{([{<[(<>())<{}[]>]({<>[]})>[[(()()){<>{}}]{{<><>}(()[])}]}{([(<>)(()[])])(({<>{}}<[]<>))([ 47 | [<{(<<<{{{([{[[][]]{()()}}<[[][]](()())>][(<{}[]>({}[]))]){({<(){}>(<>())}<{(){}}{[]{}}>){<(()())[(){}]>({{}( 48 | {<<[<[{([[<{<(<>){<>{}}>{({}())(<>{})}}<(([][]))>><{{[[]<>][[]()]}[<<>()><(){}>]}>]])}[{([((<((){ 49 | ([(({<[[((<([([]())[[][]]][{<>{}}<(){}>]]><<(<(){}>[[]()])([[][]][()()])><[{()()}<<>{}>]>>)){ 50 | [{(<<<{((<<({[()[]]<{}()>}{<<>{}>{<>[]}})((<{}[]>[()<>])<<<>[]>[<>[]]>)><<<[(){}]>{<()[]>{(){}}}>([<<><>>{{ 51 | {([({[[<({{([(<>{})<[]<>>])[[<{}><<>{}>]]}{([<[]()>[{}<>]]<([]{})>)})<({{[{}[]]{<><>}}{[[]{}]<<>{}>}} 52 | [[[((<[([({({<<>{}>[<><>]}){{(()<>)<[]()>}<{{}[]}({}())>}})[((({<><>})({[][]}(()())))<[{{}{}}{() 53 | <<[([(({<<[[[<[]<>>]]<((<><>)[{}[]])>][([<<>{}>{[][]}]{{()()}[[]()]>){(([]<>)([][])){(<><>)}}]>{[{ 54 | <([[(<{{{{({<{{}{}}(()())>[[[]()]{()<>}]}[[{[]()}{()()}>])}}<[<{[(()<>)(()())][(()())]}({<()<>>(<>{} 55 | [{{[<(<{(<<<({{}()}<<>()>)([[]()]{()[]})>(({{}<>}[<>{}])([(){}]{()<>}))>[[(<[]{}>(<><>))[<[] 56 | {{{[{<{[<<{{{{()<>}{(){}}}}<([{}<>][{}{}])<<{}<>>{()[]}>>}{[<<<>>[()<>]>]([[{}()](<>{})]{{[][]}{(){}}}) 57 | [[[{<({{(<([(<()()>({}()))])>)<<(<[(()())]{(<>())[<>[]]}><<{(){}}<{}<>>>[{[]()}(<>{})]>)([{([]<>)([][ 58 | (({[(({{[{({[(())<(){}>]})<{<<{}{}](()<>)>({[]{}}((){}))}({<[]()>[[]<>]}{<{}[]>[[]{}]})>}][{< 59 | [<{<[[[<(({[[(<>())]<{<>[]}(<><>)>]}[<{<()<>>}><[{()[]}(<>[])]{{<>()}[{}<>]}]])<[<({{}[]}(<>< 60 | [(<{{[{{([({{[{}()]<()()>}{[()()]{<>[]}}}(<<()>(<>{})>))]<([(<[]>[[]<>])([{}](<>()])])<{<<[]{}>(<> 61 | [[{{([<([{{([{[]{}}<()[]>]{<(){}>([][])})}<<{{<><>}[()]}{<<>{}>{<>}}>([(<>{})<<>[]>]{{<><>}{<>()}})>} 62 | <[{([<{<[<(<{{<><>}[{}<>]}<<{}[]><[][]>>>{({()()}{<>[]})<<[]{}>[{}[]]>})<<[({}<>)]>(({{}()})(((){}]))>> 63 | {[[<(([<[<<<<[[]()]{()<>>>{({}())<[]{}>}>[({<>{}}(<>[]))((()[])[()])]>>{[[[({})]{(()())[<>()]}]]{<{<()( 64 | <([({[[([[{{<[{}[]](<>)><{()()}[[]{}]>}<<({}{})({}{})><<(){}><<>[]>>>}[{{([][])(<>{})}[<<>[]><{}{}>]}[<[ 65 | ((<<<[<[(<<[[({}<>)<[]>][[(){}]<<>()>]]{<[()()]<()<>>>{{<>()}((){})}}>[({<<>[]>{()()}))(({[]()}{()[]} 66 | [(({{<[([{[[({(){}}{()[]}){([]{})([]{})}][{{<>()}<{}{}>}]]({{[<>[]]}(<{}[]><{}()>)}(({()()}(( 67 | <[<<{<({<{<<[(()())[()[]]]]<({{}{}}[[]<>])>>[([<(){}><{}<>>]<<[]()>(<><>)>)[[(()[]){{}()}]{( 68 | <{[<({(<({<[[(()[])][<()()>({}())]](<{[]()}[{}[]]>({(){}}{()[]}))>}[<{<<<><>>[(){}]>{<()<>>{<>[]}}}>[<{ 69 | {{{<({{(<({{[[<>()]<<>[]>]{<<><>>([]<>)}}}({<<<><>>{[]<>}><(<>()>([][])>}))>([<{[{{}()}[[][]]]<{<>[]}<[]<>>> 70 | {{(([{[[([([{<[]()>{[]{}}}(<()[]><[][]>>]({{(){}}[[][]]}[[<>]{<>()}]))])]]}])[[[([<[[[<[[]<>]<( 71 | [<(<{{<((([(([()[]]<{}>)<[<><>][{}()]>)<([[]()])>](<{((){})((){})}{([]<>)<<>[]>}){[([]())](({}[]){() 72 | <({[<(<(({[([<{}()><(){}>]([<>][()()]))(<[()[]]><[()[]]({}{})>)]{(<(<>{})[{}<>]><{{}{}}{[]()}>) 73 | {<<[<{[[[([<{{[][]}[[]()]}<[[][]][<>{}]>>]){{[<<()()>(()[])][[<>()]{<>()}]]((<<><>><{}()>)[{{ 74 | {{<([{[[<(<{{{{}[]}{<><>}}}((<{}>))>[(<(()[]){<>{}}>((()())[<>]))])>]]}[{[{((([<(){}>({}[])][<( 75 | <[[((([{[[<<[{<>[])[{}[]]]>>(<[{[][]}<{}<>>][[()<>][[][]]]>([([]<>)({}())](<{}()>{()<>})))]< 76 | ({<{<[{(((<(<(()[]){[][]}>[<()<>>[<>()]])(<{{}{}}(<>{})>)>[(({(){}}{()()])[{{}{}}[<>{}]])]) 77 | ({{((<([(({([[<>()][{}{}]]<(<>)(())>}}{<([<>()]{[]})([<>{}](<>[]))><({[]()}(<>[])){([][])[<>[]]}>})[{ 78 | ([{[<(({(<<<<<[]<>>{[]{}}><((){}){<>()}>>([[()[]][<>{}]]([[][]][()<>]))>><<[([()()]{<><>}){<{}<>>{<>< 79 | [{({<{<{({(({[[]()]([][])}{{{}()}([][]>}){<({}<>)[<>()]>[[()<>]<<>>]})[(<<()()>{[][]}><[<>( 80 | {[[<({<<<<<{[[{}{}](()())]<([][]){<><>}>}>{<[[<>()]<[]>][{<>{}}({}[])]>}>[{{{[()<>][<>{}]}}}[[[({ 81 | [<(<(<{<<{{{(<()[]>{{}})}}<[[{[]{}}([]{})][(<><>)[[]()]]]{<{<>[]}(<><>)>[({})(<>[])]}>}[(([[{}{}][ 82 | (([([[<<[[(<({()<>}{()<>})<([]<>)(<>[])>>[{<{}[]>{[]()}}])[<<([][])<[]()>>[{{}()>{{}{}}]>]]][[<<(([]<>) 83 | [([[{([{[{{<(<<>()>)(<[]()><<>{}>)>><<(<[]()>([][])){<<><>><{}[]>}>([[<><>]{{}()}]<{[]()}>)>}<({([() 84 | ([[([{<{([<<<(()())[{}()]>([{}<>](()[]))>({<{}[]><<>[]>}{([]<>)({}())})>[(<<{}()><<>()>>[[<><>]])({[[]()]<{} 85 | {[{{([(<<([([<[]()>])<{({}<>)({}())}<([]()){[]<>}>>]){((<{[]<>}({}{})>({{}[]}{{}<>}))[({[][ 86 | {{[(<<<({<{[[([]<>){()()}]((<><>)[{}()])]{[(()<>)[{}()]]}}([<<[][]>)[{()<>}<{}{}>]]{{{{}{}}((){})}{[<> 87 | {(<<[(<<<<{([[[]<>]{(){}}]{{[]()}}){<<()<>>>[{{}{}}<{}<>>]}}>[[{[{<>()}<{}[]>]<{{}[]}>}<(({}[])(<><>) 88 | <<[(<{[({<<<<{[]{}}<{}[]>>>({{()[]}{()()}}<<{}{}>({}{})>)>>})]{(<((<([[]()][()<>]){((){})<[] 89 | [[[[{<<[[{<(([{}{}]([]<>))({[]()})}(<{()[]}<{}<>>>{[<>{}]})>({[<{}{}>][<<>{}>(<>)]}[<{[][]}{{}{}} 90 | {<<[(<((({{{[[<>[]]{<>{}}](({}())({}{}))}<{<(){}>([]<>)}{((){})}]}}{<{{([]())[()]}{<(){}>(<><>)}}[[<<> 91 | <([[[[[[{<(<[<<><>>{[][]}]>)([([()[]][()()])]([{()[]){()[]}][{[]()}(()<>)]))>[{[({<>()}[{} 92 | [(({<<<({<{[(<{}{}>{()<>}){{()[]}{[]{}}}]}>})([<<<({<>()}[[]()])>({[{}<>]<[]<>>}(((){})))>>{[[([<><>]([]< 93 | {{[[({{[({<{[{()<>}([][])]([(){}])}>(<(([]())<()<>>){[[]()]([]())}><<<<>[]>[()<>]>[<[]{}>[<><>]] 94 | <{({((<{[{[<(<[]{}>(<>{}))>][{{<{}<>}{()<>}}<(<>())<()()>>}[[({}()){[]()}](<<>[]>{[]()})]]}<<{(({}<> 95 | (<<{{{[{[{((<{()<>}({}<>)>[(()<>)(<><>)]))[[[(<>[])<{}<>>][[[]<>]<{}()>]]<[(<>{})[<>]]>]}]}][( 96 | ((({([[<[([[[[()[]]<{}()>](({}())[()])]<[{(){}}({}())](([]()){{}()})>]<(({<>{}}[[]{}])<<()<> 97 | {{[(<<([({((([{}<>]{{}[]})([<><>](<>())))[{(()[])[()()]>[(()()){<>[]}]])}<([{[[]()][[]{}]}( 98 | [<<({{[<<[({((()<>)[<>()]){<{}>[[]<>]}}[<{[]()}(()<>)>])<([<<>{}>[<><>]]<{[]<>}[()<>]>){[{<><>}<<>[]>]{(( -------------------------------------------------------------------------------- /docs/day12.md: -------------------------------------------------------------------------------- 1 | # Day Twelve: Passage Pathing 2 | 3 | * [Problem statement](https://adventofcode.com/2021/day/12) 4 | * [Solution code](https://github.com/abyala/advent-2021-clojure/blob/master/src/advent_2021_clojure/day12.clj) 5 | 6 | --- 7 | 8 | ## Preamble 9 | 10 | In today's puzzle, I decided to use, or rather overuse, some functions I've only played with a bit in the past, namely 11 | `some-fn` and `every-pred`. I freely admit that I sacrificed some readability for the sake of trying a new approach, 12 | so even though the code is clear enough, I'm not sure I necessarily like it. Today's solution was a fun experiment! 13 | 14 | --- 15 | 16 | ## Part 1 17 | 18 | We're navigating through underwater caves, looking for every possible path from the start to the end, such that we 19 | never visit the same small cave twice. A small cave has an all-lowercase name, while a big cave has an all-uppercase 20 | name. Both `"start"` and `"end"` are special cases, where we never return to the start, and we're done as soon as we 21 | reach the end. 22 | 23 | The data comes in as a sequence of lines of cave names, separated by a dash. We observe that if you can navigate from 24 | cave A to B, then you can also go from B to A, so we'll want to create a map from every cave to every cave it connects 25 | to. Since we never want to return to the start, we'll explicitly remove any such mapping once we're done constructing 26 | the original map; we do this at the end so that we don't have to check both the left- and right-hand-side of each line. 27 | Since "start" and "end" are magic strings we'll refer to several times, we'll define them as vars right away. 28 | 29 | ```clojure 30 | (def start-cave "start") 31 | (def end-cave "end") 32 | 33 | (defn parse-connections [input] 34 | (let [every-path (->> (str/split-lines input) 35 | (map #(str/split % #"-")) 36 | (reduce (fn [acc [from to]] (-> acc 37 | (update from conj to) 38 | (update to conj from))) {}))] 39 | (utils/update-values every-path #(remove (partial = start-cave) %)))) 40 | ``` 41 | 42 | Since we're talking about simple vars, let's define a few other helper functions so we can remove some lambdas later 43 | on. And to do this, we'll revisit our old friend `some-fn` and introduce a new one named `every-pred`. Recall from 44 | days 5 and 9 that we use `(some-fn pred1 pred2... predn)` to take in a number of predicates, and return a new one that 45 | returns the first predicate which returns a truthy value. It's our way of saying "do any of these predicates pass?" 46 | We'll now use a similar function called `every-pred`, which has a similar constructions and returns a boolean value 47 | for whether every predicate returns a truthy value. Essentially, `some-fn` is an aggregate `or`, while `every-pred` is 48 | an aggregate `and`. Why isn't the first named `some-pred`, or the second named `every-fn`? And more relevant here, 49 | where isn't there a `no-pred` or `not-any-fn` to aggregate `not`? 50 | 51 | Anyway, we'll use `start-cave?` and `end-cave?` to look for those special values; anything that isn't a start or end 52 | cave is an intermediate cave, so we'll use the `complement` (opposite) of `some-fn` to create `intermediate-cave?`. 53 | Finally, `small-cave?` and `big-cave?` are just intermediate caves whose string values are all lowercase or uppercase. 54 | I made two helper functions here in the `utils` package, which are the String equivalent of `Character/isUpperCase` 55 | and `Character/isLowerCase`. 56 | 57 | ```clojure 58 | ; advent-2021-clojure.utils namespace 59 | (defn lower-case? [s] (every? #(Character/isLowerCase ^char %) s)) 60 | (defn upper-case? [s] (every? #(Character/isUpperCase ^char %) s)) 61 | 62 | ; advent-2021-clojure.day12 namespace 63 | (defn start-cave? [cave] (= start-cave cave)) 64 | (defn end-cave? [cave] (= end-cave cave)) 65 | (defn intermediate-cave? [cave] ((complement (some-fn start-cave? end-cave?)) cave)) 66 | (defn small-cave? [cave] ((every-pred intermediate-cave? utils/lower-case?) cave)) 67 | (defn big-cave? [cave] ((every-pred intermediate-cave? utils/upper-case?) cave)) 68 | ``` 69 | 70 | Let's think about the `find-paths` function we're about to make. It will need to take in the `connections` map, and 71 | should return all paths we can take from the start to the end, as a sequence of path vectors. We don't explicitly need 72 | the paths themselves, but it seems intuitive to structure it that way. We'll make a simple recursive function, 73 | and we'll provide both a 1-arity implementation (just the `connections`) and a 3-arity implementation (`connections`, 74 | the path to the current cave, and the set of caves we've already seen). We'll look at the last point in the path, and 75 | check to see if it's the end; if so, we return a single-element vector with the current path, since we made it to the 76 | end. If not, we'll look at all connections out from that cave, filter out for the ones that are approachable 77 | (to be implemented later), and then mapcat the recursive call from each approachable cave onto the path ending with 78 | that cave. We'll also add the latest cave to the set of `seen` caves, so we know where we've already been. 79 | 80 | ```clojure 81 | (defn find-paths 82 | ([connections] 83 | (find-paths connections [start-cave] #{})) 84 | 85 | ([connections path seen] 86 | (let [cave (last path)] 87 | (if (end-cave? cave) 88 | [path] 89 | (->> (connections cave) 90 | (filter (partial approachable? seen)) 91 | (mapcat (fn [cave] 92 | (find-paths connections (conj path cave) (conj seen cave))))))))) 93 | ``` 94 | 95 | So what makes a cave approachable? There are three reasons you can go into a cave: 96 | 1. This is the end cave, because that finishes the path. 97 | 2. This is a big cave, because we can visit big caves multiple times. 98 | 3. This is a cave we haven't seen yet, suggesting it's a small cave. 99 | 100 | To implement `approachable?`, we'll use `some-fn` to check if any of those predicates apply to the cave. 101 | 102 | ```clojure 103 | (defn approachable11 [seen cave] 104 | ((some-fn end-cave? big-cave? (complement seen)) cave)) 105 | ``` 106 | 107 | Finally, we can implement `part1` by parsing the input, finding all paths from the start to the end, and then counting 108 | the number of paths returned. 109 | 110 | ```clojure 111 | (defn part1 [input] (->> input parse-connections find-paths count)) 112 | ``` 113 | 114 | --- 115 | 116 | ## Part 2 117 | 118 | In part 2, we learn that we can still visit big caves any number of times, but we can also revisit a single small cave 119 | once. We'll have to make a small modification to the existing functions to get this to work, but overall our structure 120 | should hold. 121 | 122 | I think the easiest way to think about this is to go back to `approachable?` before rewriting `find-paths`. The 123 | `approachable?` function can now allow entry into a small cave if there hasn't already been a double-entry cave. 124 | So now, the `approachable?` function takes in three arguments - an `allow-repeat-visit?` flag that's false for part 1 125 | and true for part 2, a _map_ (not set) of caves already seen to the number of visits, and the cave in question. The 126 | `((some-fn end-cave? big-cave? (complement seen)) cave)` code from Part 1 still works just fine. But now, if none of 127 | those predicates apply to the cave, we can allow the cave if both `allow-repeat-visit?` is true and we have not seen 128 | any repeat visitors. The `has-repeats?` function will check if any cave has been visited at least twice already. 129 | 130 | ```clojure 131 | (defn has-repeats? [seen] (some (partial <= 2) (vals seen))) 132 | 133 | (defn approachable? [allow-repeat-visit? seen cave] 134 | (or ((some-fn end-cave? big-cave? (complement seen)) cave) 135 | (and allow-repeat-visit? (not (has-repeats? seen))))) 136 | ``` 137 | 138 | Now that that works, we need to make some small changes to `find-paths`. First of all, its arity will switch to a 139 | 2-arity `(allow-repeat-visit?` and `connections`) and a 4-arity (`allow-repeat-visit?`, `connections`, the `path`, and 140 | the set of only the small caves we've already seen). Most of the logic is the same, other than passing in an empty map 141 | instead of an empty set to the 4-arity implementation. But when we're ready to recursively call `find-paths`, we need 142 | to only increment the mapping of the current `cave` if it's small. Unfortunately, `(update {} :a inc)` will throw a 143 | `NullPointerException` because you can't increment a value that's not in the map yet, so to accommodate for the first 144 | instances of visiting a small cave, we'll need to execute `(update small-caves-seen #(inc (or % 0)))`; the `or` 145 | function is a great way to handle nulls, since `#(inc (or % 0))` means we increment the current value in the map, or 146 | 0 if it's not in the map. 147 | 148 | ```clojure 149 | (defn find-paths 150 | ([allow-repeat-visit? connections] 151 | (find-paths allow-repeat-visit? connections [start-cave] {})) 152 | 153 | ([allow-repeat-visit? connections path small-caves-seen] 154 | (let [cave (last path)] 155 | (if (end-cave? cave) 156 | [path] 157 | (->> (connections cave) 158 | (filter (partial approachable? allow-repeat-visit? small-caves-seen)) 159 | (mapcat (fn [cave] 160 | (find-paths allow-repeat-visit? 161 | connections 162 | (conj path cave) 163 | (if (small-cave? cave) 164 | (update small-caves-seen cave #(inc (or % 0))) 165 | small-caves-seen))))))))) 166 | ``` 167 | 168 | Finally, we can refactor `part1` and implement `part2` by calling the new `find-paths` function, passing in the 169 | appropriate values for `allow-repeat-visit?`. 170 | 171 | ```clojure 172 | (defn part1 [input] (->> input parse-connections (find-paths false) count)) 173 | (defn part2 [input] (->> input parse-connections (find-paths true) count)) 174 | ``` 175 | -------------------------------------------------------------------------------- /docs/day02.md: -------------------------------------------------------------------------------- 1 | # Day Two: Dive! 2 | 3 | * [Problem statement](https://adventofcode.com/2021/day/2) 4 | * [Solution code](https://github.com/abyala/advent-2021-clojure/blob/master/src/advent_2021_clojure/day02.clj) 5 | 6 | --- 7 | 8 | ## Part 1 9 | 10 | Ahoy, mateys! It be time to take th' ol' sub ta sea! 11 | 12 | Today we're taking our submarine out for a ride. We're given a list of instructions, which either moves our submarine 13 | forward (increasing its position), down (increasing its depth), or up (decreasing its depth). We will need to multiply 14 | its final position by its depth to give our answer. Pretty straightforward. 15 | 16 | As always, let's start with parsing. Assuming we'll be splitting the input by each line, we want to turn an individual 17 | line of `"forward 5"` to `[:forward 5]`. Clojurians would _never_ use Strings when keywords convey the intention better, 18 | and the String 5 needs to become numeric. So we split the line into the words before and after the space, converting 19 | the former into a keyword and the later into an int. 20 | 21 | ```clojure 22 | (defn parse-instruction [s] 23 | (let [[a b] (str/split s #" ")] 24 | [(keyword a) (Integer/parseInt b)])) 25 | ``` 26 | 27 | Now let's briefly think about how to represent the submarine. The easiest option is a simple map with keys of 28 | `:pos` and `:depth`; to feel good about that decision, we'll define the initial submarine state. And while we're at 29 | it, let's quickly define the function `final-position` that multiplies together the `:pos` and `:depth` of the sub. 30 | 31 | ```clojure 32 | (def initial-submarine {:pos 0 :depth 0}) 33 | 34 | (defn final-position [{:keys [pos depth]}] 35 | (* pos depth)) 36 | ``` 37 | 38 | As we move line by line through the input, we'll need update the submarine based on the instruction found. Of course, 39 | I'm using the Clojure definition of "update," which doesn't mutate the original sub but rather creates a new one based 40 | on changed data values. While one could write, for the `:forward` instruction, `(update submarine :pos #(+ % amount))`, 41 | we can simplify the expression by providing `update`'s function and arguments inline as 42 | `(update submarine :pos + amount)` instead. Combine that with a `case` macro, and we have a simple `move-sub` function. 43 | Note that I'm destructuring the instruction into its `dir` and `amount` components within the function argument list 44 | since it's clear in this namespace what an instruction looks like. 45 | 46 | ```clojure 47 | (defn move-sub [submarine [dir amount]] 48 | (case dir 49 | :forward (update submarine :pos + amount) 50 | :down (update submarine :depth + amount) 51 | :up (update submarine :depth - amount))) 52 | ``` 53 | 54 | Finally, we're ready to put it all together into the `part1` function. All we need to do is split the input string 55 | into its lines, map each line to its `[dir amount]` vector with the `parse instruction` function, reduce each such 56 | instruction with the `move-sub` function and the `initial-submarine` definition, and then calculate the 57 | `final-position`. This right here is what I love about Clojure - from three simple functions, we get a very 58 | easy-to-read coordination function that reads cleanly without a lot of syntax clutter. 59 | 60 | ```clojure 61 | (defn part1 [input] 62 | (->> (str/split-lines input) 63 | (map parse-instruction) 64 | (reduce move-sub initial-submarine) 65 | final-position)) 66 | ``` 67 | 68 | Looking good! Let's move on to Part 2. 69 | 70 | --- 71 | 72 | ## Part 2 73 | 74 | Hmm. This looks very much the same as Part 1, except that we'll have different interpretations of each of the three 75 | instructions. We'll start with the obvious solution first, and then refactor it later. 76 | 77 | First off, a submarine is now defined by three properties instead of just two -- the position, depth, and aim. So 78 | let's revise the `initial-submarine` function. Note that this shouldn't impact `part1` at all. 79 | 80 | ```clojure 81 | (def initial-submarine {:pos 0 :depth 0 :aim 0}) 82 | ``` 83 | 84 | Now we'll make a `move-sub2` function that's very similar to `move-sub`, except that each instruction changes the sub 85 | differently. The `:down` and `:up` instructions just move the aim instead of the depth, so they're easy enough. 86 | The `:forward` instruction now becomes two separate `update` calls, where the latter needs to extract out the current 87 | `aim` in order to multiply it by the `amount` to get to the right answer. 88 | 89 | ```clojure 90 | (defn move-sub2 [submarine [dir amount]] 91 | (case dir 92 | :forward (-> (update submarine :pos + amount) 93 | (update :depth + (* (:aim submarine) amount))) 94 | :down (update submarine :aim + amount) 95 | :up (update submarine :aim - amount))) 96 | ``` 97 | 98 | Finally, `part2` is the same as `part1`, except that its reducing function is `move-sub2` instead of `move-sub`. 99 | 100 | ```clojure 101 | (defn part2 [input] 102 | (->> (str/split-lines input) 103 | (map parse-instruction) 104 | (reduce move-sub2 initial-submarine) 105 | final-position)) 106 | ``` 107 | 108 | I mean, it works, but if you're actually reading this text, you should know by now that I'm not going to keep that 109 | code lying around. That means it's time to refactor! 110 | 111 | --- 112 | 113 | ## Cleanup 114 | 115 | It's clear that parts 1 and 2 only differ in how the submarine moves based on each instruction. I'd like to abstract 116 | that away by defining a function called `create-mover`, which will take in the three functions to apply to a submarine 117 | (forward, down, and up), and return a new function that will call the right function when invoked with a submarine and 118 | an instruction. 119 | 120 | It sounds much more complicated that it looks. Within the `defn`, we define an anonymous function with the same 121 | signature as the previous `move-sub` and `move-sub2` functions - it takes in a submarine and an instruction 122 | (destructured into its direction and amount), and it returns an updated submarine. To avoid a case statement, I create 123 | a map of the three directional keywords (`:forward`, `:down`, and `:up`) to their respective functions, and I call 124 | `(dir {map})`. Once we know which operation/function to call, we simply call `(op submarine amount)`. 125 | 126 | ```clojure 127 | (defn create-mover [forward-fn down-fn up-fn] 128 | (fn [submarine [dir amount]] 129 | (let [op (dir {:forward forward-fn, :down down-fn, :up up-fn})] 130 | (op submarine amount)))) 131 | ``` 132 | 133 | Now let's create our two mover functions. Each calls `create-mover` with three functions, which I've chosen to 134 | represent as anonymous functions since they're so small. The first function for part 2 (move forward) is a little more 135 | verbose since it performs two updates, but this time I destructure the `aim` out of the submarine in the function 136 | argument declaration. This is one of the awesome features of Clojure destructuring - we can define an argument of 137 | `{aim :aim :as submarine}` to mean "given an associative argument (a map), pull the `:aim` property into a 138 | local binding of `aim`, but still bind the entire argument to the binding `submarine`." Without this capability, we 139 | would have needed a `let` binding lower down, but this is much more concise and expressive. 140 | 141 | ```clojure 142 | (def part1-mover (create-mover (fn [submarine amount] (update submarine :pos + amount)) 143 | (fn [submarine amount] (update submarine :depth + amount)) 144 | (fn [submarine amount] (update submarine :depth - amount)))) 145 | (def part2-mover (create-mover (fn [{aim :aim :as submarine} amount] (-> (update submarine :pos + amount) 146 | (update :depth + (* aim amount)))) 147 | (fn [submarine amount] (update submarine :aim + amount)) 148 | (fn [submarine amount] (update submarine :aim - amount)))) 149 | ``` 150 | 151 | We could have gone farther and made everything anonymous, but that would turn my beautiful Clojure code into something 152 | that looks like Perl, and nobody wants that! 153 | 154 | ```clojure 155 | ; This is awful. Don't do this. These functions deserve to have named arguments, so let's honor them as shown above. 156 | (def part1-mover (create-mover #(update %1 :pos + %2) 157 | #(update %1 :depth + %2) 158 | #(update %1 :depth - %2))) 159 | (def part2-mover (create-mover #(-> (update %1 :pos + %2) 160 | (update :depth + (* (:aim %1) %2))) 161 | #(update %1 :aim + %2) 162 | #(update %1 :aim - %2))) 163 | ``` 164 | 165 | Now that we have defined two movers, we can make the unified `solve` function. This function takes in both the mover 166 | and the input string, and then it reduces each instruction using that `mover` function. 167 | 168 | ```clojure 169 | (defn solve [mover input] 170 | (->> (str/split-lines input) 171 | (map parse-instruction) 172 | (reduce mover initial-submarine) 173 | final-position)) 174 | ``` 175 | 176 | Finally, we redefine the `part1` and `part2` functions. Now I could use `def` and partial functions, since I used 177 | `def` for the movers, but I think that's a little harder to understand when we're not using the functions as arguments 178 | into other functions. So instead, I'll use the normal `defn` that calls the `solve` function with the correct mover 179 | and the input String. 180 | 181 | ```clojure 182 | ; Using defs for the movers were fine, but I don't like it here. 183 | (def part1 (partial solve part1-mover)) 184 | (def part2 (partial solve part2-mover)) 185 | 186 | ; For a tiny bit of repetition, I think these definitions are clearer. 187 | (defn part1 [input] (solve part1-mover input)) 188 | (defn part2 [input] (solve part2-mover input)) 189 | ``` 190 | 191 | There, now that's a handsome submarine processing algorithm! I really love the use of higher order functions in this 192 | solution. -------------------------------------------------------------------------------- /resources/day09_data.txt: -------------------------------------------------------------------------------- 1 | 7678934989765431234965432345679567899987999876543210198965019878921987654343498765678932445678998678 2 | 4567929878965420129874301286789456789976898989654321987894329767990986543232349874589321234567987567 3 | 5679896567895431298763212878992345999875787898769439876789498656789875432101234965695410145678998456 4 | 6798765457889942987654324567891239898764656999877698765678987645898976543432349876789521234567899679 5 | 7987674345679899999765465678932398765832345899998959854689965434567897676546567989895432347878965989 6 | 8999543256798777898976576789993987654521456789999843212569896545678998787959698991976543956799654695 7 | 9987652127987665767897689899989876543210345679898754343456798656789239899868789432398659897989543234 8 | 5498763439876543458789789999876999854321234599649877654767899769893123978979896543698798789878954345 9 | 4329874598654432344678994599995498765435345987430998767878943978932012367989987654569987698767895467 10 | 3212985798743210123489123989985259876746459876321249988989652989893134456799998765879896548656789578 11 | 4339987987654321234691019979874345987897898765432356799199871296789245667898999976998785438745678989 12 | 5498798998878534575692198767965456798998939878543467899299852345678956789987899897987654321236789799 13 | 6987659109986545689789987659876567899659321989654568978989643456789767899876788789998776434545697678 14 | 9876543219999876799899876546989878987543210198765679767678954587899898956985897678949996547656789799 15 | 1987654998943989899987985434599989598657432349876789656569765678987939349854864589234987858767899989 16 | 0198789887894599989996543515678993498796545456987898743478976789876321298763453678949898969898989878 17 | 1999898776795999878989432101789932349987656567898989832567897899965490989541012567898769878999878967 18 | 9899987685679889769878943232597891234598767899999876543478998999876989876432123456789656989298765456 19 | 8789876554798778654567894743456789345679878923498987654569129989989976987543234678996545392129532367 20 | 7698765443987664543456797654567899956989989014987698765678998877998765498954545689987632129098921278 21 | 6569954212396543212345898765678999899898799195696549879899987666897654349765656789876543098997892389 22 | 3459864343498752101456789878789198785787678989987832989999876545698743239878767897987654987886789492 23 | 2398765674599543412467899999891097673654569878999421999998765436789632134999898976898789876795678991 24 | 3499876965987654325698978988932986542323698767698939878999876521296541013456999285999898765644567889 25 | 4989987899998765434789769876549876541012398654567998767899987990965432123467892134598999874323456978 26 | 9878998998979879545678954987656985432143987843456789656989999889897545678578943245987898765401234567 27 | 8767899877569998776989012999767896543459876532345678945778989678789656799989987656796789876512345678 28 | 7658998766458799887899929898978997957867989665456789234569876567678998892399998767985478984323456989 29 | 8745989854345678998999898787899989898978998776569894129678985425467889910978999899876367965434567891 30 | 1239877541234589659298797656789876789989329987678953298989893212345678951867899987985459876565678932 31 | 0198765430147678940129654345896545679096598798789874987899794101234989743456789876798567987676989543 32 | 1239954321256789432334963234989632889129987679899989876997693212567897654867899965987678998789899994 33 | 4345965432367898743949892145678921299298995423998798765434589323456789965979989874398999109898798789 34 | 5459876563459999659898789236989545678987893212989659878321678934567897896998879983219999912987687678 35 | 6567987676567898998787678947899856789456954909876543987430189765678956799897569994301989899876543587 36 | 7678998987698987997654567898998767899397899899965452396542239876789545698786478989319876798767432456 37 | 8989769998789996789879678929349878998989998789876321987963458989895432987645345678999985679654321347 38 | 9995456999899985698998789210198989987678987678988432399874567894976321296532123456789876798768436458 39 | 8764367898999874587899894321987899896567896569876543498985678923965432397832012567899987899876547969 40 | 8743234567898963256789995939876798765456789456989654987696789019876943498945123678999898976998679898 41 | 7632123458987654145678989898765987654345894345698769876549894125988767569656734589998789985498798767 42 | 6543014567898543236789876789874398765656953234569898765634943234599878998767845678945678976569989656 43 | 9667323467899876347898785459995219876769652125678987654323794545679989999898976789234567897978976547 44 | 8778434568959875467897654398989323989878943012567898765474689678998997898979797890195998998989865434 45 | 9989545678943976778998769987678934699989542123456999878565678999996545987667698991989899239299754323 46 | 3597676789432987989999898996567895678997674334678987999678789989889239876555459789976789393198643514 47 | 2498787999921099998989987887478986789498765445789876569989898876778998765434345678965678989019432101 48 | 3569898939892123987679876782347897896329876786898763458999977745769899874321234567894389678998565212 49 | 9679979328789239876598765431256989965212987897987654567899865632356798765410123478943234567987654323 50 | 8998765412678998965439877632345678954323998998998765678998754321467999876323234569432123456798765634 51 | 7899976543489987654323998543458989895439879769999876789789854210349898987434546678943234897899878795 52 | 6789897654567898765214569654567898789598765657889987997698765321298787898865678989874346789954989896 53 | 5976789765789989876501239865678975678987654345678998954579877452987676789976789599865457898543292987 54 | 4345899876789876975423345976789214567898943234989879563459976569876555678987892459876568987652101298 55 | 3234989989898765987564569987894323878989432129898765432398987699765434599099921012987679876543212349 56 | 0125678999987654598965878998989439989876543298789876321297898987654321989198999129898789987654324556 57 | 1237889789876543459876989439878998996989656989678943210986789998765439878987988998789999998875434567 58 | 4356897654987659767987898998769877895498769878569656921975679989896798767896567897678998989998565678 59 | 5456789543299998978998967987654566789329899767498969899864589878987899854578456789567987776597676989 60 | 6589897632198767899219459876543045678914987654347898789653298767898998763562349895459876567459789991 61 | 7678998943987656998901367965432123489323999543216987678943109456789999872101238954398765482359898910 62 | 9889569959998549897899459987643234595439898932105698567893298967897895983432867893239654321454967891 63 | 6993459898987698786788998798654345696598787943214595466789987898976794594543456789398763210123459942 64 | 5432398767898987674597897659765456789987656894323986345679876799765789965654567898999876321254568953 65 | 6543987656999876563456789749876567899878547895434595456789965689654569876767878967899965432345778964 66 | 7679876545789995432367997439987698978965436789545698567899876789323456989898989456989996763456789765 67 | 8798987434599989321459896598998999765432125678956987689901987895434568992949995345678987654567899896 68 | 9987894323989878932998789987679899875432014589767898789912398998567899101239854234567898765678954987 69 | 9876789439878769899877678987598798989543123698978999897893459987678973212398765123498999876789543098 70 | 8765678998767456798765567896454687899955954567899896946989567898989765434569876764689998987899932129 71 | 8754567987654323998643456789323456789876865678998785435679698929999876545678989985699987498999899945 72 | 7643489998543219865432347893212345699997977899987654323798989019876987876899599876789876349998768896 73 | 8732578998654301976545456789103457789999998999876543212987678998765498997945456987898765234987656789 74 | 6543678959765214989856567893234569996987899298765432109876578999896399989432345698919954139876545899 75 | 7654789349874323498767678954645678945876789349876543499765467890987989878991234569109893239985434578 76 | 8765891299985434569878989765676789434765679459987654987654356891999876765789945678998789398765523456 77 | 9976910989876545678989999877987896523254768998998765698775456789899765774677896789987658987654312348 78 | 4987899976987678989998999998998965410123459987989979789876589899768954523456987893299767898732101267 79 | 3298998765698989199987898999999654321234567896567989895987678998656893212347898932109878999843212456 80 | 2129219854349993239876567899989876432389678985456999954398789987434789101256789543299989987656323567 81 | 3998998765457899398985457789876987545678999876567899843219899986525678919397899956989999998767439678 82 | 9897899879568998987654345678965987656789998987678998765401998897636789998989999899878999879896568789 83 | 7776901998979987698788234567894598789899897698989987654323456789647899987679998798767898765987679892 84 | 6545892987899876549887545678943219898999789549099999875634578998757999898567899632347999894398989943 85 | 5434799876989765432998656789894345967988678932129876988785699109898998765456789543456789989219397994 86 | 4327689945678954321298797895789959659876569893998965499896789212999899984345697654567899876101236789 87 | 5212578934567895532789989934567898945989989789897894345987894323498769875456789765688999865412345699 88 | 4323459323656789645679878929689967896794398698756789234598995459987654986568999878799987654323456789 89 | 6496589212345678956898767898998756789899987598767892123999989698976543987779872989892198865434567896 90 | 7587678923456789877989856787897645678998765459899921019899878997895432398889761098963239978545699975 91 | 8698989634587893989876543836989534567899876567995432198789769876789521239997653197654347987656989654 92 | 8789998545678932395978532125678923456789989979989543987698954545678994345679794989987456799769978932 93 | 9899987656789541234965432034589104567899899899878959876567893234568989456798989678976567899898867941 94 | 9998798967895410199876543456678915678998789798765899999456789125679878967987878567897678910997658910 95 | 9897659878964321987998954568789896789679644699876789878349893236898767899876563456789989899879846891 96 | 8799543989965439876339769878896799894598533989989898965258954345987656988986432345999998798765735569 97 | 7678942397897598765219898989945689923987212378899987654147895456988743877898321234568987659874323478 98 | 6569891456789679854329987893234578939876101256789998943236789679876432356789432345678986545985412567 99 | 3456789967898789965498966534134689545965432345678929854345678989765421245679544856789976439876323478 100 | 4567999878999999876987654321012789659878543656789219765656789999876532476789667767899876545987454567 --------------------------------------------------------------------------------