├── 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
--------------------------------------------------------------------------------